Android Notes|細數「十大布局」那些事兒

HLQ_Struggle發表於2020-12-07

最近的心情,最近的狀態,似乎沒法說個一二三四五。

做 Android 好幾年了,從單純的 Android,到現在大雜燴,這個滋味兒,真的是百感交匯。

文章的內容型別從來都是 Notes,這次對老本行進行回顧下。

努力呀,萬一一不小心優秀了呢?萬一一不小心和雞老大肩並肩了呢~

回顧 Android 佈局

曾經無論面試也好,閒聊也罷,談起 Android 佈局,總是不假思索直接說出五大布局。而到現在 2020 年末了,依然還是曾經的五大布局嗎?

這裡簡單的整理了一部分,按照個人使用頻率排序:

  • ConstraintLayout: 約束佈局
  • LinearLayout: 線性佈局
  • RelativeLayout: 相對定位佈局
  • FrameLayout: 幀佈局
  • CoordinatorLayout: 協調佈局
  • 百分比佈局: PercentRelativeLayout、PercentFrameLayout
  • GridLayout: 網格佈局
  • TableLayout: 表格佈局
  • AbsoluteLayout(已棄用): 絕對定位佈局
  • BlinkLayout(私有類): 布靈布靈閃動佈局

這裡說下我是怎麼找的這些佈局,方便和我一樣小白舉一反三。

引用 Flutter 一句話,萬物皆為 Weight。而在 Android 中,直觀而言,能看到的都是 View,而 View 也分不同的作用,例如 TextView、ImageView 等基礎常用 View,僅僅為了展示或者間接響應使用者操作。而基於 View 衍生出的 ViewGroup 則是通過包裹各種 View,最終呈現特有的效果。例如 LinearLayout 在原有 ViewGroup 基礎上新增水平/垂直排列方式、RelativeLayout 在原基礎上新增基於某個控制元件進行排列等。所以說直接在 Android API reference 搜尋 ViewGroup,檢視直接子類以及間接子類即可,如下所示:

文末已附上對應的連結,歡迎各位大佬指點~

五個星星我認為是必須要掌握的,比較實戰中出現評率很高了。一星簡單瞭解,面試和麵試官吹個水就好~

篇幅有限,就不一一舉例了~

反正知道肯定要比什麼都不知道要強很多的~

文中借用圖片,文末均以附上地址連結~

一、ConstraintLayout ⭐️⭐️⭐️⭐️⭐️

  • ConstraintLayout 通過各種約束進行排列子 View 的佈局。

這個東西最牛掰的一點就是,支援視覺化工具操作,及其方便。在下面的事例中也會多多少少體驗一波~

使用方式:

  • 新增 Maven 庫
repositories {
    google()
}
  • 新增 ConstraintLayout 依賴
dependencies {
    implementation "androidx.constraintlayout:constraintlayout:2.0.4"
}

當然如果你的 Android Studio 升級到最新版本,預設佈局便是 ConstraintLayout,還是要去 build 中檢視下版本。我也忘記是哪兒個版本設定預設根佈局了。

當然,貼心的 Android Studio 也提供一鍵轉化根佈局功能,如下圖:

1.相對定位 layout_constraintXXX

相對定位是在 ConstraintLayout 中建立佈局基本構建塊之一。這些約束允許一個 View 基於某個 View 進行定位,同樣我們可以在水平方向以及垂直方向進行約束 View:

  • 水平軸: 左,右,起點和終點
  • 垂直軸: 頂部,底部和文字基線

如下,實現將 B 按鈕定位在 A 按鈕右側:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A"  />

    <Button
        android:id="@+id/buttonB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="B"
        app:layout_constraintStart_toEndOf="@+id/buttonA"  />

</androidx.constraintlayout.widget.ConstraintLayout>

下面借用官方圖,為了更好的理解下面的屬性:

屬性作用
layout_constraintLeft_toLeftOf當前 View 左側對標目標 View 左側
layout_constraintLeft_toRightOf當前 View 左側對標目標 View 右側
layout_constraintRight_toLeftOf當前 View 右側對標目標 View 左側
layout_constraintRight_toRightOf當前 View 右側對標目標 View 右側
layout_constraintTop_toTopOf當前 View 頂部對標目標 View 頂部
layout_constraintTop_toBottomOf當前 View 頂部對標目標 View 底部
layout_constraintBottom_toTopOf當前 View 底部對標目標 View 頂部
layout_constraintBottom_toBottomOf當前 View 底部對標目標 View 底部
layout_constraintBaseline_toBaselineOf當前 View 與目標 View 文字基線對齊
layout_constraintStart_toEndOf當前 View 起點對標目標 View 終點
layout_constraintStart_toStartOf當前 View 起點對標目標 View 起點
layout_constraintEnd_toStartOf當前 View 終點對標目標 View 起點
layout_constraintEnd_toEndOf當前 View 終點對標目標 View 終點

2.邊距 Margins

屬性作用
android:layout_marginStart當前 View 距離目標 View 左側間距
android:layout_marginEnd當前 View 距離目標 View 右側間距
android:layout_marginLeft當前 View 距離目標 View 左側間距
android:layout_marginTop當前 View 距離目標 View 頂部間距
android:layout_marginRight當前 View 距離目標 View 右側間距
android:layout_marginBottom當前 View 距離目標 View 底部間距

3.目標 View 隱藏時,當前 View 邊距 Margins

當目標 View 的可見性為 View.GONE 時,還可以使用以下屬性設定當前 View 在前者 GONE 情況下的 margin。

屬性作用
layout_goneMarginStart目標 View 隱藏時,當前 View 距離左側間距
layout_goneMarginEnd目標 View 隱藏時,當前 View 距離右側間距
layout_goneMarginLeft目標 View 隱藏時,當前 View 距離左側間距
layout_goneMarginTop目標 View 隱藏時,當前 View 距離頂部間距
layout_goneMarginRight目標 View 隱藏時,當前 View 距離右側間距
layout_goneMarginBottom目標 View 隱藏時,當前 View 距離底部間距

如下效果:

預設 A、B 按鈕 Margin 為 50dp,在 A 按鈕隱藏狀態下,B 按鈕距離 A 的邊距變為 30dp:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="50dp"
        android:text="A"
        android:visibility="gone"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="50dp"
        android:text="B"
        app:layout_constraintStart_toEndOf="@+id/button"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_goneMarginStart="30dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

4.居中定位和偏向比例

很多時候,我們需要的效果為居中,同時某些情況下也需要去設定比例,比如寬度百分比,下面直接上效果圖:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-s4DniE61-1607305113338)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c54c8217af1c46eab864e47130655357~tplv-k3u1fbpfcp-watermark.image)]

程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:layout_width="180dp"
        android:layout_height="180dp"
        android:scaleType="fitXY"
        android:src="@drawable/img"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.19"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.16000003" />

</androidx.constraintlayout.widget.ConstraintLayout>

這裡再次回顧下當前例子中關鍵內容:

屬性作用
layout_constraintStart_toStartOf當取值為 parent 代表與父容器對齊
layout_constraintEnd_toEndOf
layout_constraintTop_toTopOf
layout_constraintBottom_toBottomOf

下面是各個組合方式對應的效果:

  • start 和 end 組合,便是水平居中
  • top 和 bottom 組合,便是垂直居中
  • start、end、top、bottom 組合便是水平/垂直居中
屬性作用
layout_constraintVertical_bias垂直方式佔比
layout_constraintHorizontal_bias水平方式佔比

5.圓形定位

這裡為了方便,我就直接截圖了:

下面著手實現如下效果:

程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我是中心"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/buttonB"
        app:layout_constraintCircle="@id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintCircleAngle="45"
        app:layout_constraintCircleRadius="100dp"
        android:text="我是花兒" />

</androidx.constraintlayout.widget.ConstraintLayout>
屬性作用
layout_constraintCircle指定圓心 View ID
layout_constraintCircleAngle設定當前 View 角度
layout_constraintCircleRadius設定半徑

6.尺寸限制

也可以為 ConstraintLayout 自身定義最小和最大大小:

屬性作用
android:minWidth設定佈局的最小寬度
android:minHeight設定佈局的最小高度
android:maxWidth設定佈局的最大寬度
android:maxHeight設定佈局的最大高度

當 ConstraintLayout 內部子 View 寬度/高度為 0dp,則同等於 match_parent。

7.尺寸百分比

這個其實我蠻喜歡的,類似百分比佈局,爽的很。

使用這塊需要注意:

  • 設定寬度/高度百分比時,需要先將對應的寬/高設定為 0dp;
  • 預設值應設定為百分比 app:layout_constraintWidth_default=“percent” 或 app:layout_constraintHeight_default=“percent” ;(這點感覺沒啥用,不信你看下面)
  • layout_constraintWidth_percent 或者 layout_constraintHeight_percent 屬性設定為介於 0 和 1 之間的值;

下面著手實現如下效果:

第一個 View 寬高佔比為:0.3:0.2,第二個 View 寬度佔比為 1:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/buttonA"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="我是中心"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_percent="0.2"
        app:layout_constraintWidth_percent="0.3"
        app:layout_constraintStart_toStartOf="parent"  />

    <Button
        android:id="@+id/buttonB"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="我是花兒"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.32"
        app:layout_constraintWidth_percent="1" />

</androidx.constraintlayout.widget.ConstraintLayout>
屬性作用
layout_constraintHeight_percent高度佔比
layout_constraintWidth_percent寬度佔比

作為一枚不折不扣的程式設計師而言,能省事兒絕對省事兒,上面寬高還得寫兩次,能不能一次搞定呢?

下面實現一個寬高比為 16:9 :

<Button
    android:id="@+id/buttonA"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:text="我是中心"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintDimensionRatio="16:9"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

效果如下:

8.鏈式

這個效果也是蠻不錯的,有些類似前端的 Flex,感覺還是不錯的。

設定屬性 layout_constraintHorizontal_chainStyle 或 layout_constraintVertical_chainStyle 在鏈的第一個元素上時,鏈的行為將根據指定的樣式而改變(預設為 CHAIN_SPREAD )。

演示圖如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-xPOChQJB-1607305113342)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/96ac4517fa63420883e536a5f6d00f8a~tplv-k3u1fbpfcp-watermark.image)]

程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="130dp">

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="B"
        app:layout_constraintEnd_toStartOf="@+id/button3"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/button1" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="C"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/button2" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A"
        app:layout_constraintEnd_toStartOf="@+id/button2"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

9.輔助物件 Guideline

據官方所言,此為 ConstraintLayout 輔助工具,預設為 View.GONE。

使用方式主要分為兩種情況:

  • 百分比定位: layout_constraintGuide_percent
  • 絕對定位: layout_constraintGuide_begin / layout_constraintGuide_end

最後我們可以通過 orientation 去設定當前輔助線的顯示方式,水平/垂直。

我個人蠻喜歡百分比方式,先來個效果:

如何確保圖片在每種機型上都位於螢幕百分之 15 高度的位置呢?通過輔助線百分比分分鐘的事兒。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="fitXY"
        android:src="@drawable/img"
        app:layout_constraintDimensionRatio="16:9"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline4" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.15" />

</androidx.constraintlayout.widget.ConstraintLayout>

而關於絕對定位更好理解啦。這裡直接錄製效果圖咯,大家注意觀察點選 Icon 後程式碼以及效果變化:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-C4LZLV2M-1607305113343)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5c2d211078254adea2af7301209ea12e~tplv-k3u1fbpfcp-watermark.image)]

番外:瞭解約束佈局效能優勢

文末已附上鍊接地址,這裡只對個人感興趣的部分做出節選。

藉助 Google 翻譯學習,配合自己理解,如錯誤,歡迎指正~

針對傳統佈局以及約束佈局的優勢,這裡以下面效果為例,簡單進行對比:

傳統佈局繪製層級:

<RelativeLayout>
  <ImageView />
  <ImageView />
  <RelativeLayout>
    <TextView />
    <LinearLayout>
      <TextView />
      <RelativeLayout>
        <EditText />
      </RelativeLayout>
    </LinearLayout>
    <LinearLayout>
      <TextView />
      <RelativeLayout>
        <EditText />
      </RelativeLayout>
    </LinearLayout>
    <TextView />
  </RelativeLayout>
  <LinearLayout >
    <Button />
    <Button />
  </LinearLayout>
</RelativeLayout>

約束佈局繪製層級:

<android.support.constraint.ConstraintLayout>
  <ImageView />
  <ImageView />
  <TextView />
  <EditText />
  <TextView />
  <TextView />
  <EditText />
  <Button />
  <Button />
  <TextView />
</android.support.constraint.ConstraintLayout>

直觀上從兩種方案繪製層級相比,明顯約束佈局優勢更大。至少相比傳統 RelativeLayout 少繪製幾個 ViewGroup。

這裡從官方博文中可以得知 Android 繪製檢視過程包括如下三個階段:

  • 測量(Measure)

    • 系統從檢視樹自頂向下遍歷,以確定每個 ViewGroup 和 View 元素大小。而測量 ViewGroup 時,還將測量其子集 View。
  • 佈局(Layout)

    • 從上到下的遍歷,通過在測量階段確定的大小來確定子 View 的位置。
  • 繪製(Draw)

    • 系統執行的一個自上而下的遍歷,對於檢視樹中的每個物件,都會建立一個 Canvas 物件,已將繪圖命令傳送 GPU。這些命令包括 ViewGroup 和 View 大小、位置,這是系統在前兩個階段中確定的內容。

所以,我們可以得出一個概念,繪製層級越深,消耗越大。反之,消耗則低,效能越高。

測量結果:ConstraintLayout更快。

ConstraintLayout 在測量/佈局階段的效能比 RelativeLayout 好約 40%:

二、LinearLayout ⭐️⭐️⭐️⭐️

  • LinearLayout 是行內以水平方式/垂直方式排列的佈局容器。

常用屬性一覽:

屬性作用
android:orientation行內排列方式(horizontal/vertical),預設水平排列
android:gravity行內 View 對齊方式
android:weightSum行內可設定的最大佔比權重
android:layout_weight當前 View 佔比權重
android:baselineAligned父容器佈局是否對齊子 View 基線
android:baselineAlignedChildIndex指定基線對齊的子 View

來個簡單的效果:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:baselineAligned="true"
    android:measureWithLargestChild="true"
    android:padding="15dp"
    android:weightSum="1"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0.5"
        android:text="取消" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0.5"
        android:text="確定" />

</LinearLayout>

三、RelativeLayout ⭐️⭐️⭐️⭐️

  • RelativeLayout 是一個以相對位置顯示子檢視的檢視組。

常用屬性:

屬性作用
android:layout_alignParentTop當前 View 上邊緣和父容器上邊緣對齊
android:layout_alignParentEnd當前 View 上邊緣和父容器右邊緣對齊
android:layout_alignParentBottom當前 View 上邊緣和父容器下邊緣對齊
android:layout_alignParentStart當前 View 上邊緣和父容器左邊緣對齊
android:layout_centerHorizontal當前 View 基於父容器水平居中
android:layout_centerVertical當前 View 基於父容器垂直居中
android:layout_centerInParent當前 View 基於父容器水平居中並垂直居中
android:layout_alignTop當前 View 位於目標 View 頂部
android:layout_toEndOf當前 View 位於目標 View 右側
android:layout_below當前 View 位於目標 View 底部
android:layout_toStartOf當前 View 位於目標 View 左側

著手實現如下效果:

<?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="130dp"
    android:padding="15dp">

    <ImageView
        android:id="@+id/ivAvatar"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:scaleType="fitXY"
        android:src="@drawable/img" />

    <TextView
        android:id="@+id/tvNickname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:layout_toEndOf="@id/ivAvatar"
        android:text="暱稱:HLQ_Struggle" />

    <TextView
        android:id="@+id/tvLevel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tvNickname"
        android:layout_marginStart="12dp"
        android:layout_marginTop="15dp"
        android:layout_toEndOf="@id/ivAvatar"
        android:text="等級:V1" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tvLevel"
        android:layout_alignParentBottom="true"
        android:layout_marginStart="12dp"
        android:layout_marginTop="15dp"
        android:layout_toEndOf="@id/ivAvatar"
        android:text="性別:男" />

</RelativeLayout>

四、FrameLayout ⭐️⭐️⭐️⭐️

  • FrameLayout 預設將控制元件層疊放置螢幕左上角。子 View 通過 android:layout_gravity 去設定自身顯示位置。

比較重要的幾個屬性:

  • android:layout_gravity: 子 View 對齊方式
  • android:foreground: 前景圖
  • android:foregroundGravity: 前景圖位置

下面開始實現如下效果:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:foreground="@android:drawable/btn_star"
    android:foregroundGravity="right|bottom"
    android:padding="30dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="< 首頁" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:text="更多" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="首頁" />

</FrameLayout>

五、GridLayout ⭐️

  • GridLayout 是以網格形式顯示子級 View 元素的 ViewGroup。

先來看看關鍵屬性:

  • android:columnCount: 列數
  • android:rowCount: 行數

GridLayout 子 View 屬性:

  • android:layout_column: 當前 View 從第幾列開始顯示
  • android:layout_columnSpan: 當前 View 佔據列數
  • android:layout_row: 當前 View 從第幾行開始顯示
  • android:layout_rowSpan: 當前 View 所佔行數
  • android:layout_gravity: 對齊方式

比較典型的例子就是計算器了吧:

程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:columnCount="4"
    android:rowCount="6">

    <TextView
        android:layout_columnSpan="4"
        android:layout_gravity="fill"
        android:layout_margin="6dp"
        android:background="#f5f5f5"
        android:text="0"
        android:textSize="50sp" />

    <Button
        android:layout_columnSpan="2"
        android:layout_gravity="fill"
        android:text="回退" />

    <Button
        android:layout_columnSpan="2"
        android:layout_gravity="fill"
        android:text="清空" />

    <Button android:text="+" />

    <Button android:text="1" />

    <Button android:text="2" />

    <Button android:text="3" />

    <Button android:text="-" />

    <Button android:text="4" />

    <Button android:text="5" />

    <Button android:text="6" />

    <Button android:text="*" />

    <Button android:text="7" />

    <Button android:text="8" />

    <Button android:text="9" />

    <Button android:text="/" />

    <Button
        android:layout_width="wrap_content"
        android:text="." />

    <Button android:text="0" />

    <Button android:text="=" />

</GridLayout>
    

六、TableLayout ⭐️

  • TableLayout 是以行和列顯示子級 View 元素的 ViewGroup。

來個效果實際說明:

TableLayout 行內 View 預設佔據一行的寬度。如果想一行包含多列,則需要用 TableRow 包裹,如下部分程式碼:

<TableLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <TextView 
        android:text="9*9=81" />

    <TableRow>

        <TextView 
            android:text="9*8=72" />

        <TextView 
            android:text="8*8=64" />
    </TableRow>

    <TableRow>

        <TextView 
            android:text="9*8=72" />

        <TextView 
            android:text="8*8=64" />

        <TextView 
            android:text="8*8=64" />
    </TableRow>

</TableLayout>
  • android:stretchColumns: 設定某列寬度為剩餘行寬度

<TableLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" 
    android:stretchColumns="0"> <!-- 第一列寬度為剩餘行寬度 -->

    <TableRow>

        <Button 
            android:text="我是第一列" />

        <Button 
            android:text="我是第二列" />

    </TableRow>

</TableLayout>
  • android:collapseColumns: 隱藏某列

<TableLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" 
    android:collapseColumns="0,2">

    <TableRow>

        <Button 
            android:text="我是第一列" />

        <Button 
            android:text="我是第二列" />

        <Button 
            android:text="我是第三列" />

        <Button 
            android:text="我是第四列" />

    </TableRow>

</TableLayout>
  • android:shrinkColumns: 收縮某列

<TableLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" 
    android:shrinkColumns="0">
	
    <!-- ... -->
    
</TableLayout>

谷歌這裡提供了一個不錯的效果,感興趣的可以自己嘗試一下,如下:

七、AbsoluteLayout ⭐️

使用方式:

  • 根據子級的 x/y 座標確定自身位置。靈活性較差,後續不易維護。

且在 Api 30 中已棄用。

下面實現如下效果:

第二個 TextView 位於第一個 TextView x/y 軸距離分別為 100dp:

<AbsoluteLayout
    android:layout_width="0dp"
    android:layout_height="300dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="世界是美好的~" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_x="100dp"
        android:layout_y="100dp"
        android:text="你在哪兒~" />

</AbsoluteLayout>

八、BlinkLayout ⭐️

這個東西就比較神奇了,一閃一閃,布靈布靈。先來個效果:

作用就是每隔 500ms 執行一次繪製,呈現的效果就是布靈布靈一閃一閃的。

使用方式也是比較 easy:

<blink
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="世界是美好的~" />

</blink>

原始碼相對比較少,下面貼一下,方便以後想看隨時看? :

private static class BlinkLayout extends FrameLayout {
    private static final int MESSAGE_BLINK = 0x42;
    private static final int BLINK_DELAY = 500;

    private boolean mBlink;
    private boolean mBlinkState;
    private final Handler mHandler;

    public BlinkLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                if (msg.what == MESSAGE_BLINK) {
                    if (mBlink) {
                        mBlinkState = !mBlinkState;
                        makeBlink();
                    }
                    invalidate();
                    return true;
                }
                return false;
            }
        });
    }

    private void makeBlink() {
        Message message = mHandler.obtainMessage(MESSAGE_BLINK);
        mHandler.sendMessageDelayed(message, BLINK_DELAY);
    }

    /**
     * View 附加到 window 上的時候進行回撥 適合初始化一些操作
     */
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        mBlink = true;
        mBlinkState = true;

        makeBlink();
    }

    /**
     * View 分離 window 的時候進行回撥 適合銷燬,重置一些操作
     */
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        mBlink = false;
        mBlinkState = true;

        mHandler.removeMessages(MESSAGE_BLINK);
    }

    /**
     * 分發子元件進行繪製
     * @param canvas
     */
    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (mBlinkState) {
            super.dispatchDraw(canvas);
        }
    }
}

The end

永遠不要停止探索。

很多時候,十分鐘的探索會給你帶來不一樣的世界。

一起努力,一起期待,一起努力為了未來變得優秀吧~

如有筆誤或者理解錯誤,歡迎交流~

Thanks

相關文章