Android中对View的更新有很多种方式,使用时要区分不同的应用场合。
1.不使用多线程和双缓冲
这种情况最简单,一般只是希望在View发生改变时对UI进行重绘。你只需显式地调用View对象中的invalidate(){关于invalidate的解释:当调用线程处于空闲状态时,会调用onDraw,刷新界面,也就是说,该函数仅是标记当前界面过期,并不直接负责刷新界面;}方法即可。系统会自动调用View的onDraw()方法。
2.使用多线程但不使用双缓冲
这种情况需要开启新的线程,新开的线程就不好访问View对象了。强行访问的话会报:android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.
这时候你需要创建一个继承了android.os.Handler的子类,并重写handleMessage(Message msg)方法。android.os.Handler是能发送和处理消息的,你需要在Activity中发出更新UI的消息,然后在Handler(可以使用匿名内部类)中处理消息(因为匿名内部类可以访问父类变量, 你可以直接调用View对象中的invalidate()方法 )。也就是说:在新线程创建并发送一个Message,然后再主线程中捕获、处理该消息。
3.使用多线程和双缓冲
Android中SurfaceView是View的子类,她同时也实现了双缓冲。可以定义一个她的子类并实现SurfaceHolder.Callback接口。由于实现SurfaceHolder.Callback接口,新线程就不需要android.os.Handler帮忙了。SurfaceHolder中lockCanvas()方法可以锁定画布,绘制完新的图像后调用unlockCanvasAndPost(canvas)解锁(显示)
先看看源代码对SurfaceHolder接口的描述

/** * 允许你控制surface view的大小、样式,编辑像素或监视surface的改变,典型的运用于SurfaceView中,需要注意
* lockCanvas方法和Callback.surfaceCreated方法
*/

再看SurfaceHolder.Callback的描述

    /**     * A client may implement this interface to receive information about     * changes to the surface.  When used with a {@link SurfaceView}, the     * Surface being held is only available between calls to     * {@link #surfaceCreated(SurfaceHolder)} and     * {@link #surfaceDestroyed(SurfaceHolder)}.  The Callback is set with     * {@link SurfaceHolder#addCallback SurfaceHolder.addCallback} method.     */

下面是一个继承自SurfaceView并实现SurfaceHolder.Callback接口的类

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {    private SurfaceHolder holder;    public MySurfaceView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public MySurfaceView(Context context) {        super(context);        holder = this.getHolder();        holder.addCallback(this);        this.setLongClickable(true);// 不设置将无法捕捉onFling()事件        setFocusable(true);// 设置键盘焦点        setFocusableInTouchMode(true);// 设置触摸屏焦点    }    protected void paintView(Canvas canvas) { // 自定义方法,类似于onDraw    }public void rePaint() { // 自定义类似于invalidate方法,调用此方法刷新View        Canvas c = null;        try {            c = holder.lockCanvas();            paintView(c);        } finally {            if (c != null) {                holder.unlockCanvasAndPost(c);            }        }    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        Canvas canvas = holder.lockCanvas(null);// 获取画布        canvas.drawColor(Color.WHITE);// 设置画布背景        holder.unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width,            int height) {    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {    }}

------------------------------------------------------View的绘制流程-----------------------------------------------------

View的绘制绘制流程:父View负责刷新、布局、显示子View;而当子View需要刷新时,则是通知父View来完成。下面通过查看原代码来验证
1.子类调用invalidate方法()

    /**     * 使当前View无效. 如果View可见,onDraw方法将会在之后某个时间点被调用,这个方法的调用必须在UI线程中,如果在非UI线程中调用需要使用postInvalidate()方法*/    public void invalidate() {        invalidate(true);    }    /**     * invalidate实际上是调用这个方法.drawing的缓存被设置为无效之后一个完整的invalidate将会发生.但是这个功能可以通过设置invalidateCachefalse来跳过无效的步骤当并不需要重新绘制View的时候(例如,一个组件保持着同样的尺寸和内容)     * @param invalidateCache 这个View的缓存是否应该被设置为无效,通常是false表示要进行全部绘制,但是可能设置为true当View的Content和dimension都没有改变时.     */    void invalidate(boolean invalidateCache) {        if (skipInvalidate()) {            return;        }        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) ||                (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) ||                (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || isOpaque() != mLastIsOpaque) {            // ......final AttachInfo ai = mAttachInfo; // 获取匹配 final ViewParent p = mParent; // 获取父类对象 // noinspection PointlessBooleanExpression,ConstantConditions            if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {                if (p != null && ai != null && ai.mHardwareAccelerated) {                    p.invalidateChild(this, null);                    return;                }            }            if (p != null && ai != null) {                final Rect r = ai.mTmpInvalRect;                r.set(0, 0, mRight - mLeft, mBottom - mTop); // 设置View的尺寸                p.invalidateChild(this, r); // 调用parent对象让parent对象重绘制child            }        }    }

>>2.child View调用invalidate时,首先找到自己父View(View的成员变量mParent记录自己的父View),然后将AttachInfo中保存的信息告诉父View刷新自己,父View调用invalidateChild函数刷新child View
下面查看ViewGroup中的invalidateChild方法的实现

    /**     * 不要调用或重写此方法,这个方法是用于实现View的绘制层次     */    public final void invalidateChild(View child, final Rect dirty) {        ViewParent parent = this;        final AttachInfo attachInfo = mAttachInfo;        if (attachInfo != null) {            // 如果child view绘制的是动画,我们希望child的mPrivateFlags拷贝到ViewGroup之上            // 并且让parent确保无效的请求通过            final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)                    == PFLAG_DRAW_ANIMATION;            // ...final int[] location = attachInfo.mInvalidateChildLocation;            location[CHILD_LEFT_INDEX] = child.mLeft;            location[CHILD_TOP_INDEX] = child.mTop;            // ...do {                View view = null;                if (parent instanceof View) {                    view = (View) parent;                }                if (drawAnimation) {                    if (view != null) {                        view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;                    } else if (parent instanceof ViewRootImpl) {                        ((ViewRootImpl) parent).mIsAnimating = true;                    }                }                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque                // flag coming from the child that initiated the invalidate                if (view != null) {                    if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&                            view.getSolidColor() == 0) {                        opaqueFlag = PFLAG_DIRTY;                    }                    if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {                        view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;                    }                }                parent = parent.invalidateChildInParent(location, dirty); // 转到第三步,调用此方法层层刷新View if (view != null) {                    // Account for transform on current parent                    Matrix m = view.getMatrix();                    if (!m.isIdentity()) {                        RectF boundingRect = attachInfo.mTmpTransformRect;                        boundingRect.set(dirty);                        m.mapRect(boundingRect);                        dirty.set((int) (boundingRect.left - 0.5f),                                (int) (boundingRect.top - 0.5f),                                (int) (boundingRect.right + 0.5f),                                (int) (boundingRect.bottom + 0.5f));                    }                }            } while (parent != null);        }    }

3>>.调用invalidateChildInParent函数依次层层刷新

    /**     * 这个方法返回null如果ViewGroup已经没有父View了,     * 或者如果这个ViewGrop已经全部被设置为无效,或者当前View的需要刷新的rectangle区域与ViewGroup不相交     */    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {        if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||                (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=                        FLAG_OPTIMIZE_INVALIDATE) {                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,                        location[CHILD_TOP_INDEX] - mScrollY); // 根据父View的位置,偏移刷新区域 final int left = mLeft;                final int top = mTop;                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {                    if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) { // 计算实际可刷新区域                        dirty.setEmpty();                    }                }                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;                location[CHILD_LEFT_INDEX] = left;                location[CHILD_TOP_INDEX] = top;                if (mLayerType != LAYER_TYPE_NONE) {                    mPrivateFlags |= PFLAG_INVALIDATED;                    mLocalDirtyRect.union(dirty);                }                return mParent;            } else {                mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;                location[CHILD_LEFT_INDEX] = mLeft;                location[CHILD_TOP_INDEX] = mTop;                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {                    dirty.set(0, 0, mRight - mLeft, mBottom - mTop);                } else {                    // in case the dirty rect extends outside the bounds of this container                    dirty.union(0, 0, mRight - mLeft, mBottom - mTop);                }                if (mLayerType != LAYER_TYPE_NONE) {                    mPrivateFlags |= PFLAG_INVALIDATED;                    mLocalDirtyRect.union(dirty);                }                return mParent;            }        }        return null;    }

更多相关文章

  1. Android(安卓)使用Scroller实现绚丽的ListView左右滑动删除Item
  2. Android学习中遇到的优秀文章的总结(持续更新)
  3. 【Android】AsyncTask源码分析
  4. Android(安卓)Webview组件使用总结
  5. Android之预览PDF文件
  6. Android(安卓)中解决Viewpage调用notifyDataSetChanged()时界面
  7. android面试题(一)
  8. Android(安卓)View(二)-View的scrollTo()以及scrollBy()说明
  9. Android异步2:深入详解 Handler+Looper+MessageQueue

随机推荐

  1. Android(安卓)boundCenterBottom
  2. Android 如何搭建一个局域网 Web 服务器
  3. Android(安卓)Matrix源码详解
  4. ListView中item点击事件、item保持选中状
  5. android 背景shape的使用
  6. Android Display System Surface Flinger
  7. Android 设置进度条背景
  8. Android 消息机制分析
  9. Android源码中添加Livewallpapers(动态墙
  10. Textview 文本旋转,倾斜