Android ImageView设置图片原理

本文转载自: http://blog.csdn.net/liuxian13183/  

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

如下:

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

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 the drawable  
     *  
     * @attr ref android.R.styleable#ImageView_src  
     */  
    @android.view.RemotableViewMethod  
    public void setImageResource(int resId) {  
        if (mUri != null || mResource != resId) {  
            updateDrawable(null);  
            mResource = resId;  
            mUri = null;  
            resolveUri();  
            requestLayout();  
            invalidate();  
        }  
    }  
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) {  
             Log.w("ImageView""Unable to find resource: " + mResource, e);  
             // Don't try again.  
             mUri = null;  
         }  
     } else if (mUri != null) {  
从源码上看,mResource不为空,而mUri不为空,所以下面的方法咱们只贴出来一部分,可以看到,图片drawable值是通过Resource对象在UI线程中完成。方法上面的解释也可佐证,同时引出下面两个问题,由于SetImageResource会使UI线程延迟,所以可以考虑下面两种做法

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

/**  
 * Sets a drawable as the content of this ImageView.  
 *   
 * @param drawable The drawable to set  
 */  
public void setImageDrawable(Drawable drawable) {  
    if (mDrawable != drawable) {  
        mResource = 0;  
        mUri = null;  
  
        int oldWidth = mDrawableWidth;  
        int oldHeight = mDrawableHeight;  
  
        updateDrawable(drawable);  
  
        if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {  
            requestLayout();  
        }  
        invalidate();  
    }  
}  
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);  
        mDrawableWidth = d.getIntrinsicWidth();  
        mDrawableHeight = d.getIntrinsicHeight();  
        applyColorMod();  
        configureBounds();  
    } else {  
        mDrawableWidth = mDrawableHeight = -1;  
    }  
}  
从源码上看,只需要把获得的值,解析一下,刷新到UI线程即可,所以Drawable对象可以在子线程中获取。

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

/**  
 * Sets a Bitmap as the content of this ImageView.  
 *   
 * @param bm The bitmap to set  
 */  
@android.view.RemotableViewMethod  
public void setImageBitmap(Bitmap bm) {  
    // if this is used frequently, may handle bitmaps explicitly  
    // to reduce the intermediate drawable object  
    setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));  
}  
/**  
 * Create drawable from a bitmap, setting initial target density based on  
 * the display metrics of the resources.  
 */  
public BitmapDrawable(Resources res, Bitmap bitmap) {  
    this(new BitmapState(bitmap), res);  
    mBitmapState.mTargetDensity = mTargetDensity;  
}  
private BitmapDrawable(BitmapState state, Resources res) {  
      mBitmapState = state;  
      if (res != null) {  
          mTargetDensity = res.getDisplayMetrics().densityDpi;  
      } else {  
          mTargetDensity = state.mTargetDensity;  
      }  
      setBitmap(state != null ? state.mBitmap : null);  
  }  
private void setBitmap(Bitmap bitmap) {  
    if (bitmap != mBitmap) {  
        mBitmap = bitmap;  
        if (bitmap != null) {  
            computeBitmapSize();  
        } else {  
            mBitmapWidth = mBitmapHeight = -1;  
        }  
        invalidateSelf();  
    }  
}  
SetImageBitmap同理,其实是通过BitmapDrawable获得一个Drawable对象,同setImageDrawable。值可以在子线程获得,在主界面刷新。

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

requestLayout:

/**  
 * Call this when something has changed which has invalidated the  
 * layout of this view. This will schedule a layout pass of the view  
 * tree.  
 */  
public void requestLayout({  
    if (ViewDebug.TRACE_HIERARCHY) {  
        ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);  
    }  
  
    mPrivateFlags |= FORCE_LAYOUT;  
    mPrivateFlags |= INVALIDATED;  
  
    if (mParent != null) {  
        if (mLayoutParams != null) {  
            mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());  
        }  
        if (!mParent.isLayoutRequested()) {  
            mParent.requestLayout();  
        }  
    }  
}  
* To intiate a layout, call {@link #requestLayout}. This method is typically  
* called by a view on itself when it believes that is can no longer fit within  
* its current bounds.  
最主要的是这句,主要是当前View已经放不用所存放的值时,需要重新计算宽高

Invalidate():

/**  
  * Invalidate the whole view. If the view is visible,  
  * {@link #onDraw(android.graphics.Canvas)} will be called at some point in  
  * the future. This must be called from a UI thread. To call from a non-UI thread,  
  * call {@link #postInvalidate()}.  
  */  
 public void invalidate() {  
     invalidate(true);  
 }  
/**  
 * This is where the invalidate() work actually happens. A full invalidate()  
 * causes the drawing cache to be invalidated, but this function can be called with  
 * invalidateCache set to false to skip that invalidation step for cases that do not  
 * need it (for example, a component that remains at the same dimensions with the same  
 * content).  
 *  
 * @param invalidateCache Whether the drawing cache for this view should be invalidated as  
 * well. This is usually true for a full invalidate, but may be set to false if the  
 * View's contents or dimensions have not changed.  
 */  
void invalidate(boolean invalidateCache) {  
    if (ViewDebug.TRACE_HIERARCHY) {  
        ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);  
    }  
  
    if (skipInvalidate()) {  
        return;  
    }  
    if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||  
            (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) ||  
            (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) {  
        mLastIsOpaque = isOpaque();  
        mPrivateFlags &= ~DRAWN;  
        mPrivateFlags |= DIRTY;  
        if (invalidateCache) {  
            mPrivateFlags |= INVALIDATED;  
            mPrivateFlags &= ~DRAWING_CACHE_VALID;  
        }  
        final AttachInfo ai = mAttachInfo;  
        final ViewParent p = mParent;  
        //noinspection PointlessBooleanExpression,ConstantConditions  
        if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {  
            if (p != null && ai != null && ai.mHardwareAccelerated) {  
                // fast-track for GL-enabled applications; just invalidate the whole hierarchy  
                // with a null dirty rect, which tells the ViewAncestor to redraw everything  
                p.invalidateChild(thisnull);  
                return;  
            }  
        }  
  
        if (p != null && ai != null) {  
            final Rect r = ai.mTmpInvalRect;  
            r.set(00, mRight - mLeft, mBottom - mTop);  
            // Don't call invalidate -- we don't want to internally scroll  
            // our own bounds  
            p.invalidateChild(this, r);  
        }  
    }  
}  

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

If either {@link #requestLayout()} or {@link #invalidate()} were called,  
 * the framework will take care of measuring, laying outand drawing the tree  
 * as appropriate.  
与此同时,我们还可以看到一个知识点postInvalidate
/**  
  * <p>Cause an invalidate to happen on a subsequent cycle through the event loop.  
  * Use this to invalidate the View from a non-UI thread.</p>  
  *  
  * <p>This method can be invoked from outside of the UI thread  
  * only when this View is attached to a window.</p>  
  *   
  * @see #invalidate()  
  */  
 public void postInvalidate() {  
     postInvalidateDelayed(0);  
 }  
/**  
 * <p>Cause an invalidate to happen on a subsequent cycle through the event  
 * loop. Waits for the specified amount of time.</p>  
 *   
 * <p>This method can be invoked from outside of the UI thread  
 * only when this View is attached to a window.</p>  
 *  
 * @param delayMilliseconds the duration in milliseconds to delay the  
 *         invalidation by  
 */  
public void postInvalidateDelayed(long delayMilliseconds) {  
    // We try only with the AttachInfo because there's no point in invalidating  
    // if we are not attached to our window  
    AttachInfo attachInfo = mAttachInfo;  
    if (attachInfo != null) {  
        Message msg = Message.obtain();  
        msg.what = AttachInfo.INVALIDATE_MSG;  
        msg.obj = this;  
        attachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);  
    }  
}  
这个刷新会排到消息队列去刷新,也就是在空闲时刷新,相对来说可以减少阻塞,此任务优先级稍低,当然我们可以加入延迟时间来相对自定义优先级。

在开发过程中,大概会遇到这样的情况,在布局的时候,需要图片的宽度或长度;或者图片太大,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值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值