Android View刷新机制

一:总体说明

在Android的布局体系中,父View负责刷新、布局显示子View;而当子View需要刷新时,则是通知父View来完成。

二:代码分析

1).ViewGroup的addView方法,理解参数的意义和传递

invalidate调用父类View的方法

addViewInner方法主要做的事情是

view的dispatchAttachedToWindow(AttachInfo info, int visibility)方法

1).View的invalidate方法,这是一个从下第向上回溯的过程,每一层的父View都将自己的显示区域与传入的刷新
Rect做交集。

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);//调用子类的方法完成

}

}

}

2)ViewGrop的invalidateChild方法

public final void invalidateChild(View child, final Rect dirty) {

ViewParent parent = this;

final AttachInfo attachInfo = mAttachInfo;

if (attachInfo != null) {

final int[] location = attachInfo.mInvalidateChildLocation;

// 需要刷新的子View的位置

location[CHILD_LEFT_INDEX] = child.mLeft;

location[CHILD_TOP_INDEX] = child.mTop;

// If the child is drawing an animation, we want to copy this flag onto

// ourselves and the parent to make sure the invalidate request goes through

final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;

// Check whether the child that requests the invalidate is fully opaque

final boolean isOpaque = child.isOpaque() && !drawAnimation && child.getAnimation() != null;

// Mark the child as dirty, using the appropriate flag

// Make sure we do not set both flags at the same time

final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;

do {

View view = null;

if (parent instanceof View) {

view = (View) parent;

}

if (drawAnimation) {

if (view != null) {

view.mPrivateFlags |= DRAW_ANIMATION;

} else if (parent instanceof ViewRoot) {

((ViewRoot) 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 && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {

view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;

}

parent = parent.invalidateChildInParent(location, dirty);

} while (parent != null);

}

}

public ViewParent
invalidateChildInParent(final int[] location, final Rect dirty) {

if ((mPrivateFlags & DRAWN) == DRAWN) {

if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=

FLAG_OPTIMIZE_INVALIDATE) {

   // 根据父View的位置,偏移刷新区域

dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY);

final int left = mLeft;

final int top = mTop;

//计算实际可刷新区域

if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||

(mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {

mPrivateFlags &= ~DRAWING_CACHE_VALID;

location[CHILD_LEFT_INDEX] = left;

location[CHILD_TOP_INDEX] = top;

return mParent;

}

} else {

mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;

location[CHILD_LEFT_INDEX] = mLeft;

location[CHILD_TOP_INDEX] = mTop;

dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX],

mBottom - location[CHILD_TOP_INDEX]);

return mParent;

}

}

return null;

}

这个向上回溯的过程直到ViewRoot那里结束,由ViewRoot对这个最终的刷新区域做刷新

ViewRoot.java

public void invalidateChild(View child, Rect dirty) {

}

由ViewRoot对象的performTraversals()方法调用draw()方法发起绘制该View树,值得注意的是每次发起绘图时,并不会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内部变量包含了一个标志位DRAWN,当该视图需要重绘时,就会为该View添加该标志位。

调用流程 :

mView.draw()开始绘制,draw()方法实现的功能如下:

1 、绘制该View的背景

2 、为显示渐变框做一些准备操作(见5,大多数情况下,不需要改渐变框)

3、调用onDraw()方法绘制视图本身   (每个View都需要重载该方法,ViewGroup不需要实现该方法)

4、调用dispatchDraw ()方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该
方法)值得说明的是,ViewGroup类已经为我们重写了dispatchDraw ()的功能实现,应用程序一般不需要重写该
方法,但可以重载父类函数实现具体的功能。

4.1 dispatchDraw()方法内部会遍历每个子视图,调用drawChild()去重新回调每个子视图的draw()方法(注意,
这个 地方“需要重绘”的视图才会调用draw()方法)。值得说明的是,ViewGroup类已经为我们重写了dispatch
Draw()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。

时间: 2024-11-02 05:38:14

Android View刷新机制的相关文章

Android View工作机制浅析(ppt)

Android View工作机制浅析(ppt)

Android 屏幕刷新机制

这次就来梳理一下 Android 的屏幕刷新机制,把我这段时间因为研究动画而梳理出来的一些关于屏幕刷新方面的知识点分享出来,能力有限,有错的地方还望指点一下.另外,内容有点多,毕竟要讲清楚不容易,所以慢慢看哈 提问环节 阅读源码还是得带着问题或目的性的去阅读,这样阅读过程中比较有条理性,不会跟偏或太深入,所以,还是先来几个问题吧:大伙都清楚,Android 每隔 16.6ms 会刷新一次屏幕. Q1:但是大伙想过没有,这个 16.6ms 刷新一次屏幕到底是什么意思呢?是指每隔 16.6ms 调用

Android View绘制机制

------------------------------------------------------------------------------ GitHub:lightSky    微博:    light_sky, 即时分享最新技术,欢迎关注 ------------------------------------------------------------------------------ 前言 该篇文章来自一个开源项目android-open-project-analy

Android View事件机制 21问21答

1.View的坐标参数 主要有哪些?分别有什么注意的要点? 答:Left,Right,top,Bottom 注意这4个值其实就是 view 和 他的父控件的 相对坐标值. 并非是距离屏幕左上角的绝对值,这点要注意. 此外,X和Y 其实也是相对于父控件的坐标值. TranslationX,TranslationY 这2个值 默认都为0,是相对于父控件的左上角的偏移量. 换算关系: x=left+tranX,y=top+tranY. 很多人不理解,为什么事这样,其实就是View 如果有移动的话,比如

Android View 事件分发机制 源码解析 (上)

一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志~ MyButton [java] view plain copy package com.example.zhy_event03; import android.content.Context; import andr

Android View 触摸事件传递机制

PS:以现在的眼光看以前写的博客感觉写的很烂,或许或一段时间再看现在的博客会有同样的感觉.所以每时每刻都去学习,去发现和理解新的东西. 引言 由于之前写的一篇关于Android事件传递顺序的博客质量太差,可能是理解不到位的原因,故最近又花了许多时间再次去看Android源码,看完之后有了新的理解,所以打算重新整理这篇博客.理解Android触摸事件传递机制有助于日后的开发以及自定义一些手势效果等.注意:这篇博客是基于Android2.0源码来分析的,不管老版本还是新版本的Android,其内部触

Android View框架总结(八)ViewGroup事件分发机制

请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52298780 上篇分析了View的事件分发流程,留了一个问题:如果上面的EventButton继承TextView的话,按下抬起,会有一个现象,我可以告诉大家现象:就是只有dispatchTouchEvent ACTION_DOWN,onTouch ACTION_DOWN,onTouchEvent ACTION_DOWN这三个,你移动,或者抬起,是没有MOVE,或者

Android View的事件分发机制

准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup.如RelativeLayout.LinearLayout.作为一个开发人员,我们会思考.当点击一个button,Android系统是如何确定我点的就是button而不是TextView的?然后还正确的响应了button的点击事件. 内部经过了一系列什么过程呢? 先铺垫一些知识能更加清晰的理解事件分

[Android] View和ViewGroup事件分发机制

在android开发中会经常遇到滑动冲突(比如ScrollView或是SliddingMenu与ListView的嵌套)的问题,需要我们深入的了解android事件响应机制才能解决,事件响应机制已经是android开发者必不可少的知识. 1.涉及到事件响应的常用方法构成 用户在手指与屏幕接触过程中通过MotionEvent对象产生一系列事件,它有四种状态: MotionEvent.ACTION_DOWN :手指按下屏幕的瞬间(一切事件的开始) MotionEvent.ACTION_MOVE :手