日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

android loading封装_我们经常用的Loading动画居然还有这种姿势

發(fā)布時(shí)間:2023/12/4 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android loading封装_我们经常用的Loading动画居然还有这种姿势 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

背景

Loading動(dòng)畫幾乎每個(gè)Android App中都有。

一般在需要用戶等待的場(chǎng)景,顯示一個(gè)Loading動(dòng)畫可以讓用戶知道App正在加載數(shù)據(jù),而不是程序卡死,從而給用戶較好的使用體驗(yàn)。

同樣的道理,當(dāng)加載的數(shù)據(jù)為空時(shí)顯示一個(gè)數(shù)據(jù)為空的視圖、在數(shù)據(jù)加載失敗時(shí)顯示加載失敗對(duì)應(yīng)的UI并支持點(diǎn)擊重試會(huì)比白屏的用戶體驗(yàn)更好一些。

加載中、加載失敗、空數(shù)據(jù)的UI風(fēng)格,一般來(lái)說(shuō)在App內(nèi)的所有頁(yè)面中需要保持一致,也就是需要做到全局統(tǒng)一。

1. 傳統(tǒng)的做法

  • 定義一個(gè)(或多個(gè))顯示不同加載狀態(tài)的控件或者xml布局文件(例如:LoadingView)
  • 每個(gè)頁(yè)面的布局中都寫上這個(gè)view
  • 在BaseActivity/BaseFragment中封裝LoadingView的初始化邏輯,并封裝加載狀態(tài)切換時(shí)的UI顯示邏輯,暴露給子類以下方法:
    • void showLoading(); //調(diào)用此方法顯示加載中的動(dòng)畫
    • void showLoadFailed(); //調(diào)用此方法顯示加載失敗界面
    • void showEmpty(); //調(diào)用此方法顯示空頁(yè)面
    • void onClickRetry(); //子類中實(shí)現(xiàn),點(diǎn)擊重試的回調(diào)方法
  • 在BaseActivity/BaseFragment的子類中可通過(guò)上一步的封裝比較方便地使用加載狀態(tài)顯示功能
  • 這種使用方式耦合度太高,每個(gè)頁(yè)面的布局文件中都需要添加LoadingView,使用起來(lái)不方便而且維護(hù)成本較高,一旦UI設(shè)計(jì)師需要更改布局,修改起來(lái)成本較高。

    2. 好一點(diǎn)的封裝方法

  • 定義一個(gè)(或多個(gè))顯示不同加載狀態(tài)的控件或者xml布局文件(例如:LoadingView)
  • 定義一個(gè)工具類(LoadingUtil)來(lái)管理LoadingView,不同狀態(tài)顯示不同的UI(或者在多個(gè)View之間切換顯示)
  • 在BaseActivity/BaseFragment中對(duì)LoadingUtil的使用進(jìn)行封裝,暴露給子類以下方法:
    • void showLoading(); //調(diào)用此方法顯示加載中的動(dòng)畫
    • void showLoadFailed(); //調(diào)用此方法顯示加載失敗界面
    • void showEmpty(); //調(diào)用此方法顯示空頁(yè)面
    • void onClickRetry(); //子類中實(shí)現(xiàn),點(diǎn)擊重試的回調(diào)方法
    • abstract int getContainerId(); //子類中實(shí)現(xiàn),LoadingUtil動(dòng)態(tài)創(chuàng)建LoadingView并添加到該方法返回id對(duì)應(yīng)的控件中
  • 在BaseActivity/BaseFragment的子類中可通過(guò)上一步的封裝比較方便地使用加載狀態(tài)顯示功能
  • 這種封裝的好處是通過(guò)封裝動(dòng)態(tài)地創(chuàng)建LoadingView并添加到指定的父容器中,讓具體頁(yè)面無(wú)需關(guān)注LoadingView的實(shí)現(xiàn),只需要指定在哪個(gè)容器中顯示即可,很大程度地進(jìn)行了解耦。如果公司只在一個(gè)App中使用,這基本上就夠了。

    但是,這種封裝方式還是存在耦合:頁(yè)面與它所使用的LoadingView仍然存在綁定關(guān)系。如果需要復(fù)用到其它App中,因?yàn)槊總€(gè)App的UI風(fēng)格可能不同,對(duì)應(yīng)的LoadingView布局也可能會(huì)不一樣,要想復(fù)用必須先將頁(yè)面與LoadingView解耦。

    如何解耦?

    1. 梳理一下我們需要實(shí)現(xiàn)的效果

    • 頁(yè)面的LoadingView可切換,且不需要改動(dòng)頁(yè)面代碼
    • 頁(yè)面中可指定LoadingView的顯示區(qū)域(例如導(dǎo)航欄Title不希望被LoadingView覆蓋)
    • 支持在Fragment中使用
    • 支持加載失敗頁(yè)面中點(diǎn)擊重試
    • 兼容不同頁(yè)面顯示的UI有細(xì)微差別(例如提示文字可能不同)

    2. 確定思路

    說(shuō)到View的解耦,很容易聯(lián)想到Android系統(tǒng)中的AdapterView(我們常用的GridView和ListView都是它的子類)及support包里提供的ViewPager、RecyclerView等,它們都是通過(guò)Adapter來(lái)解耦的,將自身的邏輯與需要?jiǎng)討B(tài)變化的子View進(jìn)行分離。我們也可以按照這個(gè)思路來(lái)解耦LoadingView:

    • 創(chuàng)建一個(gè)工具類,用于管理LoadingView各個(gè)狀態(tài)的UI展示
    • 創(chuàng)建一個(gè)Adapter接口,外部提供實(shí)現(xiàn)類,通過(guò)getView方法創(chuàng)建具體的LoadingView
    • 每個(gè)App提供一個(gè)Adapter的實(shí)現(xiàn),并注冊(cè)到工具類中
    • 工具類從Adapter.getView獲取具體的LoadingView,所以頁(yè)面中使用的代碼無(wú)需改動(dòng)
    (已實(shí)現(xiàn))頁(yè)面的LoadingView可切換,且不需要改動(dòng)頁(yè)面代碼
    • 由于每個(gè)頁(yè)面或View的加載狀態(tài)互相之間無(wú)關(guān)聯(lián)關(guān)系,需要?jiǎng)?chuàng)建一個(gè)用于管理具體某個(gè)LoadingView的狀態(tài)持有類:Holder
    • 指定LoadingView所需覆蓋的View時(shí),動(dòng)態(tài)新建一個(gè)FrameLayout布局
    • 將原View從ParentView中移除,并用它的LayoutParams將FrameLayout添加到ParentView中替代原View在ParentView中的位置
    • 再將原View添加到FrameLayout中
    • 在Fragment.onCreateView/RecyclerView.Adapter.onCreateViewHolder等方法中創(chuàng)建的View時(shí),由于View尚未添加到任何容器中,并無(wú)getParent()返回null,此時(shí)需要用動(dòng)態(tài)生成的FrameLayout代替原View作為方法的返回值返回

    上代碼更容易理解:

    public Holder wrap(View view) {FrameLayout wrapper = new FrameLayout(view.getContext());ViewGroup.LayoutParams lp = view.getLayoutParams();if (lp != null) {wrapper.setLayoutParams(lp);}if (view.getParent() != null) {ViewGroup parent = (ViewGroup) view.getParent();int index = parent.indexOfChild(view);parent.removeView(view);parent.addView(wrapper, index);}LayoutParams newLp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);wrapper.addView(view, newLp);return new Holder(mAdapter, view.getContext(), wrapper); } (已實(shí)現(xiàn))頁(yè)面中可指定LoadingView的顯示區(qū)域 (已實(shí)現(xiàn))支持在Fragment中使用 另外,還順帶支持在RecyclerView、ListView、GridView、ViewPager等情況下的使用
    • 為了不侵入U(xiǎn)I,將加載失敗點(diǎn)擊重試的點(diǎn)擊功能放在Adapter.getView中實(shí)現(xiàn)
    • 與Android系統(tǒng)中的Adapter不同的是,我們的Adapter是全局使用的,而失敗重試所需執(zhí)行邏輯每個(gè)頁(yè)面都不一樣
    • 因?yàn)镠older可以持有每個(gè)具體的LoadingView,可以將retryTask通過(guò)Holder傳遞給Adapter
    • 只需要在Adapter.getView時(shí)將Holder作為參數(shù)傳入,即可在創(chuàng)建LoadingView時(shí)獲取該retryTask對(duì)象,并在點(diǎn)擊重試按鈕時(shí)執(zhí)行retryTask
    • 同理,可以通過(guò)Holder傳遞一些附加參數(shù)給Adapter,以兼容在不同頁(yè)面上布局的細(xì)微差異
    (已實(shí)現(xiàn))支持加載失敗頁(yè)面中點(diǎn)擊重試 (已實(shí)現(xiàn))兼容不同頁(yè)面顯示的UI有細(xì)微差別(例如提示文字可能不同)

    使用Gloading來(lái)輕松實(shí)現(xiàn)低耦合的全局LoadingView

    Gloading是一個(gè)基于Adapter思路實(shí)現(xiàn)的深度解耦A(yù)pp中全局LoadingView的輕量級(jí)工具(只有一個(gè)java文件,不到300行,其中注釋占100+行,aar僅6K)

    1、 依賴Gloading

    compile 'com.billy.android:gloading:1.0.0'

    2、 創(chuàng)建Adapter,在getView方法中實(shí)現(xiàn)創(chuàng)建各種狀態(tài)視圖(加載中、加載失敗、空數(shù)據(jù)等)的邏輯

    Gloading不侵入U(xiǎn)I布局,完全由用戶自定義。示例如下:

    public class GlobalAdapter implements Gloading.Adapter {@Overridepublic View getView(Gloading.Holder holder, View convertView, int status) {GlobalLoadingStatusView loadingStatusView = null;//convertView為可重用的布局//Holder中緩存了各狀態(tài)下對(duì)應(yīng)的View// 如果status對(duì)應(yīng)的View為null,則convertView為上一個(gè)狀態(tài)的View// 如果上一個(gè)狀態(tài)的View也為null,則convertView為nullif (convertView != null && convertView instanceof GlobalLoadingStatusView) {loadingStatusView = (GlobalLoadingStatusView) convertView;}if (loadingStatusView == null) {loadingStatusView = new GlobalLoadingStatusView(holder.getContext(), holder.getRetryTask());}loadingStatusView.setStatus(status);return loadingStatusView;}class GlobalLoadingStatusView extends RelativeLayout {public GlobalLoadingStatusView(Context context, Runnable retryTask) {super(context);//初始化LoadingView//如果需要支持點(diǎn)擊重試,在適當(dāng)?shù)臅r(shí)機(jī)給對(duì)應(yīng)的控件添加點(diǎn)擊事件}public void setStatus(int status) {//設(shè)置當(dāng)前的加載狀態(tài):加載中、加載失敗、空數(shù)據(jù)等//其中,加載失敗可判斷當(dāng)前是否聯(lián)網(wǎng),可現(xiàn)實(shí)無(wú)網(wǎng)絡(luò)的狀態(tài)// 屬于加載失敗狀態(tài)下的一個(gè)分支,可自行決定是否實(shí)現(xiàn)}} }

    3、 初始化Gloading的默認(rèn)Adapter

    Gloading.initDefault(new GlobalAdapter());

    注:可以用AutoRegister在Gloading類裝載進(jìn)虛擬機(jī)時(shí)自動(dòng)完成初始化注冊(cè),無(wú)需在app層執(zhí)行注冊(cè),耦合度更低

    4、在需要使用LoadingView的地方獲取Holder

    //在Activity中顯示, 父容器為: android.R.id.content Gloading.Holder holder = Gloading.getDefault().wrap(activity);//傳遞點(diǎn)擊重試需要執(zhí)行的task,該task在Adapter中用holder.getRetryTask()獲取 Gloading.Holder holder = Gloading.getDefault().wrap(activity).withRetry(retryTask);//傳遞點(diǎn)擊重試需要執(zhí)行的task和一個(gè)任意類型的擴(kuò)展參數(shù),該參數(shù)在Adapter中用holder.getData()獲取 Gloading.Holder holder = Gloading.getDefault().wrap(activity).withRetry(retryTask).withData(obj);

    or

    //為某個(gè)View顯示加載狀態(tài) //Gloading會(huì)自動(dòng)創(chuàng)建一個(gè)FrameLayout,將view包裹起來(lái),LoadingView也顯示在其中 Gloading.Holder holder = Gloading.getDefault().wrap(view);//傳遞點(diǎn)擊重試需要執(zhí)行的task,該task在Adapter中用holder.getRetryTask()獲取 Gloading.Holder holder = Gloading.getDefault().wrap(view).withRetry(retryTask);//傳遞點(diǎn)擊重試需要執(zhí)行的task和一個(gè)任意類型的擴(kuò)展參數(shù),該參數(shù)在Adapter中用holder.getData()獲取 Gloading.Holder holder = Gloading.getDefault().wrap(view).withRetry(retryTask).withData(obj);

    5、 使用Holder來(lái)顯示各種加載狀態(tài)

    //顯示加載中的狀態(tài),通常是顯示一個(gè)加載動(dòng)畫 holder.showLoading() //顯示加載成功狀態(tài)(一般是隱藏LoadingView) holder.showLoadSuccess()//顯示加載失敗狀態(tài) holder.showFailed()//數(shù)據(jù)加載完成,但數(shù)據(jù)為空 holder.showEmpty()//如果以上默認(rèn)提供的狀態(tài)不能滿足使用,可使用此方法調(diào)用其它狀態(tài) holder.showLoadingStatus(status)

    更多API詳情請(qǐng)查看 Gloading JavaDocs

    更多Demo示例代碼請(qǐng)查看 Gloading Demo, 也可下載Demo apk體驗(yàn)

    6、封裝到BaseActivity/BaseFragment中

    • 讓BaseActivity和BaseFragment的子類中使用LoadingView更方便
    • 子類中使用LoadingView的業(yè)務(wù)邏輯與實(shí)現(xiàn)分離
    • 如果原來(lái)就是封裝到BaseActivity/BaseFragment中的,那么可以無(wú)縫切換到Gloading
    • 如果以后需要將Gloading移除替換成其它實(shí)現(xiàn),也無(wú)需修改業(yè)務(wù)代碼

    示例代碼如下:

    public abstract class BaseActivity extends Activity {protected Gloading.Holder mHolder;/*** make a Gloading.Holder wrap with current activity by default* override this method in subclass to do special initialization* @see SpecialActivity*/protected void initLoadingStatusViewIfNeed() {if (mHolder == null) {//bind status view to activity root view by defaultmHolder = Gloading.getDefault().wrap(this).withRetry(new Runnable() {@Overridepublic void run() {onLoadRetry();}});}}protected void onLoadRetry() {// override this method in subclass to do retry task}public void showLoading() {initLoadingStatusViewIfNeed();mHolder.showLoading();}public void showLoadSuccess() {initLoadingStatusViewIfNeed();mHolder.showLoadSuccess();}public void showLoadFailed() {initLoadingStatusViewIfNeed();mHolder.showLoadFailed();}public void showEmpty() {initLoadingStatusViewIfNeed();mHolder.showEmpty();}}

    7、 兼容多App場(chǎng)景下的頁(yè)面、View的復(fù)用

    每個(gè)App的LoadingView可能會(huì)不同,只需為每個(gè)App提供不同的Adapter,不同App調(diào)用不同的Gloading.initDefault(new GlobalAdapter());,具體頁(yè)面中的使用代碼無(wú)需改動(dòng)。

    注:如果使用AutoRegister,則只需在不同App中創(chuàng)建各自的 Adapter實(shí)現(xiàn)類即可,無(wú)需手動(dòng)注冊(cè)。只需改動(dòng)2處gradle文件即可:

    • 修改根目錄build.gradle,添加對(duì)AutoRegister的依賴
    buildscript {//...dependencies {//...classpath 'com.billy.android:autoregister:使用最新版'} }
    • 修改主application module下的build.gradle,添加如下代碼即可實(shí)現(xiàn)Adapter的自動(dòng)注冊(cè)
    apply plugin: 'auto-register' autoregister {registerInfo = [['scanInterface' : 'com.billy.android.loading.Gloading$Adapter', 'codeInsertToClassName' : 'com.billy.android.loading.Gloading', 'registerMethodName' : 'initDefault']] }

    演示

    為View添加加載狀態(tài)

    總結(jié)

    本文介紹了全局LoadingView在實(shí)際使用過(guò)程中可能存在的一些耦合情況,并指出了由此會(huì)影響多個(gè)App的LoadingView的UI風(fēng)格不一致導(dǎo)致頁(yè)面難以復(fù)用的問(wèn)題,同時(shí)給出了解決思路。

    另外,本文著重介紹了如何使用Gloading來(lái)輕松實(shí)現(xiàn)低耦合的全局LoadingView,喜歡的同學(xué)請(qǐng)順手甩個(gè)star支持一下 :)

    總結(jié)

    以上是生活随笔為你收集整理的android loading封装_我们经常用的Loading动画居然还有这种姿势的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。