Android ImageView设置图片原理(上)



 本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!

首先关于图片加载到ImageView上,我们来讨论几个问题:

如下:

imageView.setImageResource(resId);获得图片资源运行在主线程

  1. This does Bitmap reading and decoding on the UI  
  2.      * thread, which can cause a latency hiccup.  If that's a concern,  
  3.      * consider using {@link #setImageDrawable(android.graphics.drawable.Drawable)} or  
  4.      * {@link #setImageBitmap(android.graphics.Bitmap)} and  
  5.      * {@link android.graphics.BitmapFactory} instead.</p>  
  6.      *  
  7.      * @param resId the resource identifier of the the drawable  
  8.      *  
  9.      * @attr ref android.R.styleable#ImageView_src  
  10.      */  
  11.     @android.view.RemotableViewMethod  
  12.     public void setImageResource(int resId) {  
  13.         if (mUri != null || mResource != resId) {  
  14.             updateDrawable(null);  
  15.             mResource = resId;  
  16.             mUri = null;  
  17.             resolveUri();  
  18.             requestLayout();  
  19.             invalidate();  
  20.         }  
  21.     }  
  1. private void resolveUri() {  
  2.      if (mDrawable != null) {  
  3.          return;  
  4.      }  
  5.   
  6.      Resources rsrc = getResources();  
  7.      if (rsrc == null) {  
  8.          return;  
  9.      }  
  10.   
  11.      Drawable d = null;  
  12.   
  13.      if (mResource != 0) {  
  14.          try {  
  15.              d = rsrc.getDrawable(mResource);  
  16.          } catch (Exception e) {  
  17.              Log.w("ImageView", "Unable to find resource: " + mResource, e);  
  18.              // Don't try again.  
  19.              mUri = null;  
  20.          }  
  21.      } else if (mUri != null) {  


从源码上看,mResource不为空,而mUri不为空,所以下面的方法咱们只贴出来一部分,可以看到,图片drawable值是通过Resource对象在UI线程中完成。方法上面的解释也可佐证,同时引出下面两个问题,由于SetImageResource会使UI线程延迟,所以可以考虑下面两种做法


imageView.setImageDrawable(drawable);获得图片资源运行在子线程

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

  1. private void updateDrawable(Drawable d) {  
  2.     if (mDrawable != null) {  
  3.         mDrawable.setCallback(null);  
  4.         unscheduleDrawable(mDrawable);  
  5.     }  
  6.     mDrawable = d;  
  7.     if (d != null) {  
  8.         d.setCallback(this);  
  9.         if (d.isStateful()) {  
  10.             d.setState(getDrawableState());  
  11.         }  
  12.         d.setLevel(mLevel);  
  13.         mDrawableWidth = d.getIntrinsicWidth();  
  14.         mDrawableHeight = d.getIntrinsicHeight();  
  15.         applyColorMod();  
  16.         configureBounds();  
  17.     } else {  
  18.         mDrawableWidth = mDrawableHeight = -1;  
  19.     }  
  20. }  

从源码上看,只需要把获得的值,解析一下,刷新到UI线程即可,所以Drawable对象可以在子线程中获取。


imageView.setImageBitmap(bm);获得图片资源可以运行在子线程,且可以改变图片大小


  1. /**  
  2.  * Sets a Bitmap as the content of this ImageView.  
  3.  *   
  4.  * @param bm The bitmap to set  
  5.  */  
  6. @android.view.RemotableViewMethod  
  7. public void setImageBitmap(Bitmap bm) {  
  8.     // if this is used frequently, may handle bitmaps explicitly  
  9.     // to reduce the intermediate drawable object  
  10.     setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));  
  11. }  

  1. /**  
  2.  * Create drawable from a bitmap, setting initial target density based on  
  3.  * the display metrics of the resources.  
  4.  */  
  5. public BitmapDrawable(Resources res, Bitmap bitmap) {  
  6.     this(new BitmapState(bitmap), res);  
  7.     mBitmapState.mTargetDensity = mTargetDensity;  
  8. }  

  1. private BitmapDrawable(BitmapState state, Resources res) {  
  2.       mBitmapState = state;  
  3.       if (res != null) {  
  4.           mTargetDensity = res.getDisplayMetrics().densityDpi;  
  5.       } else {  
  6.           mTargetDensity = state.mTargetDensity;  
  7.       }  
  8.       setBitmap(state != null ? state.mBitmap : null);  
  9.   }  

  1. private void setBitmap(Bitmap bitmap) {  
  2.     if (bitmap != mBitmap) {  
  3.         mBitmap = bitmap;  
  4.         if (bitmap != null) {  
  5.             computeBitmapSize();  
  6.         } else {  
  7.             mBitmapWidth = mBitmapHeight = -1;  
  8.         }  
  9.         invalidateSelf();  
  10.     }  
  11. }  

SetImageBitmap同理,其实是通过BitmapDrawable获得一个Drawable对象,同setImageDrawable。值可以在子线程获得,在主界面刷新。


与此同时,有两个方法出现频率比较高,requestLayout和invalidate方法。

requestLayout:

  1. /**  
  2.  * Call this when something has changed which has invalidated the  
  3.  * layout of this view. This will schedule a layout pass of the view  
  4.  * tree.  
  5.  */  
  6. public void requestLayout() {  
  7.     if (ViewDebug.TRACE_HIERARCHY) {  
  8.         ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);  
  9.     }  
  10.   
  11.     mPrivateFlags |= FORCE_LAYOUT;  
  12.     mPrivateFlags |= INVALIDATED;  
  13.   
  14.     if (mParent != null) {  
  15.         if (mLayoutParams != null) {  
  16.             mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());  
  17.         }  
  18.         if (!mParent.isLayoutRequested()) {  
  19.             mParent.requestLayout();  
  20.         }  
  21.     }  
  22. }  
  1. * To intiate a layout, call {@link #requestLayout}. This method is typically  
  2. * called by a view on itself when it believes that is can no longer fit within  
  3. * its current bounds.  

最主要的是这句,主要是当前View已经放不用所存放的值时,需要重新计算宽高


Invalidate():

  1. /**  
  2.   * Invalidate the whole view. If the view is visible,  
  3.   * {@link #onDraw(android.graphics.Canvas)} will be called at some point in  
  4.   * the future. This must be called from a UI thread. To call from a non-UI thread,  
  5.   * call {@link #postInvalidate()}.  
  6.   */  
  7.  public void invalidate() {  
  8.      invalidate(true);  
  9.  }  

  1. /**  
  2.  * This is where the invalidate() work actually happens. A full invalidate()  
  3.  * causes the drawing cache to be invalidated, but this function can be called with  
  4.  * invalidateCache set to false to skip that invalidation step for cases that do not  
  5.  * need it (for example, a component that remains at the same dimensions with the same  
  6.  * content).  
  7.  *  
  8.  * @param invalidateCache Whether the drawing cache for this view should be invalidated as  
  9.  * well. This is usually true for a full invalidate, but may be set to false if the  
  10.  * View's contents or dimensions have not changed.  
  11.  */  
  12. void invalidate(boolean invalidateCache) {  
  13.     if (ViewDebug.TRACE_HIERARCHY) {  
  14.         ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);  
  15.     }  
  16.   
  17.     if (skipInvalidate()) {  
  18.         return;  
  19.     }  
  20.     if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||  
  21.             (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) ||  
  22.             (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) {  
  23.         mLastIsOpaque = isOpaque();  
  24.         mPrivateFlags &= ~DRAWN;  
  25.         mPrivateFlags |= DIRTY;  
  26.         if (invalidateCache) {  
  27.             mPrivateFlags |= INVALIDATED;  
  28.             mPrivateFlags &= ~DRAWING_CACHE_VALID;  
  29.         }  
  30.         final AttachInfo ai = mAttachInfo;  
  31.         final ViewParent p = mParent;  
  32.         //noinspection PointlessBooleanExpression,ConstantConditions  
  33.         if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {  
  34.             if (p != null && ai != null && ai.mHardwareAccelerated) {  
  35.                 // fast-track for GL-enabled applications; just invalidate the whole hierarchy  
  36.                 // with a null dirty rect, which tells the ViewAncestor to redraw everything  
  37.                 p.invalidateChild(this, null);  
  38.                 return;  
  39.             }  
  40.         }  
  41.   
  42.         if (p != null && ai != null) {  
  43.             final Rect r = ai.mTmpInvalRect;  
  44.             r.set(0, 0, mRight - mLeft, mBottom - mTop);  
  45.             // Don't call invalidate -- we don't want to internally scroll  
  46.             // our own bounds  
  47.             p.invalidateChild(this, r);  
  48.         }  
  49.     }  
  50. }  


把计算宽高、实际宽高、布局等刷新进来


  1. If either {@link #requestLayout()} or {@link #invalidate()} were called,  
  2.  * the framework will take care of measuring, laying out, and drawing the tree  
  3.  * as appropriate.  

与此同时,我们还可以看到一个知识点postInvalidate

  1. /**  
  2.   * <p>Cause an invalidate to happen on a subsequent cycle through the event loop.  
  3.   * Use this to invalidate the View from a non-UI thread.</p>  
  4.   *  
  5.   * <p>This method can be invoked from outside of the UI thread  
  6.   * only when this View is attached to a window.</p>  
  7.   *   
  8.   * @see #invalidate()  
  9.   */  
  10.  public void postInvalidate() {  
  11.      postInvalidateDelayed(0);  
  12.  }  

  1. /**  
  2.  * <p>Cause an invalidate to happen on a subsequent cycle through the event  
  3.  * loop. Waits for the specified amount of time.</p>  
  4.  *   
  5.  * <p>This method can be invoked from outside of the UI thread  
  6.  * only when this View is attached to a window.</p>  
  7.  *  
  8.  * @param delayMilliseconds the duration in milliseconds to delay the  
  9.  *         invalidation by  
  10.  */  
  11. public void postInvalidateDelayed(long delayMilliseconds) {  
  12.     // We try only with the AttachInfo because there's no point in invalidating  
  13.     // if we are not attached to our window  
  14.     AttachInfo attachInfo = mAttachInfo;  
  15.     if (attachInfo != null) {  
  16.         Message msg = Message.obtain();  
  17.         msg.what = AttachInfo.INVALIDATE_MSG;  
  18.         msg.obj = this;  
  19.         attachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);  
  20.     }  
  21. }  
这个刷新会排到消息队列去刷新,也就是在空闲时刷新,相对来说可以减少阻塞,此任务优先级稍低,当然我们可以加入延迟时间来相对自定义优先级。

在开发过程中,大概会遇到这样的情况,在布局的时候,需要图片的宽度或长度;或者图片太大,ImageView太小,要做图片压缩;这时就要用到接下来讲到的知识点。

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;//API中有说明,此时返回bitmap值为null

bitmap = BitmapFactory.decodeFile(pathName,options);

int height=options.outHeight;//获得图片的高

int width=options.outWidth;//获得图片的宽

options.inJustDecodeBounds=false;//之后设置

bitmap=BitmapFactory.decodeFile(pathName, options);

可获得正确的bitmap值。


 本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!

原文地址:https://www.cnblogs.com/hehehaha/p/6147354.html