Android渲染优化解析

本文已同步发表到个人技术微信公众号,扫一扫文章底部的二维码或在微信搜索 “程序员驿站”便可关注,不按期更新优质技术文章。同时,也欢迎加入QQ技术群(群号:650306310)一块儿交流学习!html

这篇文章是继“Android电量优化全解析”与“Android内存优化全解析”以后关于Android性能优化的第三篇原创文章,主要讲解了Android渲染优化相关知识点,但愿对你们有所帮助。今天我讲述的内容按照下面的结构进行。java

卡顿现象

渲染功能是应用程序最广泛的功能,开发任何应用程序都是这样,一方面,设计师要求为用户展示可用性最高的超然体验,另外一方面,那些华丽的图片和动画,并非在全部的设备上都能流畅地运行。咱们先来了解一下什么是渲染性能。android

首先,咱们要知道Android系统每隔16ms就从新绘制一次Activity,也就是说,咱们的应用必须在16ms内完成屏幕刷新的所有逻辑操做,这样才能达到每秒60帧,然而这个每秒帧数的参数由手机硬件所决定,如今大多数手机屏幕刷新率是60赫兹(赫兹是国际单位制中频率的单位,它是每秒中的周期性变更重复次数的计量),也就是说咱们有16ms(1000ms/60次=16.66ms)的时间去完成每帧的绘制逻辑操做,若是错过了,好比说咱们花费34ms才完成计算,那么就会出现咱们称之为丢帧的状况。程序员

安卓系统尝试在屏幕上绘制新的一帧,可是这一帧还没准备好,因此画面就不会刷新。若是用户盯着同一张图看了32ms而不是16ms,用户会很容易察觉出卡顿感,哪怕仅仅出现一次掉帧,用户都会发现动画不是很顺畅,若是出现屡次掉帧,用户就会开始抱怨卡顿,若是此时用户正在和系统进行交互操做,例如滑动列表或者输入数据,那么卡顿感就会更加明显,用户会绝不留情地对咱们的应用进行吐槽,如今咱们对绘制每帧花费的时间有了更清晰的了解,再来看看是什么缘由致使了卡顿,如何去解决应用中的这些问题。express

渲染管线

Android系统的渲染管线分为两个关键组件:CPU和GPU,它们共同工做,在屏幕上绘制图片,每一个组件都有自身定义的特定流程。咱们必须遵照这些特定的操做规则才能达到效果。apache

在CPU方面,最多见的性能问题是没必要要的布局和失效,这些内容必须在视图层次结构中进行测量、清除并从新建立,引起这种问题一般有两个缘由:一是重建显示列表的次数太多,二是花费太多时间做废视图层次并进行没必要要的重绘,这两个缘由在更新显示列表或者其余缓存GPU资源时致使CPU工做过分。缓存

在GPU方面,最多见的问题是咱们所说的过分绘制(overdraw),一般是在像素着色过程当中,经过其余工具进行后期着色时浪费了GPU处理时间。性能优化

接下来咱们将讲解更多关于失效布局和重绘的内容,以及如何使用SDK中的工具找出拖累应用性能的缘由bash

CPU和 GPU

想要开发一款性能优越的应用,咱们必须了解底层是如何运行的。有一个主要问题就是,Activity是如何绘制到屏幕上的?那些复杂的XML布局文件和标记语言,是如何转化成用户能看懂的图像的?微信

实际上,这是由栅格化操做来完成的,栅格化就是将例如字符串、按钮、路径或者形状的一些高级对象,拆分到不一样的像素上在屏幕上进行显示,栅格化是一个很是费时的操做。咱们全部人的手机里面都有一块特殊硬件,它就是图像处理器(GPU显卡的处理器),目的就是加快栅格化的操做,GPU在上个世纪90年代被引入用来帮助加快栅格化操做。

GPU使用一些指定的基础指令集,主要是多边形和纹理,也就是图片,CPU在屏幕上绘制图像前会向GPU输入这些指令,这一过程一般使用的API就是Android的OpenGL ES,这就是说,在屏幕上绘制UI对象时不管是按钮、路径或者复选框,都须要在CPU中首先转换为多边形或者纹理,而后再传递给GPU进行格栅化。

咱们要知道,一个UI对象转换为一系列多边形和纹理的过程确定至关耗时,从CPU上传处理数据到GPU一样也很耗时。因此很明显,咱们须要尽可能减小对象转换的次数,以及上传数据的次数,幸好,OpenGL ES API容许数据上传到GPU后能够对数据进行保存,当咱们下次绘制一个按钮时,只须要在GPU存储器里引用它,而后告诉OpenGL如何绘制就能够了,一条经验之谈:渲染性能的优化就是尽量地上传数据到GPU,而后尽量长地在不修改的状况下保存数据,由于每次上传资源到GPU时,咱们都会浪费宝贵的处理时间,Android系统的Honeycomb版本发布以后,整个UI渲染系统就在GPU中运行,以后各个版本都在渲染系统性能方面有更多改进。

Android系统在下降、从新利用GPU资源方面作了不少工做,这方面彻底不用担忧,举例说,任何咱们的主题所提供的资源,例如Bitmaps、Drawables等都是一块儿打包到统一的纹理当中,而后使用网格工具上传到GPU,例如Nine Patches等,这样每次我须要绘制这些资源时,咱们就不用作任何转换,他们已经存储在GPU中了,大大加快了这些视图类型的显示。然而随着UI对象的不断升级,渲染流程也变得愈来愈复杂,例如说绘制图像,就是把图片上传到CPU存储器,而后传递到GPU中进行渲染。路径使用时彻底另一码事,咱们须要在CPU中建立一系列的多边形,甚至在GPU中建立掩蔽纹理来定义路径。绘制字符就更加复杂一些,首先咱们须要在CPU中把字符绘制制成图像,而后把图像上传到GPU进行渲染再返回到CPU,在屏幕上为字符串的每一个字符绘制一个正方形。

如今Android系统已经解决了大多数性能问题,除非咱们还有更高要求,咱们基本不会发现与GPU相关的问题,而后还有一个GPU性能问题瓶颈,这个问题困扰着每一个程序员,这就是过分绘制。

GPU的主要问题 -过分绘制(overdraw)

若是咱们曾经粉刷过房子,咱们应该知道,给墙壁粉刷工做量很是大,若是咱们须要从新粉刷,第一次的粉刷就白干了。一样的道理,咱们的应用程序会由于过分绘制,从而致使性能问题,若是咱们想兼顾高性能和完美的设计,每每会碰到一种性能问题,即过分绘制。过分绘制是一个术语,指的是屏幕上的某个像素点在同一帧的时间内被绘制了屡次。假如咱们有一堆重叠的UI卡片,最接近用户的卡片在最上面,其他卡片都藏在下面,也就是说咱们花大力气绘制的那些下面的卡片基本都是不可见的。

问题就在于此,由于每次像素通过渲染后,并非用户最后看到的部分,这就是在浪费GPU的时间。目前流行的一些布局是一把双刃剑,带给咱们漂亮视觉感觉的同时,也形成过分绘制的问题,为了最大限度地提升应用程序的性能,咱们必须尽可能减小过分绘制。幸运的是,Android手机提供了查看过分绘制状况的工具,在开发者选项中打开"Show GPU overdraw"选项,手机屏幕显示会出现一些异常不用过于惊慌,Android在屏幕上使用不一样颜色,标记过分绘制的区域,若是某个像素点只渲染了一次,咱们看到的是它原来的颜色,随着过分绘制的增多,标记颜色也会逐渐加深,例如1倍过分绘制会被标记为蓝色,2倍、3倍、4倍过分绘制遵循一样的模式。因此当咱们调试应用程序的用户界面时,目标就是尽量的减小过分绘制,将红色区块转变成蓝色区块,为了完成目标有两种清楚过分绘制的方法,首先要从视图中清楚那些,没必要要的背景和图片,他们不会在最终渲染图像中显示,记住,这些都会影响性能。其次,对视图中重叠的屏幕区域进行定义,从而下降CPU和GPU的消耗,接下来咱们深刻了解过分绘制。

可视化方式解决过分绘制

下面以一个列表界面作为例子讲解

如今咱们看到了示例代码的应用程序,如今就想象咱们本身开发了一款聊天应用,咱们想了解应用程序在过分绘制性能上的表现如何,首先要作的就是搜集信息,在这一步咱们须要打开手机上的GPU过分绘制调试。

看看这些过分绘制的地方,咱们须要减小这些过分绘制,尤为是红色区域,这里说明一下各个颜色表明的意思。

如今深刻了解一下UI是如何建立的,看看可否作一些清理,减小过分绘制,办法一就是清除没必要要的背景和图片。例如咱们想把Chatum背景中的这块区域变成绿色或者2倍过分绘制区域,为何能实现这个效果呢?这是因为Chatum的BaseActivity采用了不透明白色背景的布局填充整个屏幕,咱们喜欢这样,可是却与Android的材料主题默认设置相冲突,特别是窗口背景图片,这些都致使了没必要要的过分绘制,做为一个开发者咱们必须作一个决定,咱们但愿保留白色背景,材料主题其实没有任何意义,咱们能作的一个优化就是把Activity的背景图片设置为null,我向你们展现一下在代码中如何实现,打开Chatum的BaseActivity咱们看一下onCreate方法,使用下列声明取消原来的背景,就是这样,经过取消背景咱们将过分绘制区域的颜色,由绿色变成了蓝色,变成了1倍过分绘制。

优化后:

咱们看一下XML标记文件看看可否再作一些调整,咱们可能已经注意到有三个XML文件,指定了Chatum的用户界面,有Chatum Latinum的BaseActivity,XML聊天片断还有聊天记录的单个XML,前面已经提过,咱们想在这里保留白色背景,在这里咱们什么都不作,可是其余两个XML文件可否作一些调整.

activity_chatum_latinum

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
    android:id="@+id/activity_chatum_latinum_container"
    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"
    android:background="@android:color/white"
    tools:context=".MainActivity"
    tools:ignore="MergeRootFrame"/>
复制代码

fragment_chats.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout 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"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:background="@android:color/white"
    tools:context=".MainActivity$ChatListFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/narrow_space"
        android:textSize="@dimen/large_text_size"
        android:layout_marginBottom="@dimen/wide_space"
        android:text="@string/header_text" />

    <ListView
        android:id="@+id/listview_chats"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="@android:color/transparent"
        android:dividerHeight="@dimen/divider_height" />
</LinearLayout>
复制代码

chat_item.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:paddingBottom="@dimen/chat_padding_bottom">

    <ImageView
        android:id="@+id/chat_author_avatar"
        android:layout_width="@dimen/avatar_dimen"
        android:layout_height="@dimen/avatar_dimen"
        android:layout_margin="@dimen/avatar_layout_margin" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:orientation="vertical">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#78A"
            android:background="@android:color/white"
            android:orientation="horizontal">

            <TextView xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:padding="@dimen/narrow_space"
                android:gravity="bottom"
                android:id="@+id/chat_author_name" />

            <TextView xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:textStyle="italic"
                android:padding="@dimen/narrow_space"
                android:id="@+id/chat_datetime" />
        </RelativeLayout>

        <TextView xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="@dimen/narrow_space"
            android:background="@android:color/white"
            android:id="@+id/chat_text" />
    </LinearLayout>
</LinearLayout>
复制代码

在剩下的文件中应该能够找到4个没必要要的背景,咱们来查看一下,在BaseActivity中记住咱们但愿保留白色背景,如今咱们来看聊天片断XML文件咱们在这里声明了一个没必要要的白色背景,咱们并不须要这个声明由于可使用BaseActivity的白色背景(fragment_chats.xml)。

如今来看聊天记录单个XML文件(chat_item.xml),这里有三个没必要要的背景,咱们来删除它们。

好了,如今咱们来看看,过分绘制的状况有没有改善,咱们的屏幕就应该如今这样,恰当的删除这些背景,干净多了,对不对?

很快就快大功告成了,可是咱们还能够再作一个优化,注意头像区域仍存在过分绘制,由于咱们绘制了一个方框而后再绘制头像图片,在没有获取到头像时咱们才设置一个背景,咱们可使用一些条件码来实现,咱们打开ChatAdapter.java,

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.mobileperf.render;

import android.content.Context;
import android.graphics.Color;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import java.util.ArrayList;
import java.util.Date;

/**
 * A custom adapter that is backed by an array of Chat objects. References a TextView with the name
 * of a chat author (a Droid), a TextView with a chat's text, another TextView with the chat's
 * timestamp, and an ImageView for the chat author's avatar. */ public class ChatAdapter extends ArrayAdapter<Chat> { public ChatAdapter(Context context, ArrayList<Chat> chats) { super(context, 0, chats); } @Override public View getView(int position, View view, ViewGroup parent) { Chat chat = getItem(position); if (view == null) { view = LayoutInflater.from(getContext()).inflate( R.layout.chat_item, parent, false); } // Find the UI widgets for a chat item. TextView chat_author_name = (TextView) view.findViewById(R.id.chat_author_name); TextView chat_text = (TextView) view.findViewById(R.id.chat_text); TextView chat_datetime = (TextView) view.findViewById(R.id.chat_datetime); ImageView chat_author_avatar = (ImageView) view.findViewById(R.id.chat_author_avatar); // Display the author's name using the color associated with the author.
        chat_author_name.setText(chat.getAuthor().getName());
        chat_author_name.setTextColor(chat.getAuthor().getColor());

        // Display the chat text.
        chat_text.setText(chat.getText());

        // Set the timestamp for the chat in "x minutes ago" format.
        chat_datetime.setText(DateUtils.getRelativeTimeSpanString(
                chat.getDatetime().getTime(),
                new Date().getTime(),
                DateUtils.MINUTE_IN_MILLIS,
                DateUtils.FORMAT_ABBREV_RELATIVE));


        // Display the chat author's avatar (a droid image) and a background color associated with // the author. if (chat.getAuthor().getAvatarId() != 0) { Picasso.with(getContext()).load(chat.getAuthor().getAvatarId()).into( chat_author_avatar); } chat_author_avatar.setBackgroundColor(chat.getAuthor().getColor()); return view; } } 复制代码

这部分代码负责在我的聊天记录上传后进行填写,咱们找到了getview方法,在底部这里咱们找到一个逻辑操做,用来显示头像的同时设置背景颜色,咱们来看看可否变得更智能一些,咱们来写一段代码,在未获取到头像时仅用来设置背景颜色,而后咱们将把背景颜色设置为透明,而后上传头像,好了,这就是咱们更新后的代码。

修改前:

修改后:

注意,当为获取到头像时咱们要作的是,在头像一般的位置加载透明色,而后,为头像设置真的背景色,剩下的就是获取到头像时的操做,咱们恰当的加载头像,而后,咱们将背景色设置为透明,这样咱们就能将过分绘制最小化。好了,咱们来看看状况有没有改善。

很好,咱们能够看到头像区域,在更新代码后过分绘制减小了,好了,这就是咱们最后的优化。

咱们总结下,优化前过分绘制很是严重,首先要作的,就是将背景图片设置为null,其次,就是清除XML文件中,没必要要的背景声明,最后,咱们只在为获取到头像时,显示背景颜色。通过这些优化,咱们再来看看,过分绘制的状况相比开始有了很大改善。

注意: 有些过分绘制对于运行性能,多是必要的也是能够接受的,好比说Android的ActionBar,可是,若是咱们但愿应用体验更进一步,咱们能够考虑尽量地减小过分绘制。

clipRect和quickReject

值得指出的是,Android系统知道过分绘制是个麻烦,Android会设法避免绘制,那些在最终图片中不显示的UI组件,这种优化类型,称做剪辑,它对UI性能很是重要。若是咱们能肯定某个对象会被彻底阻挡,那就彻底没有必要绘制它,事实上,这是最重要的性能优化方法之一,并且是有Android系统执行的,可是不幸的是,这一技术没法应对复杂的自定义的View,系统没法检测onDraw具体会执行什么操做。这些状况下,底层系统没法识别如何去绘制对象,系统很难将覆盖的View,从渲染管道中清除。例如,这叠牌只有最上面的牌是彻底可见的,其余牌都被挡住了,这就意味着绘制那些重叠的像素就是浪费时间。

为了解决这个问题,咱们可使用Canvas类的一些特别方法去帮助Android系统识别被遮挡的不须要绘制的部分,最有用的办法是Canvas.clipRect,它能够帮助咱们识别给定View的图片边界,边界以外区域的任何绘制操做会被忽视,若是碰到此类重叠的View,这个方法特别好用,就像例子中的纸牌。若是咱们知道自定义View可见部分的范围,或者知道遮挡部分的范围,咱们就能够定义ClipRect边界,能够避免遮挡区域的任何绘制操做,ClipRect API帮助系统识别出无需绘制的区域,对自定义View进行剪辑时,这个方法也颇有用处。好比说,若是咱们知道绘制对象在剪辑矩形以外,这个方法就很是好用,幸运的是,咱们没必要亲自搞清楚重叠逻辑,咱们可使用Canvas.quickReject方法,断定给定区域是否彻底在剪辑矩形以外,这种状况下能够忽略所有绘制工做。如今咱们来看一个相关案例,咱们对它作一些改进。

布局优化

是时候来了解一下渲染管道中的CPU部分,为了在屏幕上绘制某个东西,Android一般将高级XML文件转换为GPU可以识别的对象,而后显示在屏幕上,这个操做是在DisplayList的帮助下完成的,DisplayList持有全部要交给GPU绘制到屏幕上的数据信息,包含GPU要绘制的所有对象的信息列表,还有执行绘制操做的OpenGL命令列表,在某个View第一次须要被渲染时,DisplayList会所以被建立,当这个View要显示到屏幕上时,咱们将绘制指令提交给GPU来执行DisplayList,咱们下次渲染这个View时,好比说位置发生了变化,咱们仅仅须要执行DisplayList就够了,可是若是咱们修改了View的某些可见组件的内容,那么以前的DisplayList就没法继续使用了,这时咱们要从新建立一个DisplayList,从新执行渲染指令并更新到屏幕上。

请注意 :任什么时候候View的绘制内容发生变化,都须要从新建立DisplayList并从新执行指令更新到屏幕,这个流程的表现性能,取决于咱们的View的复杂程度,取决于视觉变化的类型,同时对渲染管道也会产生一些影响。

举例说,假如某个文本框尺寸忽然变成当前的两倍,在改变尺寸前,须要经过父View从新计算,并摆放其余子View的位置,在这种状况下咱们改变了某个View,后面就会有不少工做要作,这些类型的视觉变化须要渲染管道的额外工做,当咱们的View的尺寸变化时,触发了测量操做,会通过整个View Hierarchy,询问各个View的新尺寸,咱们一旦改变了View的大小就会触发上述过程,不管是填充或者图片尺寸、设置文本大小、宽度、高度等等,若是咱们是改变对象位置或者询问布局,或者某个View从新摆放子View都会触发布局操做,会触发整个Hierarchy从新计算对象在屏幕上的新位置。

如今Android运行系统已经很是善于处理记录并执行渲染管道,除非咱们要处理自定义View或者同时须要绘制太多View,其余状况下通常不会耗费太多时间,测量和布局操做性能也很好,可是当咱们的View Hierarchy失控时也更容易出现问题,执行这些功能的时间是和咱们的View Hierarchy中须要处理的节点数成正比的,系统须要处理的View越多处理时间就越长。 某些View可能比其余View要耗费更多时间,形成这种浪费的首要缘由是,View Hierarchy中包含太多的无用View,这些View根本不会显示在屏幕上,一旦触发测量操做和布局操做只会拖累应用程序的性能表现,幸亏有一款叫作Hierarchy Viewer工具,它能够帮助咱们查找并修复这些流氓View,咱们来看看。

Hierarchy Viewer工具

关于Hierarchy Viewer工具的介绍和使用不在本次内容讲解以内,关于这块的资料网上已经不少了。

能够参考:Android UI 优化——使用HierarchyViewer工具(www.cnblogs.com/xyzlmn/p/36…

嵌套结构的性能评测

咱们可能已经知道,在咱们建立Android用户界面时,应该让咱们的布局尽量简单和扁平化,我有一些很好的建议,请记住庞大的布局十分浪费资源,每一个附加嵌套布局和内置视图都会直接影响咱们的应用程序的性能和响应灵敏性,所以请记住,咱们应该了解咱们的应用程序的行为模式,如今咱们要返回到Android Device Monitor,我已经打开Hierarchy Viewer视窗,和之前同样咱们跳转到这里的窗格,选择咱们的设备而后选择想要查看的活动。

在这个例子中咱们要看一下这个根节点,这是咱们的线性布局,这是跟视图群组将会显示这两行,咱们在这里能够看到它们,请注意来自于这个父级线性布局的两个不一样子元素,其中一个表示咱们聊天界面第一行,可是它使用嵌套线性布局实现,第二个对应于布局中的第二行,此次不使用嵌套设计,咱们使用扁平化设计,使用相对布局视图群组,这对应于XML中的代码,进入Android Studio查看咱们的源代码,打开activity_compare_layout.xml文件,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical">
    <!--Version 1 -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="16dp">

        <ImageView
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:src="@drawable/alex"
            android:layout_margin="10dp"/>
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Line 1:悲观者说,但愿是地平线,就算看得见,也永远走不到;"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Line 2:乐观者说,但愿是启明星,即便摘不到,也能告诉人们曙光就在前头。"/>
        </LinearLayout>

    </LinearLayout>

    <!--Version 2 -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp">

        <ImageView
            android:id="@+id/iv_icon"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:src="@drawable/chris"
            android:layout_margin="10dp"/>

        <TextView
            android:id="@+id/tv_line1"
            android:layout_toRightOf="@id/iv_icon"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Line 1:忙碌是一种幸福,让咱们没时间体会痛苦;"/>

        <TextView
            android:id="@+id/tv_line2"
            android:layout_toRightOf="@id/iv_icon"
            android:layout_below="@id/tv_line1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Line 2:奔波是一种快乐,让咱们真实的感觉生活;疲惫是一种享受,让咱们无暇空虚。"/>
            
    </RelativeLayout>

</LinearLayout>

复制代码

如今从新在屏幕上显示以便于进行比较,咱们看到一个父级容器它是一个由垂直方向组成的线性布局,所以这些控件将会从上到下排列,如今我想让咱们注意这里,这是咱们的第一个聊天行,咱们的实现方法不是使用结构化或嵌套布局,这种方法更加直观,逻辑性很强,例如,咱们从一个水平性质的父级线性布局开始,在左侧咱们将设置一个ImageView,在右侧建立另一个嵌套线性布局以容纳咱们的文本,可是在这个例子中,方向是垂直的而不是水平的,它表明第一个条目,接下来咱们能够看到聊天模板的第二行,与使用嵌套结构不一样咱们决定采用扁平化布局,使用相对位置来描述它们。这样作对于性能有什么影响?让咱们返回Hierarchy Viewer

从渲染过程的角度来讲线性布局设计比相对布局更慢一些,与相对布局比较,线性布局须要更多的资源开销,这里所有是绿色。若是咱们有机会采起扁平化布局,咱们应该想办法尽量使用它。

Chatum Latinum优化前:

Chatum Latinum优化后:

关注个人技术公众号"程序员驿站",天天都有优质技术文章推送,微信扫一扫下方二维码便可关注:

image
相关文章
相关标签/搜索