当我们感觉到的流畅画面,需要的画面帧数要到达40帧到60帧每秒。而1帧的时间大约是16.67ms,换句话说,在1000ms的时间内,16.67ms大约就是现实60帧画面的单位时间。在Android系统中,系统是通过VSYNC信号触发对UI的渲染的,如果系统每次渲染的事件都保持在16.67ms之内,那末我们看到的UI界面将是非常的流畅的,这也就需要我们将所有程序的逻辑都保证在16ms以内,如果不能在16ms内完成绘制,那末就将造成丢帧的现象。即当前该重绘的帧被未处理完成的逻辑阻塞,例如1次绘制任务耗时20ms,那末在16ms系统发出的VSYNC信号就没法绘制,该帧就会被抛弃,等待下次信号擦次开始绘制,这就是画面卡顿的缘由。
Android系统提供了检测UI渲染时间的工具,在“开发者选项中”有“GPU显现模式分析”,选择“在屏幕上显示为条形图”,以下所示(本测试机为魅族,其他手机可能略有不同):
每个条形图都包括有3部份,蓝色部份表示丈量绘制Display List的时间,红色代表的是OpenGL渲染Display List所需要的时间,黄色代表的是CPU等待GPU处理的时间,中间的绿色横线代表的是VSYNC时间16ms,需要尽可能将所有条形图都控制在这条绿线之下。
overDraw表示的就是过度绘制,是指在1帧的时间内(16.67ms)像素被绘制了屡次,理论上1个像素每次只绘制1次是最优的,但是由于堆叠的布局致使1些像素会被屡次绘制,而每次绘制都会对应到CPU的1组绘图命令和GPU的1些操作,造成CPU和GPU资源的浪费。在系统默许的绘制Activity的背景,如果再给布局绘制了堆叠的背景,那末默许Activity的背景就是无效的过度绘制。
在我们的“开发者选项”中有这样1个检测工具“调用GPU过度绘制”,激活该功能以后可以通过界面上的色彩来判断overDraw的次数。
这个工具可以帮助我们检测当前区域的绘制次数,从而优化界面绘图层次,尽可能增大蓝色的区域,减少红色的区域。
在Android中,系统对View进行丈量,布局和绘制时,都是通过对View数的遍历来进行操作的。如果1个View树的高度太高,就会严重影响到丈量,布局和绘制的速度,因此,优化布局的第1个方法就会是下降View树的高度,Google也在API文档中建议View树的高度不宜超过10层。在现在的XML文件的根布局中,我们默许RelativeLayout来替换使用LineraLayout作为默许的根布局,其缘由就是通过扁平的RelativeLayout来下降LineraLayout嵌套所产生的布局树的高度,从而提高UI的渲染速度。
在1个利用程序的界面上,为了保持风格的统1,很多界面都会存在共通的UI,比如所说Topbar,Bottombar,Actionbar等等,如果在每个界面上都进行赋值这1段共通的布局代码,不但不利于后期代码的保护,还会增加程序的冗余。这时候候就能够使用include标签来定义1这1个共通的UI。
<?xml version="1.0" encoding="utf⑻"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="0dp"
android:textSize="28sp"
android:text="這是共通的UI界面"
android:gravity="center">
</TextView>
在该共通的布局中,我将layout_width和layout_height设置为0dp,这样就迫使调用者在使用时必须对控件的狂傲进行赋值,否者是没法看见该控件的。
下1步就是如何使用该共通的UI布局了。只需要在使用该共通的UI布局文件中使用include标签的layout属性对这个共通的UI的ID的援用便可。
<?xml version="1.0" encoding="utf⑻"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/commen_ui"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20sp"
android:text="你好你好你好"/>
</LinearLayout>
这时候候如果我们要对布局中的属性进行赋值,就要重新覆盖某1项属性,进行赋值便可。效果以下:
使用ViewStub标签来实现对1个view的援用并且实现延迟加载。ViewStub是1个非常轻量级的组件,它不但不可视,而且大小为0,下面来演示如何使用ViewStub来进行实现延迟加载的目的。
首先创建1个布局,这个布局在初始化加载时是不需要显示的,只有在某些情况下才需要进行显示的,例如查看用户信息的时候,只有点击了某1个按钮时,用户详细信息才显示出来。
当运行程序后,我们发现ViewStub中的布局确切没有显示出来,那末要如何才能重新加载显示的布局呢?
首先要通过findViewById()方法找到组件ViewStub。
mStub = (ViewStub) findViewById(R.id.vs_viewstub);
接下来就有两种方式显示这个view:
mStub.setVisibility(View.VISIBLE);
View inflate = mStub.inflate();
这两种方式都是可以将ViewStub重新进行展开,显示援用的布局,而唯1的区分在于就是inflate()可以返回援用的布局,从而可以通过View.findViewById()方法来找到对应的控件。
View inflate = mStub.inflate();
TextView textview = (TextView) inflate.findViewById(R.id.textview);
textview.setText("我是点击后加载的");
注意:不管只用那种方式,1旦ViewStub被设置可见或是inflate以后,ViewStub就不存在了,不能被反复的inflate,取而代之的就是被inflate的Layout,并将这个Layout的ID重新设置为ViewStub中通过android:inflateId属性所指定的ID,这也就是为何两次点击以后会报错的缘由:以下所示:
Caused by: java.lang.IllegalStateException: ViewStub must have a non-null ViewGroup viewParent
扼要说明View.GONE和ViewStub标签的区分是甚么?共同点都是初始时都不会显示,但是ViewStub标签只会在显示时才会去渲染全部布局,而View.GONE,在初始化布局树的时候就已添加在布局树文件中了,相比之下ViewStub的效力会更高。
以下图所示:
hierarchyviewer.bat是没法在真机上进行使用的,只能在摹拟器上使用或是原生的摹拟器上使用,也就是没有加密的装备上。固然真机上也是可以用的,可以到github上下载1个开源项目View Server,下面在摹拟器上使用hierarchyviewer.bat。
hierarchyviewer.bat位于sdk\tools目录下,直接双击便可启动,以下:
注意:我们在使用hierarchyviewer的时候,1定要是摹拟器是打开的,这样才能在hierarchyviewer中看到我们要显示的布局文件。
下面我们写1个非常冗余的布局进行显示文件,代码以下所示:
<?xml version="1.0" encoding="utf⑻"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="你好啊啊 啊啊啊啊 啊"
android:textColor="#efff0019"
android:textSize="20sp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
这个布局文件是3层LinearLayout嵌套以后里面装了1个button,很明显这些LinearLayout都是冗余的,利用hierarchyviewer可以打开这个布局文件,以下图所示:
通常情况下,我们只关注ID为content的Framlayout的分支,这也是setContentView()设置的内容,可以很明显的看出在layout布局文件中(红色布局),这3层LinearLayout没有任何的分支,说明是冗余嵌套,可以直接去掉的。
当点击其中1个view的时候,可以显示view的绘制情况的,不过第1次点击的时候各种显示的事件都是n/a,需要点击菜单中的Profile Node按钮重新进行计算,才能回去到绘制信息。在系统的右下方会给出不同色彩的小圆点,用来表示绘制的效力,绿黄红分别代表的是好中差的绘制效力。