項目中的頁面,只要不是純靜態數據的頁面,都有進行狀態展現和切換的需求。好比一個列表頁面正在請求數據,須要展現Loading的效果;若是請求失敗還須要展現失敗的界面;若是數據未空,還須要展現空數據的頁面。php
這種狀態頁面的需求會涵蓋項目中大部分的頁面,若是實現的不優雅,寫起來會很是的棘手。android
我最先見過的一種是硬編碼方案,就是將全部的狀態佈局代碼都寫在每一個頁面中,默認都gone掉,就算你獨立抽取一個佈局,經過include引入也是屬於這種方案。而後在代碼中提供幾個showLoading()
, showEmpty()
,showError()
之類的方法來控制佈局的隱藏和可見。git
佈局的代碼大概相似於這樣:github
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">
<!--界面原有佈局 -->
<FrameLayout/>
<!--請求Loading佈局 -->
<FrameLayout android:visibility="gone"/>
<!--請求失敗佈局 -->
<FrameLayout android:visibility="gone"/>
<!--請求數據爲空的佈局 -->
<FrameLayout android:visibility="gone"/>
</LinearLayout>
複製代碼
這種方案直接侵入了每個界面的佈局,下降了佈局的可讀性,不可取。app
上面這種方案能夠經過重構減小代碼,好比在基類中抽取出showLoading()
, showEmpty()
,showError()
之類的方法,子類調用便可。佈局
上面的硬編碼方案只適用於總體界面須要狀態切換的場景,但有時候咱們須要對按鈕進行狀態切換,好比一個登陸按鈕,收藏按鈕,關注按鈕。這是基於用戶體驗的考慮,用戶須要知道軟件的運行狀態,軟件須要在邏輯上給用戶交互反饋,某種意義上和Material Design的觸摸反饋理念是一致的。this
當用戶點擊關注時,按鈕應該有關注中的狀態效果,關注中的狀態下不可進行交互。效果以下:編碼
若是是這種需求的話,那上面的硬編碼方案就很難寫了。固然若是你願意的話,你能夠給全部須要狀態切換的按鈕外面包裹一些佈局,也不是不能作。spa
我所指望的效果是,能夠動態給咱們的界面或者某個具體的按鈕加上狀態切換的功能,而寫佈局的時候咱們不須要編寫一行狀態相關的東西。code
基於個人指望,我實現了一個StateLayout,它是一個自定義的佈局,代碼並不複雜。它能作到的效果是這樣:
我不打算貼代碼了,由於沒有什麼難的東西。它用起來很是簡單,對現有的佈局零侵入,經過動態的給目標View包裹一些狀態佈局來實現。
對Activity/Fragment使用:
val stateLayout = StateLayout(this)
.wrap(this)
.showLoading()
複製代碼
對指定View使用:
val layout2 = StateLayout(this)
.wrap(view)
.showLoading()
複製代碼
默認狀況下是顯示內容佈局,改變狀態的方法:
stateLayout.showLoading() //default state
stateLayout.showContent()
stateLayout.showError()
stateLayout.showEmpty()
複製代碼
你還能夠自定義每種狀態對應的佈局,畢竟默認的狀態佈局是有點醜的:
StateLayout(this)
.config(loadingLayoutId = R.layout.custom_loading, //自定義加載中佈局
errorLayoutId = R.layout.custom_error, //自定義加載失敗佈局
emptyLayoutId = R.layout.custom_empty, //自定義數據位爲空的佈局
useContentBgWhenLoading = true, //加載過程當中是否使用內容的背景
enableLoadingShadow = true, //加載過程當中是否啓用半透明陰影蓋在內容上面
retryAction = { //點擊errorView的回調
Toast.makeText(this, "點擊了重試", Toast.LENGTH_SHORT).show()
})
.wrap(view)
.showLoading()
複製代碼
類庫的地址在這裏:github.com/li-xiaojun/…
我致力於構建現代化的Android開發技術棧,用兼顧優雅和效率的代碼,來開發高質量的Android應用。