Android 中ImageView 显示图片的几种方法简要分析

image派
setImageBitmap
setImageDrawable
setImageResource
setImageURI
updateDrawable和resolveUri
updateDrawable
resolveUri
background 派
setBackgroundResource
setBackground
setBackgroundDrawable
Imageview的属性 background和src的区别
总结
我们知道,对于Imageview显示图片,常用的有一下几种方式

 imaegView.setImageBitmap();
 imaegView.setImageResource();
 imaegView.setImageDrawable();
 imaegView.setImageURI();

 imaegView.setBackground();
 imaegView.setBackgroundResource();
 imaegView.setBackgroundDrawable();                   
1
2
3
4
5
6
7
8
一般常用的应该就是这几种了,可是这几种有什么区别呢,由以上方法咱们可以分为两个派系,以setImage开头的image派和以setBackground开头的background派,下面分别从源码角度查看分析这两个派系

image派

setImageBitmap()

public void setImageBitmap(Bitmap bm) {
        setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
    }
1
2
3
通过源码可知,setImageBitmap()最终是把传递过来的bitmap转换成一个Drawable对象,然后调用的setImageDrawable()方法,那setImageDrawable()方法又是干嘛的啊

setImageDrawable()

    public void setImageDrawable(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();
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
由源码可知, 
* 全局变量mResource设置为0, 
* 全局变量mUri置为null, 
* 把传递过来的drawable对象再传递到updateDrawable()法中

setImageResource()

 public void setImageResource(int resId) {
        final int oldWidth = mDrawableWidth;
        final int oldHeight = mDrawableHeight;
        updateDrawable(null);
        mResource = resId;
        mUri = null;
        resolveUri();
        if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
            requestLayout();
        }
        invalidate();
    }
1
2
3
4
5
6
7
8
9
10
11
12
没有Drawable对象,所以 updateDrawable()传递null
把传递过来的res对象赋值给 全局变量mResource,
全局变量mUri置为null,
调用 resolveUri()
setImageURI()

  public void setImageURI(Uri uri) {
        if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) {
            updateDrawable(null);
            mResource = 0;
            mUri = uri;
            final int oldWidth = mDrawableWidth;
            final int oldHeight = mDrawableHeight;
            resolveUri();
            if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
                requestLayout();
            }
            invalidate();
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
全局变量mResource设置为0
把传递过来的uri对象赋值给全局变量mUri,
由于没有Drawable对象,所以 updateDrawable()递null
调用 resolveUri()
从上可知,有两个方法挺关键的,一个是updateDrawable(),还有就是resolveUri(),接下来就查查这两个方法是干嘛的

updateDrawable()和resolveUri()

updateDrawable()

 private void updateDrawable(Drawable d) {
        if (mDrawable != null) {
            mDrawable.setCallback(null);
            unscheduleDrawable(mDrawable);
        }
        mDrawable = d;
        if (d != null) { 
            d.setCallback(this);
            if (d.isStateful()) {
                d.setState(getDrawableState());
            }
            d.setLevel(mLevel);
            d.setLayoutDirection(getLayoutDirection());
            d.setVisible(getVisibility() == VISIBLE, true);
            mDrawableWidth = d.getIntrinsicWidth();
            mDrawableHeight = d.getIntrinsicHeight();
            applyColorMod();
            configureBounds();
        } else {
            mDrawableWidth = mDrawableHeight = -1;
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
由于setImageURI()和setImageResource()递过来的d 为null,只是执行了else 中的语句,即把全局变量 mDrawableWidth = mDrawableHeight 设置为-1
如果d不为null,就是对drawable的一些更新
resolveUri()

private void resolveUri() {
        if (mDrawable != null) {
            return;
        }
        Resources rsrc = getResources();
        if (rsrc == null) {
            return;
        }
        Drawable d = null;
        if (mResource != 0) {
            try {
                d = rsrc.getDrawable(mResource);
            } catch (Exception e) {
                mUri = null;
            }
        } else if (mUri != null) {
            String scheme = mUri.getScheme();
            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
                try {
                    ContentResolver.OpenResourceIdResult r = mContext.getContentResolver().getResourceId(mUri);
                    d = r.r.getDrawable(r.id);
                } catch (Exception e) {
                    Log.w("ImageView", "Unable to open content: " + mUri, e);
                }
            } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
                    || ContentResolver.SCHEME_FILE.equals(scheme)) {
                InputStream stream = null;
                try {
                    stream = mContext.getContentResolver().openInputStream(mUri);
                    d = Drawable.createFromStream(stream, null);
                } catch (Exception e) {
                    Log.w("ImageView", "Unable to open content: " + mUri, e);
                } finally {
                    if (stream != null) {
                        try {
                            stream.close();
                        } catch (IOException e) {
                            Log.w("ImageView", "Unable to close content: " + mUri, e);
                        }
                    }
                }
        } else {
                d = Drawable.createFromPath(mUri.toString());
            }
            if (d == null) {
                System.out.println("resolveUri failed on bad bitmap uri: " + mUri);
                // Don't try again.
                mUri = null;
            }
        } else {
            return;
        }
        updateDrawable(d);
    }
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
通过上面的代码可知 
* 如果mResource不为null ,把mResource 转换成一个Drawable对象,然后执行updateDrawable() 
* 如果mUri不为null,就把uri转换成一个Drawable对象,然后执行updataDrawable()方法

所以,不管是setImageUri还是setImageDrawable或者setImageResource()或者setImageBitmap 
* 首先都是把传递过来的对象转换成一个Drawable对象, 
* 然后把执行updataDrawable()方法, 
* 因为之前在updataDrawable 中重新设置的宽高,所以执行requestLayout() 重新布局view 
* 最后执行invalidate()重新绘制

background 派

 
然后我们发现,background派的方法都是来自与ImageView的父类View中的

setBackgroundResource()

 public void setBackgroundResource(int resid) {
        if (resid != 0 && resid == mBackgroundResource) {
            return;
        }
        Drawable d = null;
        if (resid != 0) {
            d = mContext.getDrawable(resid);
        }
        setBackground(d);
        mBackgroundResource = resid;
    }
1
2
3
4
5
6
7
8
9
10
11
把传递过来的resid转换成一个Drawable对象,然后调用setBackground()

setBackground()

 public void setBackground(Drawable background) {
        setBackgroundDrawable(background);
    }
1
2
3
调用setBackgroundDrawable方法

setBackgroundDrawable

     /**
     * @deprecated use {@link #setBackground(Drawable)} instead
     */
   public void setBackgroundDrawable(Drawable background) {
        computeOpaqueFlags();

        if (background == mBackground) {
            return;
        }
        boolean requestLayout = false;
        mBackgroundResource = 0;
        if (mBackground != null) {
            mBackground.setCallback(null);
            unscheduleDrawable(mBackground);
        }
        if (background != null) {
            Rect padding = sThreadLocal.get();
            if (padding == null) {
                padding = new Rect();
                sThreadLocal.set(padding);
            }
            resetResolvedDrawables();
            background.setLayoutDirection(getLayoutDirection());
            if (background.getPadding(padding)) {
                resetResolvedPadding();
                switch (background.getLayoutDirection()) {
                    case LAYOUT_DIRECTION_RTL:
                        mUserPaddingLeftInitial = padding.right;
                        mUserPaddingRightInitial = padding.left;
                        internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);
                        break;
                    case LAYOUT_DIRECTION_LTR:
                    default:
                        mUserPaddingLeftInitial = padding.left;
                        mUserPaddingRightInitial = padding.right;
                        internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);
                }
                mLeftPaddingDefined = false;
                mRightPaddingDefined = false;
            }
            if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() ||
                    mBackground.getMinimumWidth() != background.getMinimumWidth()) {
                requestLayout = true;
            }
            background.setCallback(this);
            if (background.isStateful()) {
                background.setState(getDrawableState());
            }
            background.setVisible(getVisibility() == VISIBLE, false);
            mBackground = background;
            if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
                mPrivateFlags &= ~PFLAG_SKIP_DRAW;
                mPrivateFlags |= PFLAG_ONLY_DRAWS_BACKGROUND;
                requestLayout = true;
            }
        } else {
            mBackground = null;
            if ((mPrivateFlags & PFLAG_ONLY_DRAWS_BACKGROUND) != 0) {
                mPrivateFlags &= ~PFLAG_ONLY_DRAWS_BACKGROUND;
                mPrivateFlags |= PFLAG_SKIP_DRAW;
            }
            requestLayout = true;
        }
        computeOpaqueFlags();
        if (requestLayout) {
            requestLayout();
        }
        mBackgroundSizeChanged = true;
        invalidate(true);
    }
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
注意该方法的注释@deprecated 说明已经过时了,让使用setBackground()方法,可是我就不明白了,setBackground方法里面是直接调用的setBackgroundDrawable()方法,不知道为啥这样设计,可能Google工程师为了简化方法名称吧。 
setBackgroundDrawable()最后也会执行到requestLayout()和invalidate(true)方法,

其实这几个方法的区别最终归结到就是 
setBackgroundDrawable(drawable)和setImageDrawable(Drawable)的区别 
其实这两个的区别又让我想起了Image属性中的background和src的区别,这又有什么关系呢,然后继续查找代码,在ImageView中的构造函数中找到了答案 
 
然后在View中找关于backgroud属性的一些。

 switch (attr) {
        case com.android.internal.R.styleable.View_background:
             background = a.getDrawable(attr);
             ...
       }
       ....
      if (background != null) {
            setBackground(background);
        }
1
2
3
4
5
6
7
8
9
于是就得到了下面的结论: 
* android:background=”“相当于执行setBackgroundDrawable() 
* android:src=”“相当于执行setBackgroundDrawable()

关于这两个的区别,可以参考这篇blog 
setBackgroundDrawable与setImageDrawable的区别

至于background和src的其他的区别,参考网上得知

Imageview的属性 background和src的区别

src是图片内容(前景),bg是背景,可以同时使用。
background会根据ImageView组件给定的长宽进行拉伸,而src就存放的是原图的大小,不会进行拉伸 。
scaleType只对src起作用;bg可设置透明度。
总结

稍微总结一下,就是关于ImageView 显示图片的几种方式,

 imaegView.setImageBitmap();
 imaegView.setImageResource();
 imaegView.setImageDrawable();
 imaegView.setImageURI();

 imaegView.setBackground();
 imaegView.setBackgroundResource();
 imaegView.setBackgroundDrawable(); 
1
2
3
4
5
6
7
8
主要都是在这两个方法中setBackgroundDrawable(drawable)和setImageDrawable(Drawable),而 android:background=”“相当于执行setBackgroundDrawable() android:src=”“相当于执行setBackgroundDrawable() 
仅此而已,其实最主要 的分析还没有做,就是requestLayout()和invalidate()这两个方法,这两个是关于View的绘制的,以后会做相应的分析。
 

猜你喜欢

转载自blog.csdn.net/NCTU_to_prove_safety/article/details/87966747