java invalidate_view.invalidate()原理

加载View的onDraw()方法的时机以及invalidate()方法的作用。

事实上,远远没有您想象的那么简单。为了写好这篇博客,还是拿例子说事吧。

[java] view plaincopyprint?

package mark.zhang;

import android.app.Activity;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.RectF;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

public class ViewDrawTestActivity extends Activity {

// 用于测试

static int times = 1;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

MyView mView = new MyView(this);

mView.invalidate();

//setContentView(mView);

}

/**

* 内部类,继承View

*

* @author mark

*/

class MyView extends View {

MyView(Context context) {

super(context);

}

Paint vPaint = new Paint(); // 绘制样式物件

int i = 0; // 弧形角度

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

Log.d("mark", "this run onDraw() " + (times++) + " times!");

// 设定绘图样式

vPaint.setColor(0xff00ffff); // 画笔颜色

vPaint.setAntiAlias(true); // 反锯齿

vPaint.setStyle(Paint.Style.STROKE);

// 绘制一个弧形

canvas.drawArc(new RectF(60, 120, 260, 320), 0, i, true, vPaint);

// 弧形角度

if ((i += 10) > 360) {

i = 0;

}

// 重绘, 再一次执行onDraw 程序

// invalidate();

}

}

}

例子没有多大的变化,只是在onCreate()方法中直接调用invalidate()方法,如:

[java] view plaincopyprint?

mView.invalidate();

这样做的目的主要是想看看,自己调用View的invalidate()方法会不会触发onDraw()方法。运行一下:

呵呵,onDraw()方法并没有执行!那么是不是因为没有调用setContentVIew()方法呢?修改onCreate()方法:

[java] view plaincopyprint?

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

MyView mView = new MyView(this);

mView.invalidate();

setContentView(mView);

mView.invalidate();

}

再次运行,效果:

[html] view plaincopyprint?

D/mark ( 251): this run onDraw() 1 times!

说明,只有setContentVIew()方法中的invalidate()方法启了作用,自己调用View的invalidate()方法,mView.invalidate()没启任何作用。但是,在MyView的onDraw()方法中调用invalidate()方法可以循环调用onDraw()方法,类似递归。

分析一下,invalidate()方法的源码吧,在这里也许可以找到答案。

[java] view plaincopyprint?

/**

* Invalidate the whole view. If the view is visible, {@link #onDraw} 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() {

if (ViewDebug.TRACE_HIERARCHY) {

ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);

}

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

mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;

final ViewParent p = mParent;

final AttachInfo ai = mAttachInfo;

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

}

}

}

这里可以看到p.invalidateChild(this, r)(看源码只看关键部分,不然你会很晕!),其中p是ViewParent实例对象。ViewParent是一个接口,现在我们关心谁实现了这个接口?

通过千辛万苦的search,终于找到ViewParen的实现类ViewRoot:

[java] view plaincopyprint?

/**

* The top of a view hierarchy, implementing the needed protocol between View

* and the WindowManager. This is for the most part an internal implementation

* detail of {@link WindowManagerImpl}.

*

* {@hide}

*/

@SuppressWarnings({"EmptyCatchBlock"})

public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { }

那么,看看该类实现的invalidateChild()方法:

[java] view plaincopyprint?

public void invalidateChild(View child, Rect dirty) {

checkThread();

if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);

if (mCurScrollY != 0 || mTranslator != null) {

mTempRect.set(dirty);

dirty = mTempRect;

if (mCurScrollY != 0) {

dirty.offset(0, -mCurScrollY);

}

if (mTranslator != null) {

mTranslator.translateRectInAppWindowToScreen(dirty);

}

if (mAttachInfo.mScalingRequired) {

dirty.inset(-1, -1);

}

}

mDirty.union(dirty);

if (!mWillDrawSoon) {

scheduleTraversals();

}

}

关键代码在这儿:

[java] view plaincopyprint?

if (!mWillDrawSoon) {

scheduleTraversals();

}

这个方法是向Handler发送消息:

[java] view plaincopyprint?

public void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

sendEmptyMessage(DO_TRAVERSAL);

}

}

接下来,看看ViewRoot的Handler的handleMessage的实现:

[java] view plaincopyprint?

public void handleMessage(Message msg) {

switch (msg.what) {

// 、、、

case DO_TRAVERSAL:

// 、、、

performTraversals();

}

}

performTraversals()方法,调用ViewRoot的私有方法private void draw(boolean fullRedrawNeeded),在该方法中有句代码很关键:

[java] view plaincopyprint?

mView.draw(canvas);

其实这句代码,就是调用View的draw()方法 ,关键代码:

[java] view plaincopyprint?

if (!dirtyOpaque) onDraw(canvas);

也就是说,满足这个方法,就会回调onDraw()方法。到此为止,您应该明白,当我们自己调用invalidate()方法时,想使onDraw()方法回调,必须满足条件。

总结:子view会通过父view启动刷新方法,调用子view的onDraw。

E/AndroidRuntime: FATAL EXCEPTION: Thread-3 Process: com.example.zfang.course2_1, PID: 31755 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6855) at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1075) at android.view.ViewGroup.invalidateChild(ViewGroup.java:5242) at android.view.View.invalidateInternal(View.java:13574) at android.view.View.invalidate(View.java:13538) at android.view.View.invalidate(View.java:13522) at android.widget.TextView.checkForRelayout(TextView.java:7354) at android.widget.TextView.setText(TextView.java:4479) at android.widget.TextView.setText(TextView.java:4336) at android.widget.TextView.setText(TextView.java:4311) at com.example.zfang.course2_1.fragment.MineFragment$1$1.showProgress(MineFragment.java:89) at com.example.zfang.course2_1.service.DownloadService$1.run(DownloadService.java:86) at java.lang.Thread.run(Thread.java:761) D/EGL_emulation: eglMakeCurrent: 0xa9d850c0: ver 2 0 (tinfo 0xa9d831d0) D/OpenGLRenderer: endAllActiveAnimators on 0x8c994500 (RippleDrawable) with handle 0xa9dff550 W/System.err: java.net.SocketTimeoutException: connect timed out W/System.err: at java.net.PlainSocketImpl.socketConnect(Native Method) W/System.err: at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:334) W/System.err: at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:196) W/System.err: at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:178) W/System.err: at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:356) W/System.err: at java.net.Socket.connect(Socket.java:586) W/System.err: at com.android.okhttp.internal.Platform.connectSocket(Platform.java:113) W/System.err:
最新发布
06-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值