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

    xiaoxiao2021-12-10  9

    image派 setImageBitmapsetImageDrawablesetImageResourcesetImageURIupdateDrawable和resolveUri updateDrawableresolveUri background 派 setBackgroundResourcesetBackgroundsetBackgroundDrawableImageview的属性 background和src的区别 总结

    我们知道,对于Imageview显示图片,常用的有一下几种方式

    imaegView.setImageBitmap(); imaegView.setImageResource(); imaegView.setImageDrawable(); imaegView.setImageURI(); imaegView.setBackground(); imaegView.setBackgroundResource(); imaegView.setBackgroundDrawable();

    一般常用的应该就是这几种了,可是这几种有什么区别呢,由以上方法咱们可以分为两个派系,以setImage开头的image派和以setBackground开头的background派,下面分别从源码角度查看分析这两个派系

    image派

    setImageBitmap()

    public void setImageBitmap(Bitmap bm) { setImageDrawable(new BitmapDrawable(mContext.getResources(), bm)); }

    通过源码可知,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(); } }

    由源码可知, * 全局变量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(); } 没有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(); } } 全局变量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; } } 由于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); }

    通过上面的代码可知 * 如果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; }

    把传递过来的resid转换成一个Drawable对象,然后调用setBackground()

    setBackground()

    public void setBackground(Drawable background) { setBackgroundDrawable(background); }

    调用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); }

    注意该方法的注释@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); }

    于是就得到了下面的结论: * 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();

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

    转载请注明原文地址: https://ju.6miu.com/read-700258.html

    最新回复(0)