您的位置:首页 > 移动开发 > Android开发

Android ImageView设置图片原理(上)

2014-04-16 10:15 615 查看
 本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!

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

如下:

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

[html] view
plaincopyprint?





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();  

        }  

    }  

[html] view
plaincopyprint?





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);获得图片资源运行在子线程

[html] view
plaincopyprint?





/**  

 * 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();  

    }  

}  

[html] view
plaincopyprint?





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);获得图片资源可以运行在子线程,且可以改变图片大小

[html] view
plaincopyprint?





/**  

 * 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));  

}  

[html] view
plaincopyprint?





/**  

 * 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;  

}  

[html] view
plaincopyprint?





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);  

  }  

[html] view
plaincopyprint?





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:

[html] view
plaincopyprint?





/**  

 * 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();  

        }  

    }  

}  

[html] view
plaincopyprint?





* 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():

[html] view
plaincopyprint?





/**  

  * 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);  

 }  

[html] view
plaincopyprint?





/**  

 * 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(this, null);  

                return;  

            }  

        }  

  

        if (p != null && ai != null) {  

            final Rect r = ai.mTmpInvalRect;  

            r.set(0, 0, mRight - mLeft, mBottom - mTop);  

            // Don't call invalidate -- we don't want to internally scroll  

            // our own bounds  

            p.invalidateChild(this, r);  

        }  

    }  

}  

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

[html] view
plaincopyprint?





If either {@link #requestLayout()} or {@link #invalidate()} were called,  

 * the framework will take care of measuring, laying out, and drawing the tree  

 * as appropriate.  

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

[html] view
plaincopyprint?





/**  

  * <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);  

 }  

[html] view
plaincopyprint?





/**  

 * <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值。

 本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: