Android 佈局小技巧彙總

weixin_34007020發表於2017-03-12

xml 佈局檔案看似好寫,也的確好,但是大家總是百思不得其解,為啥我寫的頁面顯示慢,效率低,擴充套件麻煩,有的手機會出相容問題!這都是大家平時積累不夠的鍋啊,xml佈局檔案的書寫是有規範的,你寫得差一點有的手機的確沒事,但是這並不代表所有都是如此,我本人就碰到過使用相對佈局不規範,出現的相容問題。還有一兩層佈局巢狀就能解決的問題,為啥還有人寫巢狀寫到讓人奔潰呢,善用 xml,寫好 xml,可是看一個 android 開發技術功底的事,這裡記錄一些我個人看到的,總結的小技巧。

目錄導航

  • 靈活使用 linearlayout 佈局的權重
  • textview 最大行數,固定顯示幾行,下劃線,刪除線
  • 相對佈局規範:能確定位置的,被依賴的控制元件寫在前面
  • EditView的自定義樣式:游標,下劃線,選中圖示,選中文字顏色
  • Scrollbar自定義樣式
  • clipChildren屬性的使用:允許子View超出父View
  • 點九圖(.9.png)的使用
  • 使用 tools:attribute 屬性在 xml 中檢視效果
  • 快速生成 style 樣式

靈活使用 linearlayout 佈局的權重


  • 將一個Button在一行中居中,並且Button的寬度佔螢幕寬度的一半

解決方案:先看具體的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:background="#708069"

    android:gravity="center_horizontal"

    android:orientation="horizontal"

    android:weightSum="1" >

    <Button

        android:layout_width="0dp"

        android:layout_height="wrap_content"

        android:layout_weight="0.5"

        android:text="測試按鈕" />
 
</LinearLayout>

其中LinearLayout的android:gravity="center_horizontal"作用是讓Button水平居中;LinearLayout的android:weightSum="1",Button設定android:layout_weight="0.5",則表示button佔據父佈局即LinearLayout一半的寬度,而LinearLayout為match_parent,因此Button會佔據螢幕一半的寬度。


1785445-b291c6d010cce0cc
  • 最外層是LinearLayout,裡面水平從左到右放TextView A,TextView B和一個Button C,要求A居左,C居右,B緊靠A,且單行顯示,如果文字過多則打點。

解決方案:為A設定android:layout_width="wrap_content",為C設定 android:layout_width="wrap_content",且 android:gravity="right|center_vertical",為B設定android:layout_width="0dp",android:layout_weight="1"和android:singleLine="true"。 很容易理解,A在左邊,C在右邊,然後B利用權重佔據剩餘的水平空間,由於設定了singleLine,因此文字如果超過一行就會打點。

  • 實現下圖的效果


    1785445-fa86abe02043401c

解決方案:

(1)左右分別為TextView和ImageView(當然使用Button也可以,這不是重點),外層包一個水平的LinearLayout。使用shape為TextView和Button分別設定帶圓角(左邊的shape設定左上和左下圓角,右邊的shape設定右上和右下圓角)和藍色邊框的背景。這樣做的問題是TextView的右邊框和Button的左邊框都會存在,導致中間的線會是正常邊框的兩倍。因為shape不支援隱藏某一邊的邊框,因此只能另想他法,我採用了將右邊Button左移動1dp(邊框的寬度為1dp,或者TextView右移1dp),這樣中間的兩個線條就會重合,基本可以實現如圖效果,但也有瑕疵,細看的話,中間的豎線由於是兩條線的重合效果,會顯得比邊框亮一些。

(2)為外層LinearLayout設定帶四個圓角和顏色的邊框,LiearLayout裡面水平放置2個控制元件,左邊TextView,中間一個豎線View(width=1dp,跟邊框相同寬度,高度為match_parent,顏色為邊框顏色),右邊是Button,然後再給TextView和Button設定shape背景,與方法(1)不同的是不要設定實體邊框,因為LinearLayout已經設定了邊框。這樣實現的效果就比較完美了。值得注意的是需要給LinearLayout設定一個邊框寬度大小的padding,不然TextView和Button的預設顏色會覆蓋掉LinearLayout邊框的顏色,導致看不到外層邊框。

textview 最大行數,固定顯示幾行,下劃線,刪除線


  • TextView文字最多顯示兩行,多則在末尾打點顯示。

解決方案:為TextView新增如下屬性:

android:maxLines="2"

android:ellipsize="end"

第一個屬性表示該TextView最多可顯示兩行。第二個屬性設為end,表示將會在末尾打點,還可以設為其他屬性,如start、middle、none等,根據名字很容易就能理解了。

注意有一個android:ellipsize="marquee",即傳說中的跑馬燈效果,

若要讓TextView裡的文字滾動,必須滿足以下幾個因素:

(1)TextView裡文字寬度超過TextView的寬度,一般需設定android:singleLine="true"

(2)android:ellipsize="marquee"

(3)只有在TextView獲取到焦點時,才會滾動.所以加上android:focusableInTouchMode="true"

android:focusable="true"

滾動重複次數設定:

android:marqueeRepeatLimit="marquee_forever"

注意:有時候設定了android:maxLines="2",而文字只有一行的時候,會導致TextView跟有兩行文字時高度不一致,因此常常同時設定

android:lines="2",強制佔據兩行的高度。

  • 需求:給文字設定下劃線、中劃線

解決方案:如下程式碼分別設定了下劃線和中劃線

textView.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG );

textView.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);

注意,所生成的線的顏色跟TextView文字的顏色是一致的,如果想把文字和下劃線分別設為不同的顏色,貌似沒有發現TextView有現成的方法或屬性支援,我們可以採用其他策略實現,如文字和下劃線使用兩個不同的view來實現。

相對佈局規範:能確定位置的,被依賴的控制元件寫在前面


先來看一個佈局
[圖片上傳失敗...(image-75d901-1518453662209)]
這個佈局的特點是按鈕3底部對齊,按鈕2在按鈕3的上面,文字框水平充滿剩餘的區域,按鈕1頂部對齊,列表框垂直充滿剩餘的區域。

下面我們會拆分為下面兩個子問題:

水平充滿剩餘區域的問題:

水平方向上有兩個元件,一個元件寬度為wrap_content(或者固定寬度),另外一個元件的需要充滿剩餘的寬度,效果如下:
[圖片上傳失敗...(image-527c18-1518453662210)]
左側一個文字框,右側一個按鈕
如果是巢狀一個LinearLayout佈局肯定就十分簡單了,如果用RelativeLayout也是可以的,如下:

<Button
    android:id="@+id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_above="@id/btn3"
    android:layout_alignParentRight="true"
    android:text="按鈕2"
    />
<EditText
    android:id="@+id/et"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignBaseline="@id/btn2"
    android:layout_alignParentLeft="true"
    android:layout_toLeftOf="@id/btn2"
    />

主要方法如下:(主要通過toRightOf和toLeftOf兩個屬性)

  1. 兩個元件的佈局寬度都是wrap_content(或者固定寬度)
  2. 左邊的元件alignParentLeft,右邊的元件alignParentRight(如果他們左右分別有一些固定寬度的元件,就用toRightOf(左側)或者toLeftOf(右側)指定的元件)
  3. 為了讓左邊的元件緊挨著右邊的元件,也就是拉長左側的文字框,設定toLeftOf="右邊元件的ID" (這裡是重點),這樣,左側文字框就會自動拉伸至和右側按鈕緊挨著。
  4. 當然,最後,兩邊元件要水平對齊,對左邊元件用alignBaseline="右邊元件的ID"
  5. 注意要把右側按鈕在程式碼中放到前面(否則編譯時找不到對應的id,因為左側EditText佈局依賴右側按鈕)

垂直充滿剩餘空間的問題:

垂直方向上有兩個元件,第一個元件的高度為wrap_content(或固定高度),另外一個元件的高度需要充滿剩餘的高度,效果如前面的ListView:
[圖片上傳失敗...(image-10e696-1518453662210)]

這個時候也可以通過RelativeLayout實現,下面我貼出了整個佈局的程式碼(為了更好的說明,我在上面和下面都加了一個元件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
    <Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="固定寬度按鈕"/>
    <Button
        android:id="@+id/btn3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="按鈕3"/>
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/btn3"
        android:layout_alignParentRight="true"
        android:text="按鈕2"
        />
    <EditText
        android:id="@+id/et"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@id/btn2"
        android:layout_alignParentLeft="true"
        android:layout_toLeftOf="@id/btn2"
        />
    <ListView
        android:id="@+id/lv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/btn2"
        android:layout_below="@id/btn1"
        />
</RelativeLayout>

主要看ListView的佈局,方法如下:

  1. 讓ListView below=上面的元件
  2. 讓ListView above=下面的元件
  3. 注意將ListView依賴的元件都放到ListView的上面
    看前面我們給出的完整的佈局檔案程式碼,這裡需要說明如下內容:

注意元件在程式碼中的前後擺放,不然會編譯報錯(前面一直在說),方法如下:
需要拉伸的元件放到後面,然後用另一個元件來進行整體的佈局。也就是被依賴位置的都放到前面,那些固定長寬(指的是固定或者wrap_content且不依賴其他元件佈局的)或者依賴父容器的元件都放到前面,其他元件放到後面,如前面的按鈕1,按鈕3,按鈕2都放到了前面,因為按鈕2依賴於按鈕3(按鈕2 above 按鈕3),所以按鈕2也放到了按鈕3的後面。 (按鈕1說的是最上面那個固定高度按鈕)

讓兩個元件水平中線對齊的問題:

讓一個元件和另外一個元件中線對齊:讓其中一個元件足夠高(或者我們知道哪個元件高度一定是高於頂一個元件的),然後讓需要對齊的元件和這個沒有內容的元件上下皆對齊(alignTop,alignBottom設定),然後將第二個元件的gravity為垂直居中即可。
如果是有 baseline 屬性的 TextView,Button 這類顯示文字的元件的話,可以通過 align_Baseline 來進行對齊,這裡的 baseline指的是文字底部對其的,就像我們小時候拿一把尺子,然後比著尺子寫字那種意思。

EditView的自定義樣式:游標,下劃線,選中圖示,選中文字顏色


首先看看預設樣式(SdkVersion=23,安卓6.0):


1785445-4e051490c0331c15

文字選擇操作時:


1785445-17dae353063ee1d2

文字選中時:
1785445-39e8e0fcecc83642

1.修改游標顏色

修改游標的顏色很簡單,只需要使用android:textCursorDrawable="XXX" 屬性。

首先我們自定義drawable,cursor.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#9bd435"/> <!--顏色設定為淺綠色-->
    <size android:width="2dp"/>

</shape>

使用:

<EditText
     android:layout_width="match_parent"
     android:textCursorDrawable="@drawable/cursor"
     android:layout_height="50dp" />

效果圖:


1785445-475590aa05d77a73

2.修改選中圖示

這個圖示就是預設樣式的2圖與3圖中的墨綠色水滴狀圖示。同樣也很簡單,直接上程式碼。

<EditText
     android:layout_width="match_parent"
     android:textCursorDrawable="@drawable/cursor"
     android:textSelectHandleLeft="@drawable/icon"
     android:textSelectHandleRight="@drawable/icon"
     android:textSelectHandle="@drawable/icon"
     android:layout_height="50dp" />

效果:


1785445-ae73382538ae8bab

1785445-fd358c0615ff5a99

是不是還覺得有點彆扭,文字的選中顏色與EditView預設的下劃線還是墨綠色,其實改起來也很簡單。加上下面兩行程式碼。

android:backgroundTint="#9bd435"     <!--下劃線顏色-->
android:textColorHighlight="#9bd435" <!--選中文字背景色-->

最終自定義效果:


1785445-607e1d9ab4e49bab

使用Material Design主題屬性

首先了解一下Material Design 各個屬性。這裡有張在網上找來的圖(感謝),此圖一目瞭然。


1785445-913fab1a1b4db1d2

那麼其實就簡單了,程式碼如下

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
       <item name="colorAccent">#9bd435</item>
       <item name="android:colorControlActivated">#9bd435</item>
       <item name="android:colorControlNormal">#cccccc</item>
</style>

補充一下:android:colorControlActivated表示EditText、Switch、CheckBox、RadioButton等控制元件啟用時候的顏色。android:colorAccent與android:colorControlActivated作用一樣,但是它可以同時設定文字選中顏色。android:colorControlNormal表示EditText、Switch、CheckBox、RadioButton等控制元件預設時的顏色。相容5.0以下必須使用Theme.AppCompat主題(相容部分效果),並且Activity要繼承AppCompatActivity,在相容時候需要把前面的android:字首去掉。

效果圖:


1785445-105bb1e2ef8a71bd

1785445-4a7928e49ab27ccd

我看了下自己手機中的部分應用,發現使用1、2方法去自定義的只有UC瀏覽器,其中微信和淘寶直接使用的預設樣式。支付寶使用了3方法,畢竟簡單,效果也不錯。

Scrollbar自定義樣式


首先看看預設樣式(SdkVersion=23,安卓6.0):

<?xml version="1.0" encoding="utf-8"?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:paddingLeft="20dp"
    android:paddingRight="20dp"
    android:layout_height="wrap_content">

    ......

</ScrollView>

效果圖:

1785445-e590950dc21cc856

自定義滾動條首先我們要自定義drawable,scrollbar.xml自定義程式碼:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="#9bd435"/>
    <corners android:radius="2dp" />

</shape>

使用scrollbarThumbVertical:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:paddingLeft="20dp"
    android:paddingRight="20dp"
    android:scrollbarSize="4dp"
    android:scrollbars="vertical"
    android:scrollbarThumbVertical="@drawable/scrollbar"
    android:layout_height="wrap_content">

    ......

</ScrollView>

效果圖:


1785445-e9c89bad5c374ee5

當然水平方向的滾動條也是可以自定義的,同時這些也都適用於ListView、RecyclerView。

android:scrollbars="horizontal"
android:scrollbarThumbHorizontal="xxx"

最後還有一個android:scrollbarStyle="xxx",可以設定滾動條的位置。預設是insideOverlay,下面我直接上相應設定對應的效果圖。

insideInset:(位置在padding內,會插入在View後面,不會遮擋View)


1785445-214edc0df7dd3b40

outsideOverlay:(位置在padding外,覆蓋在View上,如果滾動條比padding大會遮擋View)


1785445-6e005785e204f5ec

outsideInset:(位置在padding外,會插入在View後面,不會遮擋View)


1785445-70559c4288c6d3a9

最後兩張圖可能乍一看是一樣的,其實仔細看button距滾動條的位置其實是不一樣的。

3.修改滑動盡頭陰影顏色

預設陰影如圖:(SdkVersion=23,安卓6.0)

1785445-00d77a7f947c1a6a

修改顏色有一種簡單方法,使用Material Design主題屬性colorPrimary,程式碼效果如下:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
       <item name="colorPrimary">#9bd435</item>
</style>
1785445-ba9e0bd6ac953b7c

當然,如果你想去除陰影也非常簡單,加上android:overScrollMode="never" 屬性即可。

clipChildren屬性的使用:允許子View超出父View


android:clipChildren的意思是是否允許子View超出父View。好像有點懵,那我們直接上例子。

1785445-6bf6576c74f5626e

圖中是現在大多外賣app都會有的一個購物車效果。其中紅框中的部分高度略高於旁邊的View。那麼這時就可以使用clipChildren來實現。首先在佈局根節點設定android:clipChildren="false",在使用android:layout_gravity="xxx"控制超出部分。

程式碼:

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:clipChildren="false"> <!--這裡-->

    <LinearLayout
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:orientation="horizontal">

        <RelativeLayout
            android:layout_gravity="bottom" <--這裡
            android:layout_marginLeft="10dp"
            android:layout_width="48dp"
            android:layout_height="60dp">

            <ImageView
                android:src="@drawable/icon_cart"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

            <TextView
                android:layout_marginTop="6dp"
                android:layout_alignParentRight="true"
                android:background="@drawable/icon_spot"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                tools:text="1"
                android:textSize="12sp"
                android:gravity="center"
                android:textColor="#ffffff"/>

        </RelativeLayout>

        <TextView
            android:layout_marginLeft="10dp"
            android:textColor="#9bd435"
            tools:text="¥5.00"
            android:textStyle="bold"
            android:textSize="18sp"
            android:gravity="center_vertical"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent" />

        <TextView
            android:layout_width="110dp"
            android:textColor="#ffffff"
            android:gravity="center"
            android:textSize="16sp"
            android:text="去購物車"
            android:background="#9bd435"
            android:layout_height="match_parent" />

    </LinearLayout>

</RelativeLayout>

效果圖:


1785445-d2b6695a1570715d

點九圖(.9.png)的使用


接著上面的購物車效果,在圖中是不是有一個代表購買商品數量的數字。如果此時一個土豪一次買了上百份的外賣,上面的效果會如何?我就試了試,得到了下面的效果:


1785445-2eac3358524cb22f

可以清楚地看到原本的圓形被橫向拉伸了。。。那就說明這個圓形圖示不是點九圖。那麼我們來製作張。

大家使用Studio可以很方便的去製作,首先右鍵圖片,會彈出以下選單:


1785445-02e1614dae074368

點選Create 9-Patch file... 建立點九圖片。


1785445-bb8bd59b2b7b8e51

上圖就是最終完成的圖片,在上面我有標註各個位置的含義。

替換圖片後現在再來看看效果:


1785445-d05f8bb1a0f01e69

使用 tools:attribute 屬性在 xml 中檢視效果


xml 中的 tools 屬性是一個測試用的輔助屬性,設定了 tools 的屬性並不會真是的去給這個屬性值賦值,而僅僅是用來在 xml 觀察效果的


1785445-e0b002720b1fabdb.gif
4134622-fec5a0b33476b3e5.gif

快速生成 style 樣式


以前我們寫 style 樣式的樣式是一樣一樣複製過去的,麻煩的要死,但是呢 AS 這個 IDE 開發工具是無比強大的,其實可以快速生成 style 樣式的。右鍵開啟選單,依次選擇Refactor -> Extract -> Style ,下面看我的圖就明白了


1785445-17c747ae07f0bae5.png
Snip20180213_3.png

我們先正常編寫一個 view ,然後在這個 view 的位置點選右鍵,然後如上面的操作,就會出現一個生成 style 樣式的確認頁,可以選擇相關的屬性,給 style 命名。是不是很方面

參考文章:

相關文章