那些 Android 程序员必会的视图优化策略(1)

android:layout_height=“match_parent”
android:background=“#ffffffff”
android:orientation=“vertical”>


其显示结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

移除控件中不需要的背景.png

可以看到,2个使用了跟父布局同样背景的TextView会导致了一次过度绘制。

那么,我们平时只需要遵循以下两个原则就可以减少次过度绘制:

1.对于子控件,如果其背景颜色跟父布局一致,那么就不用再给子控件添加背景了。
2.如果子控件背景五颜六色,且能够完全覆盖父布局,那么父布局就可以不用添加背景了。

2.3.2 将layout层级扁平化

往往我们在写界面的时候都会使用基本布局来实现,这可能会出现一些性能问题。比如:使用嵌套的LinearLayout可能会导致布局的层次结构变得过深。另外,如果在LinearLayout中使用了layout_weight的话,那么他的每一个子 view都需要测量两次。特别是用在 ListViewGridView 时,他们会被反复测量。

布局嵌套过多的话会导致过度绘制,从而降低性能,因此我们需要将布局的层次结构尽量扁平化。

2.3.2.1 使用Layout Inspector去查看layout的层次结构

之前的Android SDK工具包含了一个名为Hierarchy Viewer的工具,可以在应用运行时分析布局。但是在Android Studio 3.1之后,Hierarchy Viewer就给移除掉了。并且Android的团队表示不再开发Hierarchy Viewer。所以这里就不介绍Hierarchy Viewer

这里使用Android推荐的Layout Inspector来查看layout的层次结构。

在Android Studio中点击Tools > Android > Layout Inspector。然后在出现的 Choose Process 对话框中,选择想要检查的应用进程即可。

Layout Inspector会自动捕获快照,然后会显示以下内容:

  • View Tree:视图在布局中的层次结构。
  • Screenshot:每个视图可视边界的设备屏幕截图。
  • Properties Table:选定视图的布局属性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

layout-inspector.png

通过左侧View Tree即可看到布局中的层次结构。

偷偷提一句,Layout Inspector也可以用来分析别人APP的布局。

2.3.2.2 使用嵌套少的布局

假如要实现以下布局:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

layout-listitem.png

我们可以使用LinearLayoutRelativeLayout来完成。但是LinearLayout相比于RelativeLayout,就多了一层,所以RelativeLayout明显是一个更优的选择。如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

过度嵌套LinearLayout.png

所以,合理选择不同的布局能够减少嵌套。

2.3.2.3 使用merge标签减少嵌套

通过<include>标签能够复用布局。

比如,我们要复用如下的一个布局,一个垂直的线性布局包含一个ImageViewTextView,其布局文件layout_include.xml如下:

<ImageView

/>

<TextView

/>

然后我们就可以通过<include>来复用这个布局了,其布局文件activity_include.xml如下:

但是上面这个例子会有个问题:其父布局是垂直的线性布局,include进来的也是垂直的线性布局,这就会造成了布局嵌套,而且这种嵌套是没必要的,那么就可以使用<merge>标签来减少这种嵌套。将layout_include.xml改成以下即可:

<ImageView

/>

<TextView

/>

我们可以用Layout Inspector来看下使用<merge>标签优化前后的布局层次结构:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用merge标签减少嵌套.png

2.3.2.4 使用lint来优化布局的层次结构

lint是一个静态代码分析工具,可以用来协助优化布局的性能。要使用lint,点击Analyze> Inspect Code即可,如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

lint-inspect-code.png

布局性能方面的信息位于Android> Lint> Performance下,我们可以点开它来看下一些优化建议。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

lint-display.png

下面是lint的一些优化技巧:

  1. 使用复合图片
    如果一个线性布局中包含一个 ImageView 和一个 TextView,可以使用复合图片来替换掉

  2. 合并根节点
    如果一个FrameLayout 是整个布局的根节点,并且也没有提供背景、留白等等,那么可以使用<merge>标签来替换掉,因为DecorView本身就是一个FrameLayout

  3. 移除布局中无用的叶子
    布局是一个树形的结构,如果一个布局没有子 View 或者背景,那么可以把它移除掉(这布局本身就不可见了)。

  4. 移除无用的父布局
    如果一个布局没有兄弟,也不是ScrollView 或者根 View,并且也没有背景,那么可以把这个父布局移除掉,然后把它的子view移到它的父布局下。

  5. 避免过深的层次结构
    过多的布局嵌套不利于性能,可以使用更扁平化的布局,如RelativeLayoutGridLayoutConstraintLayout等布局来提高性能。布局默认的最大深度为10。

lint的功能其实很强大,可以用来检测优化各个方面,平时我们遇到lint的一些警告,能修复优化的话就尽量去完善掉。

2.3.3 减少透明度的使用

对于不透明的view,只需要渲染一次即可把它显示出来。但是如果这个view设置了alpha值,则至少需要渲染两次。这是因为使用了alphaview需要先知道混合view的下一层元素是什么,然后再结合上层的view进行Blend混色处理。透明动画、淡入淡出和阴影等效果都涉及到某种透明度,这就会造成了过度绘制。可以通过减少渲染这些透明对象来改善过度绘制。比如:在TextView上设置带透明度alpha值的黑色文本可以实现灰色的效果。但是,直接通过设置灰色的话能够获得更好的性能。

2.3.4 减少自定义View的过度绘制,使用clipRect()

下面我们自定义一个View用来显示多张重叠的表情包,效果图如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自定义view_1.png

onDraw()方法也很简单,就是遍历所有表情包,然后绘制出来:

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

for (int i = 0; i < imgs.length; i++) {
canvas.drawBitmap(imgs[i], i * 100, 0, mPaint);
}
}

显示过度绘制区域:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自定义view_2.png

五颜六色的,过度绘制比较严重,那么如何解决?

我们先来分析一下为什么会出现过度绘制:以第一张图为例,上面的代码会把整张图都绘制出来了,第二张在第一张上面继续绘制,这就造成了过度绘制。

那么,解决办法也很简单,对于前面的n-1张图,我们只需要绘制一部分即可,对于最后一张才绘制完整的。

Canvas中的clipRect()方法能够设置一个裁剪矩形,只在这个矩形区域内的内容才能够绘制出来。

优化后的代码如下:

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

for (int i = 0; i < imgs.length; i++) {
canvas.save();
if (i < imgs.length - 1) {
//前面的n-1张图,只裁剪一部分
canvas.clipRect(i * 100, 0, (i + 1) * 100, imgs[i].getHeight());
} else if (i == imgs.length - 1) {
//最后一张,完整的
canvas.clipRect(i * 100, 0, i * 100 + imgs[i].getWidth(), imgs[i].getHeight());
}
canvas.drawBitmap(imgs[i], i * 100, 0, mPaint);
canvas.restore();
}

优化后的效果图如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自定义view_3.png

所有区域都是蓝色的,即只有1次过度绘制。

Canvas除了clipRect()方法外,还有clipPath()等方法,优化时选择合理的方法去裁剪即可。

3.一些布局优化技巧

除了避免过度绘制之外,还有一些其他的优化技巧能够帮我们提升性能。这里简单介绍一下一些比较常用的技巧。

3.1 使用性能更优的布局

  1. 在无嵌套布局的情况下,FrameLayoutLinearLayout的性能比RelativeLayout更好。因为RelativeLayout会测量每个子节点两次。
  2. ConstraintLayout的性能比RelativeLayout更好,推荐使用ConstraintLayout。后面会介绍ConstraintLayout的使用。

3.2 使用include标签提高布局的复用性

使用<include>标签提取布局的公用部分,能够提高布局的复用性。具体例子这里就不写了,可以回头看看<merge>标签那一小节的例子。

3.3 使用ViewStub标签延迟加载

在项目中,有些复杂的布局很少使用到,比如进度指示器等等。那么我们可以通过<ViewStub>标签来实现在需要时才加载布局。使用<ViewStub>能够减少内存的使用并且加快渲染速度。

ViewStub是一个轻量级的视图,它没有尺寸,也不会绘制任何内容和参与布局。下面是一个ViewStub的例子:

这里的panel_import就是具体要加载的布局ID。

通过以下代码即可在需要时加载布局:

findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
或者
View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();

一旦布局加载后,ViewStub就不再是原来布局的一部分了,它会被新加载进来的布局替换掉。需要注意的是,ViewStub不支持<merge>标签。

架构师筑基包括哪些内容

我花了将近半个月时间将:深入 Java 泛型.、注解深入浅出、并发编程.、数据传输与序列化、Java 虚拟机原理、反射与类加载、高效 IO、Kotlin项目实战等等Android架构师筑基必备技能整合成了一套系统知识笔记PDF,相信看完这份文档,你将会对这些Android架构师筑基必备技能有着更深入、更系统的理解。

由于文档内容过多,为了避免影响到大家的阅读体验,在此只以截图展示部分内容

注:资料与上面思维导图一起看会更容易学习哦!每个点每个细节分支,都有对应的目录内容与知识点!



这份资料就包含了所有Android初级架构师所需的所有知识!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
由于文档内容过多,为了避免影响到大家的阅读体验,在此只以截图展示部分内容

注:资料与上面思维导图一起看会更容易学习哦!每个点每个细节分支,都有对应的目录内容与知识点!

[外链图片转存中…(img-Jp54uVkL-1715418444471)]
[外链图片转存中…(img-7Hgo84Km-1715418444472)]
这份资料就包含了所有Android初级架构师所需的所有知识!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值