• 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/ ,引用必须注明出处!

  • 相关阅读:
    Android 序列化 反序列功能
    Android两个应用之间共享数据之SharedPrefence
    利用Asp.Net的Identity控制登录权限
    基元类型
    CLR概述
    python中的函数-基础
    较为复杂的sql
    oracle中的替换字段字符串的方法
    使用Sql查看表对应的外键关系。
    js中的除法
  • 原文地址:https://www.cnblogs.com/fengju/p/6174421.html
Copyright © 2020-2023  润新知