zoukankan      html  css  js  c++  java
  • Android View 绘制刷新流程分析

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

     

  • 相关阅读:
    svn常用命令
    mysql5.6 sql_mode设置
    centos6.5 mysql5.6主从复制
    linux 挂载windows共享文件夹
    hadoop+hive+hbase+zookeeper安装
    Linux踢出登陆用户的正确姿势
    个人博客项目部署到腾讯云记录(私人记录)
    Python中的单例模式的几种实现方式和优化以及pyc文件解释(转)
    关于window.location.hash的理解及其应用(转)
    Django model反向关联名称的方法(转)
  • 原文地址:https://www.cnblogs.com/a284628487/p/3175614.html
Copyright © 2011-2022 走看看