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
相關文章
相關標籤/搜索