android imageview研究总结

开发过程中,总有一些知识点没有彻底理解,导致在后续的开发过程中总会时不时的出现问题,譬如imageview,如出现oom的问题,今天就来彻底研究以下imageview的知识,搞清楚,以后就不会出现这种问题了。

1、官网了解 官网链接(需翻墙)

首先来看一下继承关系,imageview继承至view,所以它拥有view的属性和方法,同时可以看下它的子类,有ImageButton等,等研究完Imageview再来看看ImageButton。

1
2
3
4
5
6
7
8
9
java.lang.Object
↳ android.view.View
↳ android.widget.ImageView
Known Direct Subclasses
AppCompatImageView, ImageButton, QuickContactBadge
Known Indirect Subclasses
AppCompatImageButton, FloatingActionButton, ZoomButton

1.1、重要属性及方法

1
2
3
4
5
6
7
8
Nested Classes
enum ImageView.ScaleType
Options for scaling the bounds of an image to the bounds of this view.
这是一个枚举类,用于表示图片的缩放范围
1
2
3
4
5
6
7
8
9
10
xml:
android:scaleType
java:
setScaleType(ImageView.ScaleType)
意思:
Controls how the image should be resized or moved to match the size of this ImageView.
图片的显示类型,调整大小来适应imageview
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
xml:
android:src
java:
setImageResource(int)
意思:
Sets a drawable as the content of this ImageView.
android:src
Sets a drawable as the content of this ImageView.
May be a reference to another resource, in the form "@[+][package:]type:name" or to a theme attribute in the form "?[package:][type:]name".
May be a color value, in the form of "#rgb", "#argb", "#rrggbb", or "#aarrggbb".
This corresponds to the global attribute resource symbol src.
Related Methods
setImageResource(int)
1
2
3
4
ImageView为我们提供了adjustViewBounds属性,用于设置缩放时是否保持原图长宽比! 单独设置不起作用,需要配合maxWidth和maxHeight属性一起使用!而后面这两个属性 也是需要adjustViewBounds为true才会生效的~
android:maxHeight:设置ImageView的最大高度
android:maxWidth:设置ImageView的最大宽度

1.2、区别类似的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
void setImageBitmap(Bitmap bm)
Sets a Bitmap as the content of this ImageView.
void setImageDrawable(Drawable drawable)
Sets a drawable as the content of this ImageView.
void setImageIcon(Icon icon)
Sets the content of this ImageView to the specified Icon.
void setImageResource(int resId)
Sets a drawable as the content of this ImageView.

以上这四种加载图片的方式有什么区别呢?在选择加载一张图片的时候,我们该如何选择?下面就来一一了解其中的区别:

public void setImageBitmap (Bitmap bm)

1
2
3
4
5
6
7
8
public void setImageBitmap (Bitmap bm)
Added in API level 1
Sets a Bitmap as the content of this ImageView.
Parameters
bm Bitmap: The bitmap to set

我们进入到源码中去看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Sets a Bitmap as the content of this ImageView.
*
* @param bm The bitmap to set
*/
@android.view.RemotableViewMethod
public void setImageBitmap(Bitmap bm) {
// Hacky fix to force setImageDrawable to do a full setImageDrawable
// instead of doing an object reference comparison
mDrawable = null;
if (mRecycleableBitmapDrawable == null) {
mRecycleableBitmapDrawable = new ImageViewBitmapDrawable(
mContext.getResources(), bm);
} else {
mRecycleableBitmapDrawable.setBitmap(bm);
}
setImageDrawable(mRecycleableBitmapDrawable);
}

实际上setImageBitmap做的事情就是把Bitmap对象封装成Drawable对象,然后调用setImageDrawable来设置图片。因此代码里面才写上了建议,如果需要频繁调用这个方法的话最好自己封装个固定的Drawable对象,直接调用setImageDrawable,这样可以减少Drawable对象。因为每次调用setImageBitmap方法都会对Bitmap对象new出一个Drawable。

接下来我们进入setImageDrawable方法中一窥究竟。

public void setImageDrawable (Drawable drawable)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Sets a drawable as the content of this ImageView.
*
* @param drawable the Drawable to set, or {@code null} to clear the
* content
*/
public void setImageDrawable(@Nullable Drawable drawable) {
if (mDrawable != drawable) {
mResource = 0;
mUri = null;
final int oldWidth = mDrawableWidth;
final int oldHeight = mDrawableHeight;
updateDrawable(drawable);
if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
requestLayout();
}
invalidate();
}
}

setImageDrawable参数是Drawable,也是可以接受不同来源的图片,方法中所做的事情就是更新ImageView的图片。

public void setImageResource (int resId)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Sets a drawable as the content of this ImageView.
*
* <p class="note">This does Bitmap reading and decoding on the UI
* thread, which can cause a latency hiccup. If that's a concern,
* consider using {@link #setImageDrawable(android.graphics.drawable.Drawable)} or
* {@link #setImageBitmap(android.graphics.Bitmap)} and
* {@link android.graphics.BitmapFactory} instead.</p>
*
* @param resId the resource identifier of the drawable
*
* @attr ref android.R.styleable#ImageView_src
*/
@android.view.RemotableViewMethod
public void setImageResource(@DrawableRes int resId) {
// The resource configuration may have changed, so we should always
// try to load the resource even if the resId hasn't changed.
final int oldWidth = mDrawableWidth;
final int oldHeight = mDrawableHeight;
updateDrawable(null);
mResource = resId;
mUri = null;
resolveUri();
if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
requestLayout();
}
invalidate();
}

我们看一下它的注释部分内容:

This does Bitmap reading and decoding on the UI thread, which can cause a latency hiccup. If that’s a concern, consider using {@link setImageDrawable(android.graphics.drawable.Drawable)} or {@link setImageBitmap(android.graphics.Bitmap)} and {@link android.graphics.BitmapFactory} instead.

setImageResource方法也是对图片进行读取和解析的,不过是在UI主线程中进行的,所以有可能对一个Activity的启动造成延迟。所以如果顾虑到这个官方建议用setImageDrawable和setImageBitmap来代替。

所以综合来看setImageDrawable是最省内存高效的,如果担心图片过大或者图片过多影响内存和加载效率,可以自己解析图片然后通过调用setImageDrawable方法进行设置。

1.3、src和background

我们在上面使用了setImageResource()、setImageDrawable()、setImageBitmap()方法,会发现一个问题,我们来进行简单的测试一下:
添加布局文件activity_image_view.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Activity.ImageActivity">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>

ImageActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.frame.android.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.ImageView;
import com.frame.android.R;
import org.xutils.view.annotation.ContentView;
import org.xutils.view.annotation.ViewInject;
@ContentView(R.layout.activity_image_view)
public class ImageActivity extends BaseActivity {
@ViewInject(R.id.image)
ImageView image;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//src第一种
// setImageBitmapData();
//src第二种
// setImageResourceData();
//src第三种
// setImageDrawableData();
//background第一种
// setImageBackgroundResourceData();
//background第二种
// setImageBackgroundDrawableData();
}
/**
* src第三种
*/
private void setImageDrawableData() {
image.setImageDrawable(getResources().getDrawable(R.drawable.guide_1));
}
/**
* src第二种
*/
private void setImageResourceData() {
image.setImageResource(R.drawable.guide_2);
}
/**
* src第一种
*/
private void setImageBitmapData() {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.guide_1);
image.setImageBitmap(bitmap);
}
/**
* background第一种
*/
private void setImageBackgroundResourceData() {
image.setBackgroundResource(R.drawable.guide_1);
}
/**
* background第二种
*/
private void setImageBackgroundDrawableData() {
image.setBackgroundDrawable(getResources().getDrawable(R.drawable.guide_1));
}
//API 16以上支持
// private void setImageBackgroundData() {
// image.setBackground(getResources().getDrawable(R.drawable.guide_1));
// }
}

以上代码我进行了简单的注释,依次放开注释就会得到以下的效果:

  1. src第一种~src第三种
    src第一种~src第三种

  2. background第一种~background第二种
    background第一种~background第二种

以上两种效果不注意看还发现不出来,下面一个全屏的,而上面一个是空出来的,也就是说显示的是原图,而下一张则是拉伸了全屏显示。这也是src与background的区别。

background会根据ImageView组件给定的长宽进行拉伸,而src就存放的是原图的大小,不会进行拉伸 。src是图片内容(前景),bg是背景,可以同时使用。

此外: scaleType只对src起作用;bg可设置透明度。

  1. setBackground
  2. setBackgroundResource
  3. setBackgroundDrawable

对应属性background,会根据ImageView组件给定的长宽进行拉伸。

  1. setImageBitmap
  2. setImageResource

对应属性src,存放的是原图的大小,不会进行拉伸。如果想改变src的大小,应该使用属性scaleType。

之所以有这两个,是考虑到src如果是PNG格式等有透明属性的图片的话,就会在透明的地方显示出设置的background的背景,而不是黑色或者其他系统默认的填充色等。这样也是有助于美观的。

如,我们指定一张透明图,然后显示背景的红色:

1
2
3
4
5
6
7
<ImageView
android:background="#FF0000"
android:id="@+id/image"
android:src="@drawable/bg_ac"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"/>

效果为:

红色

若指定为蓝色:

1
2
3
4
5
6
7
<ImageView
android:background="#0000FF"
android:id="@+id/image"
android:src="@drawable/bg_ac"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"/>

效果为:
蓝色

1.4、ScaleType

参考官网解释:链接(需翻墙)

类型 解释 翻译
CENTER Center the image in the view, but perform no scaling,From XML, use this syntax: android:scaleType=”center” 在视图中心显示图片,并且不缩放图片
CENTER_CROP Scale the image uniformly (maintain the image’s aspect ratio) so that both dimensions (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding). From XML, use this syntax: android:scaleType=”centerCrop”. 按比例缩放图片,使得图片长 (宽)的大于等于视图的相应维度
CENTER_INSIDE Scale the image uniformly (maintain the image’s aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding). From XML, use this syntax: android:scaleType=”centerInside”. 按比例缩放图片,使得图片长 (宽)的小于等于视图的相应维度
FIT_CENTER Scale the image using CENTER. From XML, use this syntax: android:scaleType=”fitCenter” 按比例缩放图片到视图的最小边,居中显示
FIT_END Scale the image using END. From XML, use this syntax: android:scaleType=”fitEnd”. 按比例缩放图片到视图的最小边,显示在视图的下部分位置
FIT_START Scale the image using START. From XML, use this syntax: android:scaleType=”fitStart” 把图片按比例扩大/缩小到视图的最小边,显示在视图的上部分位置
FIT_XY Scale the image using FILL. From XML, use this syntax: android:scaleType=”fitXY”. 把图片不按比例缩放到视图的大小显示
MATRIX Scale using the image matrix when drawing. From XML, use this syntax: android:scaleType=”matrix”. 用矩阵来绘制

我们通过实例来解释上面的type,加深理解。

1.4.1、fitEnd,fitStart,fitCenter

这组值的相同点就是缩放比例不变,原图什么长宽比例,缩放过后的比例保持不变。我们以一张原始图来看:
原始图

1
2
3
4
5
6
7
8
<ImageView
android:id="@+id/image0"
android:layout_width="768px"
android:layout_height="600px"
android:scaleType="fitStart"
android:background="#3a3635"
android:src="@drawable/ic_launcher"
/>

其中设置的fitStart是啥意思呢?先看一下效果图:
fitStart

看到了什么?这张图被放大了,同时也是等比例放大的,原图大小是48px * 48px 的图片,通过设置了fitStart属性,上面的xml代码中我们设置了imageview的宽和高分别为768px和600px,所以这个属性的意思就是将图片放大到600px然后居于左上角显示。

同理:fitEnd和fitCenter的效果分别为:
fitEnd

(fitEnd)

fitCenter

(fitCenter)

对于fitXY要另外说,因为它破坏了等比例,是宽和高分别放缩直至imageview的大小,即原图的48 48 放到768600的大小。如下图所示:
fitXY

(fitXY)很明显,已经变形了。

1.4.2、centerCrop与centerInside
1
2
3
centerCrop:按横纵比缩放,直接完全覆盖整个ImageView
centerInside:按横纵比缩放,使得ImageView能够完全显示这个图片
1
2
3
4
5
6
7
8
<ImageView
android:id="@+id/image0"
android:layout_width="700px"
android:layout_height="700px"
android:scaleType="centerInside"
android:background="#3a3635"
android:src="@drawable/guide_1"
/>

centerInside

(centerInside) 通过等缩放完全显示原图片

centerCrop

(centerCrop)裁剪显示原图缩放到imageview的大小

1.4.3、matrix

矩阵变换,从原图的左上角开始绘图,如果原图的大小超过了imageview的大小,那么开始裁剪显示。
matrix

1.4.4、center

保持原图的大小,显示在ImageView的中心。当原图的size大于ImageView的size,超过部分裁剪处理。

1
2
3
4
5
6
7
8
<ImageView
android:id="@+id/image0"
android:layout_width="500px"
android:layout_height="500px"
android:scaleType="center"
android:background="#3a3635"
android:src="@drawable/head"
/>

center

(center)原图的大小没有超过imageview的大小,直接居中显示

center

(center)原图的大小超过imageview的大小,裁剪显示。

2、ImageButton

2.1、继承关系

1
2
3
4
5
6
7
8
java.lang.Object
↳ android.view.View
↳ android.widget.ImageView
↳ android.widget.ImageButton
Known Direct Subclasses
AppCompatImageButton, FloatingActionButton, ZoomButton

从上面的继承关系来看,ImageButton继承至ImageView,所以我们来看看他们之间的区别。

Class Overview

Displays a button with an image (instead of text) that can be pressed or clicked by the user. By default, an ImageButton looks like a regular Button, with the standard button background that changes color during different button states. The image on the surface of the button is defined either by the android:src attribute in the XML element or by the setImageResource(int) method.

To remove the standard button background image, define your own background image or set the background color to be transparent.

To indicate the different button states (focused, selected, etc.), youcan define a different image for each state. E.g., a blue image by default, an orange one for when focused, and a yellow one for when pressed. An easy way to do this is with an XML drawable “selector.”For example:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/button_pressed" /> <!-- pressed -->
<item android:state_focused="true"
android:drawable="@drawable/button_focused" /> <!-- focused -->
<item android:drawable="@drawable/button_normal" /> <!-- default -->
</selector>

Save the XML file in your project res/drawable/ folder and then reference it as a drawable for the source of your ImageButton (in the android:src attribute). Android will automatically change the image based on the state of the button and the corresponding images defined in the XML.

The order of the elements is important because they are evaluated in order. This is why the “normal” button image comes last, because it will only be applied after android:state_pressed and android:state_focused have both evaluated false.

其实ImageButton和Button的用法基本类似,至于与图片相关的则和后面ImageView相同


http://crazyandcoder.github.io/