Android view 刷新流程

Android view 刷新流程

参考 http://www.2cto.com/kf/201307/226138.html

http://blog.csdn.net/chenzhiqin20/article/details/8628952

1.刷新方法

1.不使用多线程和双缓冲

ui线程中调用invalidate() {关于invalidate的解释:当调用线程处于空闲状态时,会调用onDraw,刷新界面,也就是说,该函数仅是标记当前界面过期,并不直接负责刷新界面;}方法即可。系统会自动调用View的onDraw()方法。

2.使用多线程但不使用双缓冲

使用handler ,在handler 的handlemsg方法中调用invalidate3.使用多线程和双缓冲 Android中SurfaceView是View的子类,她同时也实现了双缓冲。可以定义一个她的子类并实现SurfaceHolder.Callback接口。由于实现SurfaceHolder.Callback接口,新线程就不需要android.os.Handler帮忙了。SurfaceHolder中lockCanvas()方法可以锁定画布,绘制完新的图像后调用unlockCanvasAndPost(canvas)解锁(显示)

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

}

}

2.Invalidate和postinvalidate

Invalidate()必须是在UI线程中被调用,如果在新线程中更新视图的就调用postInvalidate()。

3.view.invalidate 调用过程

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

void invalidate(boolean invalidateCache) {

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

}

}

}

子View调用invalidate时,首先找到自己父View(View的成员变量mParent记录自己的父View),然后将AttachInfo中保存的信息告诉父View刷新自己。

View的父子关系的建立分为两种情况:

1) View加入ViewGroup中

private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) {

.....

// tell our children

if (preventRequestLayout) {

child.assignParent(this);

} else {

child.mParent = this;

}

.....

}

2)DecorView注册给WindowManagerImpl时,产生一个ViewRoot作为其父View。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView){

.....

view.assignParent(this);

....

}

AttachInfo是在View第一次attach到Window时,ViewRoot传给自己的子View的。这个AttachInfo之后,会顺着布局体系一直传递到最底层的View

View.java

void dispatchAttachedToWindow(AttachInfo info, int visibility) {

mAttachInfo = info;

.....

}

ViewGroup.java

void dispatchAttachedToWindow(AttachInfo info, int visibility) {

super.dispatchAttachedToWindow(info, visibility);

for (int i = 0; i < count; i++) {

children[i].dispatchAttachedToWindow(info, visibility);

}

}

并且在新的View被加入ViewGroup时,也会将该AttachInfo传给加入的View

ViewGroup.java

private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) {

child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));

}

到这里明白了mParent与AttachInfo代表的意义,可以继续刷新过程的分析。

在invalidate中,调用父View的invalidateChild,这是一个从第向上回溯的过程,每一层的父View都将自己的显示区域与传入的刷新Rect做交集。

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

scheduleTraversals();

}

相关推荐

YONEX 尤尼克斯 双刃10LCW 李宗伟《败者为王》纪念羽拍 DUO10 DR10 DUORA10
超78万只内地生鲜鸡“飞”上澳门餐桌
Bet体育365提款流水

超78万只内地生鲜鸡“飞”上澳门餐桌

09-21 👁️ 9501
什么属相不能养铁树
Bet体育365提款流水

什么属相不能养铁树

02-02 👁️ 9596
法国队大名单:姆巴佩、登贝莱领衔,拉比奥在列,埃基蒂克落选