项目中的页面,只要不是纯静态数据的页面,都有进行状态展现和切换的需求。好比一个列表页面正在请求数据,须要展现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应用。