澳门新葡萄京娱乐场 8

View优化(一)

Android Studio提供了一个强大的“Preview”工具,可以帮助您预览您的布局文件将如何在用户的设备上呈现。XML布局可能是Android开发中最常用的资源。
您的项目中每个活动至少有一个布局文件。 Android
Studio的预览工具可帮助您实现这些伟大的设计,并快速迭代它们,甚至不需要运行您的应用程序。它还允许您查看布局的不同配置,例如在纵向或横向时的外观,或者TextView在多个语言环境(如英语,德语或希腊语)上的外观。

1,View的相关分析工具
1.1 Hierarchyviewer
Hierarchy
Viewer是随AndroidSDK发布的工具,位置在tools文件夹下,名为hierarchyviewer.bat,双击即可运行。它是Android自带的非常有用而且使用简单的工具,可以帮助我们更好地检视和设计用户界面(UI)。
具体来说主要功能有2个:
1.
从可视化的角度直观地获得UI布局设计结构和各种属性的信息,帮助我们优化布局设计;
2.
结合debug帮助观察特定的UI对象进行invalidate和requestLayout操作的过程。
1.2 显示GPU过渡绘制
可以在手机打开
设置–>开发者选项–>显示GPU过度绘制,这个开关的作用是按
不同颜色值来显示布局的过度绘制,绘制的层次从最优到最差:
蓝–>绿–>淡红–>红:
蓝色 1x过度绘制
绿色 2x过度绘制
淡红色 3x过度绘制
红色 超过4x过度绘制

首先我们要知道Overdraw是什么?
所谓Overdraw,就是过度绘制的意思,是指在一帧的时间内(16.67ms)像素被绘制了多次。理论上一个像素每次只绘制一次是最优的,但是由于重叠的布局导致一些像素会被多次绘制,而每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作,当这个操作耗时超过16.67ms时,就会出现掉帧现象,也就是我们所说的卡顿。所以对重叠不可见元素的重复绘制会产生额外的开销,需要尽量减少Overdraw的发生。

即使预览工具是强大的,可以使你的发展日子轻而易举,与此同时,它也有一些缺点会让程序猿抓狂。这篇文章谈论Preview的常见问题和技巧。

澳门新葡萄京娱乐场 1

Android提供了测量Overdraw的选项,在开发者选项-调试GPU过度绘制(Show GPU
Overdraw),打开选项就可以看到当前页面Overdraw的状态,就可以观察屏幕的绘制状态。该工具会使用三种不同的颜色绘制屏幕,来指示overdraw发生在哪里以及程度如何,其中:

这里是最常见的关于Preview的问题列表和解决方案(图片请右键用新标签放大查看):

图片1.png

没有颜色: 意味着没有overdraw。像素只画了一次。

问题1:Preview看起来为空

假设你有一个布局,其内容将从后端获得的数据填充…你很快意识到,由于内容是动态的,预览工具不能填充屏幕,你什么也看不到。对这个问题的一个较为简单解决方案是在真机上测试,那时你有这些数据,但是这样Preview的意义就失去了。

澳门新葡萄京娱乐场 2

在这种情况下的问题是TextViewImageView没有任何内容可供显示。
这是处理动态内容时的常见问题。即使代码编译没有问题,没有人可以在不查看XML代码的情况下理解该布局。

当创建使用任何后端数据相关视图的布局时,一个好的做法是仅在预览时填充它。通过使用tools命名空间而不是android来声明xml属性,这将允许您指定只在预览时使用的属性。例如我们使用tools:text
=“Title”
tools:src =“@ drawable / cool_pic”,大功告成!

澳门新葡萄京娱乐场 3
使用tools前缀声明的属性完全与android一样,但仅用于预览。使用tools:text
而不是Android:text,保证了你所有的内容都只是在预览的时候会出现,程序运行起来不会出现所有tools相关的东西。

如果你没有足够的符合所有ImageView要求的宽高比的图片(jpg等),会发生什么?
您可以让设计狮提供一些资源来测试各种适配,不过这可能需要一些额外的努力和维护;这时也可以使用tools:rcs或者tools:layout_heighttools:layout_width来测试显示效果,而不必修改真实属性。

为了有更直观的认识,这里有一个简单的布局:

蓝色: 意味着overdraw
1倍。像素绘制了两次。大片的蓝色还是可以接受的(若整个窗口是蓝色的,可以摆脱一层)。

问题2:测试最大宽高

或者当您的布局旨在显示来自外部源的一些内容时,它有时被要求具有一些最大宽度或/和高度(也就是使用了maxHeight属性),来确保您的布局看起来美观,即使外部源发送大于预期或某些宽高比未被同意的图像。这时可以使用tools:layout_heighttools:layout_width,并且设置一个固定颜色的toos:background来预览各个尺寸下的图片可以在ImageView中占用的空间。

澳门新葡萄京娱乐场 4

<RelativeLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
澳门新葡萄京娱乐场,android:layout_width=”match_parent”
android:layout_height=”match_parent” >
<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”400dp”
android:background=”#ffffff”
android:orientation=”vertical” >
<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”300dp”
android:background=”#ffffff”
android:orientation=”vertical” >
<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”200dp”
android:background=”#ffffff”
android:orientation=”horizontal” >
<ImageView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:src=”@drawable/ic_launcher” />
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:text=”简单测试” />
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:background=”#ffffff”
android:text=”简单测试” />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignParentBottom=”true”
android:text=”简单测试” />
</RelativeLayout>

绿色: 意味着overdraw
2倍。像素绘制了三次。中等大小的绿色区域是可以接受的,但你应该尝试优化、减少它们。

问题3:修复损坏的预览

如下图所示的错误经常发生:创建自定义View时,务必确保您的视图可以实例化,而不使用任何在预览期间可能不存在的外部依赖项。请记住,预览不会在应用程序中运行,而是在IDE中的JVM上运行。
这将模拟在Android设备上的工作原理,你应该假设你不能访问任何数量的不在View框架内的依赖。使用例如Glide的图像加载器将是不可能的。
出于同样的原因,任何依赖注入框架将不工作,因为它不会在预览上下文中初始化,导致视图在被扩充时抛出异常。

澳门新葡萄京娱乐场 5

在这种情况下可以使用View.isInEditMode()
使用它来检查您是否是正在预览,并跳过在Preview时不可用的依赖:

public ImageWithCaptionView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (!isInEditMode()) {
            ArticlesApplication.getInjector().inject(this);
        }
}

这个xml文件的视图其实可以很清晰的看出哪些布局过渡绘制。
目标: 我们在布局时候,尽量减少红色Overdraw,看到更多的蓝色区域。
1.3 Android Lint
该工具主要是用来检查工程中代码的不合理,布局不合理,资源重复,图片重复等,让开发者进一步优化自己的应用。详细了解可网上搜索学习使用。一般情况下,我们遇到的Lint问题都需要逐个解决,避免不必要的可能导致的应用崩溃等。
2.View优化:
2.1.View布局优化

浅红: 意味着overdraw 3倍。像素绘制了四次,小范围可以接受。

问题4:merge布局重叠

merge标签可以帮助您减少布局代码的重复。

然而,merge的问题是,它内部的所有组件将被折叠在一起,同时显示在预览里,造成视觉混乱。如下图所示,TextView覆盖在ImageView的上面:

澳门新葡萄京娱乐场 6

您可以使用工具:tools:showIn=”layout”来显示使用它的一些其他现有布局内的布局的内容。
请注意,如果您在多个地方使用不同的父布局,则只能选择一个布局进行预览。

从Android Studio 2.2开始,您现在可以使用工具:parentTag
=“LinearLayout”
例如将渲染布局为LinearLayout。下图是使用后的效果,不再有重叠:

澳门新葡萄京娱乐场 7

  1. 确保每个布局都合适地被使用,认真考虑布局中的每个属性的使用,能简化则简化之。多考虑是否有其他更优的布局方案替换当前的方案。
  2. 能实现同样的布局效果,尽量使用Relativelayout以减少布局文件中View对象的个数,减少内存的占用。某些情况也可考虑LinearLayout.
    如嵌套的LinearLayout较少时,小屏幕手机适配时。
    a)Relativelayout相对LinearLayout的优势:
    i.比较容易实现各种布局样式,相对线性布局可减少View对象创建的个数,
    减少内存的占用
    ii.比较灵活的布局形式
    LinearLayout中如果使用了layout_weight
    属性会使其在measure时比较消耗时间。所以慎用LinearLayout和它的layout_weight属性。
  3. 布局宽度和高度单位使用dip(dp), 不要使用px;
  4. 文字大小单位使用sp;
  5. 内容较多界面使用ScrollView包裹;
  6. 使用
    style主题来定义一个通用的属性,从而重复利用代码,既可以减少代码量,又方便以后项目代码维护。
  7. 将布局扁平化,减少根节点,减少布局嵌套的层次。如使用TextView可以设置四个方向图片来直接替代LinearLayout下包裹一个ImageView
    和TextView,从而减少了一层嵌套布局。如下图微信界面优化后的效果:
    引用自:http://www.2cto.com/kf/201506/412711.html

    澳门新葡萄京娱乐场 8

    图片2.png

  8. 不重复设置属性。嵌套多个子布局时,能在父布局中设置的属性,就不在子布局中单独设置,如多个子布局并列时的padding属性,既有助于提高性能,又减少代码量,方便维护。

  9. 使用一些功能强大的控件优化布局。如V7包中的 LinearLayoutCompat
    组件来实现线性布局元素之间的分割线,从而减少了使用View来实现分割线效果。LinearLayoutCompat的具体内容可参考
    http://blog.csdn.net/feiduclear_up/article/details/46619637
  10. 优化GPU过度绘制:
    i.优化布局,减少层级,减少嵌套
    ii.减少没必要的背景background设置(显示GPU过度绘制中的例子)
    iii.给Activity定义一个Theme,通过theme定义背景可以减少一层

暗红: 意味着overdraw
4倍。像素绘制了五次或者更多。这是错误的,要修复它们。

问题5:在预览时显示隐藏的视图

你的活动可能包含一些在onCreat时需要隐藏的View,但在一些事件后显示它们。通过设置这些视图在布局中的visibility:”GONE”,可以确保它们永远不会在预览时可见。

问题是,这些视图将从预览中消失,如果一些其他开发人员打开布局,并在预览中查找它们,他们将无法找到它。这是一个问题,因为它需要更多的精力和时间来了解屏幕上发生了什么。

您可以使用tools:visibility =“visible”属性,以仅在预览面板中显示它。

那么我们怎么优化overdraw呢?总的原则就是:尽量避免重叠不可见元素的绘制,基于这个原则,我们大概可以想出以下几个方式:

问题6:ListView的Item和Header/Footer预览

使用tools:listitem / tools:listheader /
tools:listfooter
可以实现在预览中增加item、header、footer的效果。例如:

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:listitem="@layout/sample_list_item"
    tools:listheader="@layout/sample_list_header"
    tools:listitem="@layout/sample_list_footer" />

此特性在AS2.2有bug(无效),并在2.3中修复。

1、合理选择控件容器
由于overdraw是因为重复绘制了同一片区域的像素点,那我们首先想到的是解决布局问题。Android提供的Layout控件主要包括LinearLayout、TableLayout、FrameLayout、RelativeLayout。俗话说条条大路通罗马,同一个界面我们可以使用不同的容器控件来表达,但是各个容器控件描述界面的复杂度是不一样的。一般来说LinearLayout最易,RelativeLayout较复杂。但是尺有所短,寸有所长,LinearLayout只能用来描述一个方向上连续排列的控件,而RelativeLayout几乎可以用于描述任意复杂度的界面。但大家需要注意的一点就是,往往表达能力越强的容器控件,性能往往略低一些,因为系统需要将更多的时间花在计算子控件的位置上。综上所述:
LinearLayout易用,效率高,表达能力有限。RelativeLayout复杂,表达能力强,效率低。

那么对于同一界面而言,作为开发者考虑是使用尽量少的、表达能力强的RelativeLayout作为容器,还是选择多个、表达能力稍弱的LinearLayout来展示。从减少overdraw的角度来看,LinearLayout会增加控件数的层级,自然是RelativeLayout更优,但是当某一界面在使用LinearLayout并不会比RelativeLayout带来更多的控件数和控件层级时,LinearLayout则是首选。所以在表达界面的时候,作为一个有前瞻性的开发者要根据实际情况来选择合适容器控件,在保证性能的同时,尽量避免overdraw。

2、去掉window的默认背景
当我们使用了Android自带的一些主题时,window会被默认添加一个纯色的背景,这个背景是被DecorView持有的。当我们的自定义布局时又添加了一张背景图或者设置背景色,那么DecorView的background此时对我们来说是无用的,但是它会产生一次Overdraw,带来绘制性能损耗。去掉window的背景可以在onCreate()中setContentView()之后调用

getWindow().setBackgroundDrawable(null);
或者在theme中添加
android:windowbackground=”null”;

3、去掉其他不必要的背景
有时候为了方便会先给Layout设置一个整体的背景,再给子View设置背景,这里也会造成重叠,如果子View宽度mach_parent,可以看到完全覆盖了Layout的一部分,这里就可以通过分别设置背景来减少重绘。再比如如果采用的是selector的背景,将normal状态的color设置为“@android:color/transparent”,也同样可以解决问题。这里只简单举两个例子,我们在开发过程中的一些习惯性思维定式会带来不经意的Overdraw,所以开发过程中我们为某个View或者ViewGroup设置背景的时候,先思考下是否真的有必要,或者思考下这个背景能不能分段设置在子View上,而不是图方便直接设置在根View上。

4、ClipRect & QuickReject
为了解决Overdraw的问题,Android系统会通过避免绘制那些完全不可见的组件来尽量减少消耗。但是不幸的是,对于那些过于复杂的自定义的View(通常重写了onDraw方法),Android系统无法检测在onDraw里面具体会执行什么操作,系统无法监控并自动优化,也就无法避免Overdraw了。但是我们可以通过canvas.clipRect()来帮助系统识别那些可见的区域。这个方法可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视。这个API可以很好的帮助那些有多组重叠组件的自定义View来控制显示的区域。同时clipRect方法还可以帮助节约CPU与GPU资源,在clipRect区域之外的绘制指令都不会被执行,那些部分内容在矩形区域内的组件,仍然会得到绘制。除了clipRect方法之外,我们还可以使用canvas.quickreject()来判断是否没和某个矩形相交,从而跳过那些非矩形区域内的绘制操作。

5、ViewStub
ViewStub即高效占位符。在Android应用开发中,经常会遇到这样的情况,运行时动态根据条件来决定显示哪个View或布局。常用的做法是把View都写在上面,先把它们的可见性都设为View.GONE,然后在代码中动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是,耗费资源。虽然把View的初始可见View.GONE,但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性,会耗费内存等资源。

建议可以使用android.view.ViewStub,ViewStub是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。可以为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化,然后当ViewStub被设置为可见的时候,或是调用了ViewStub.inflate()的时候,ViewStub所向的布局就会被Inflate和实例化,然后ViewStub的布局属性都会传给它所指向的布局。这样,就可以使用ViewStub来方便的在运行时,要还是不要显示某个布局。

<ViewStub
android:id=”@+id/stub_view”
android:inflatedId=”@+id/panel_stub”
android:layout=”@layout/progress_overlay”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_gravity=”bottom” />

当你想加载布局时,可以使用下面其中一种方法:

((ViewStub)
findViewById(R.id.stub_view)).setVisibility(View.VISIBLE);
View importPanel = ((ViewStub) findViewById(R.id.stub_view)).inflate();

6、Merge标签
Merge标签功能很强悍,可以干掉一个view层级。但是也有一些使用条件的限制。有两种情况下我们可以使用Merge标签来做容器控件。第一种子视图不需要指定任何针对父视图的布局属性,就是说父容器仅仅是个容器,子视图只需要直接添加到父视图上用于显示就行。另外一种是假如需要在LinearLayout里面嵌入一个布局(或者视图),而恰恰这个布局(或者视图)的根节点也是LinearLayout,这样就多了一层没有用的嵌套,无疑这样只会拖慢程序速度。而这个时候如果我们使用merge根标签就可以避免那样的问题。另外Merge只能作为XML布局的根标签使用,当Inflate以<merge
/>开头的布局文件时,必须指定一个父ViewGroup,并且必须设定attachToRoot为true。

下面通过一个例子,来帮助大家理解吧:

<RelativeLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent” >
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”merge标签使用” />
</RelativeLayout>

把上面这个XML加载到页面中,布局层级是RelativeLayout-TextView。但是采用下面的方式,把RelativeLayout提换成merge,RelativeLayout这一层级就被干掉了。

<merge
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent” >
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”merge标签使用” />
</merge>

7、善用draw9patch
给ImageView加一个边框,你肯定遇到过这种需求,通常在ImageView后面设置一张背景图,露出边框便完美解决问题,此时这个ImageView,设置了两层drawable,底下一层仅仅是为了作为图片的边框而已。但是两层drawable的重叠区域去绘制了两次,导致overdraw。优化方案:
将背景drawable制作成draw9patch,并且将和前景重叠的部分设置为透明。由于Android的2D渲染器会优化draw9patch中的透明区域,从而优化了这次overdraw。
但是背景图片必须制作成draw9patch才行,因为Android
2D渲染器只对draw9patch有这个优化,否则,一张普通的Png,就算你把中间的部分设置成透明,也不会减少这次overdraw。

8、慎用Alpha
假如对一个View做Alpha转化,需要先将View绘制出来,然后做Alpha转化,最后将转换后的效果绘制在界面上。通俗点说,做Alpha转化就需要对当前View绘制两遍,可想而知,绘制效率会大打折扣,耗时会翻倍,所以Alpha还是慎用。如果一定做Alpha转化的话,可以采用缓存的方式。

view.setLayerType(LAYER_TYPE_HARDWARE);
doSmoeThing();
view.setLayerType(LAYER_TYPE_NONE);

通过setLayerType方式可以将当前界面缓存在GPU中,这样不需要每次绘制原始界面,但是GPU内存是相当宝贵的,所以用完要马上释放掉。

9、避免“OverDesign”
overdraw会给APP带来不好的体验,overdraw产生的原因无外乎:复杂的Layout层级,重叠的View,重叠的背景这几种。开发人员无节制的View堆砌,究其根本无非是产品无节制的需求设计。有道是“由俭入奢易,由奢入俭难”,很多APP披着过度设计的华丽外衣,却忘了简单易用才是王道的本质,纷繁复杂的设计并不会给用户带来好的体验,反而会让用户有压迫感,产品本身也有可能因此变得卡顿。当然,一切抛开业务谈优化都是空中楼阁,这就需要产品设计也要有一个权衡,在复杂的业务逻辑与简单易用的界面展现中做一个平衡,而不是一味的OverDesign。

发表评论

电子邮件地址不会被公开。 必填项已用*标注