天天看点

Android -- View

setContentView                                                                       

只要你使用过Activity,那么你一定使用过setContentView这个方法。一般都是这样调用该方法:

然后,在手机或者模拟器上就可以看见自己的布局。

如果,你留意的话,setContentView还有很多过载方法:

Android -- View
Android -- View

那么,getWindow()方法是做什么的呢?一探究竟:

可以看出,该方法返回一个Window实例。但是Window是一个抽象类啊,怎么可以有实例对象???原来,Window类有一个子类PhoneWindow,那么如何得知getWindow返回的是PhoneWindow实例呢?来,看下面这张图:

Android -- View

至此,您应该明白setContentView()方法是调用PhoneWindow类的同名方法。源码如下:

Android -- View
Android -- View

每个Activity都会实例化一个Window并且只有一个,而View就像是贴在Window上的装饰品。窗户(Window)只有一个,但是窗花(View)可以有很多。

LayoutInflater                                                                        

获得 LayoutInflater 实例

Android -- View
Android -- View

对于第一种,主要是调用 Activity 的 getLayoutInflater() 方法。

继续跟踪研究 android 源码,Activity 中的该方法是调用 PhoneWindow 的 getLayoutInflater()方法!

可以看出它其实是调用 LayoutInflater.from(context), 那么该方法其实是调用第二种方法,看看源码,如下:

Android -- View
Android -- View

inflate 方法

inflate 原意是充气之类的,在这里主要意思就是,扩张、使之膨胀。换句话说就是将当前视图view补充完整、扩展该视图。

Android -- View
Android -- View

示例代码:

Android -- View
Android -- View

对于上面代码,指定了第二个参数 ViewGroup root,当然你也可以设置为 null 值。

注意:该方法与 findViewById 方法不同。

inflater 是用来找 layout 下 xml 布局文件,并且实例化!而 findViewById() 是找具体 xml 下的具体 widget 控件(如: Button,TextView 等)。

postInvalidate()   (参考)                                                      

在子线程中控制UI:

Android -- View
Android -- View

postInvalidate() 方法,源码:

Android -- View
Android -- View

其实,是调用了 Handler 的处理消息的机制!该方法可以在子线程中直接用来更新UI。但是在 Button 的事件中开启线程,更新 UI就会报错报异常。

Handler 和 invalidate 方法结合多线程更新 UI                              

方法 invalidate 主要用在主线程中(即UI 线程中),不可以用于子线程。如果在子线程中需要使用 postInvalidate 方法。

Android -- View
Android -- View

invalidate 方法如果你直接在主线程中调用,是看不到任何更新的。需要与Handler结合!

Android 在 onDraw 事件处理绘图,而 invalidate() 函数可以再一次触发 onDraw 事件,然后再一次进行绘图动作。

Android -- View
Android -- View

经过测试,发现 times 一直在++,说明 onDraw 被多次调用,并且一直在画图!

注释掉的话:

Android -- View

可以看出,图像只画了一条线,说明onDraw()方法被调用一次。从log上也可以看出来:

那么,是什么力量促使onDraw()方法被调用呢?

setContentView()View view方法,其实是调用PhoneWindow的setContentView(View view)方法,调用关系如下:

Android -- View

从而可以看出,invalidate()方法是促使onDraw()方法被调用的力量。

那么,修改代码,将内部类MyView的onDraw()方法中的invalidate()注释取消,再看看运行效果:

Android -- View
Android -- View

控制台:

Android -- View
Android -- View

可以看出,invalidate()方法使onDraw()一直被调用,实现重绘的效果。

在invalidate()方法源码中,有这么一段注释:

这段话,说明了上面的实现(调用onDraw()方法)。但是在子线程中必须使用postInvalidate()方法。

invalidate()源码分析                                                                

Android -- View
Android -- View

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

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

Android -- View

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

Android -- View
Android -- View

再次运行,效果:

Android -- View

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

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

Android -- View
Android -- View

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

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

Android -- View
Android -- View

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

Android -- View
Android -- View

关键代码在这儿:

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

Android -- View
Android -- View

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

Android -- View
Android -- View

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

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

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

调用关系,请看草图!

Android -- View
Android -- View

我是天王盖地虎的分割线                                    

<b></b>

<b>本文转自我爱物联网博客园博客,原文链接:http://www.cnblogs.com/yydcdut/p/3851955.html,如需转载请自行联系原作者</b>

继续阅读