天天看點

LoadingBar - 如何更優雅的使用Loading

轉載請注明出處:

http://blog.csdn.net/aa464971/article/details/70197394

*本篇文章已授權微信公衆号 __guolin_blog (郭霖)__獨家釋出

Github位址:

https://github.com/xiandanin/LoadingBar

LoadingBar已更新2.0,檢視最新文檔

前言

Loading是很普遍的需求,比如請求的時候需要顯示Loading,請求完成以後再取消Loading,而一般的實作方式是在布局xml裡添加一個ProgressBar,但是這樣寫就有很多不便,每個頁面的layout都要寫一個ProgressBar,顯示的位置也固定了,還耦合了很多代碼。

而LoadingBar就是為了跟友善的操作Loading而生,高度解耦,樣式全部通過工廠類決定。

結構介紹

LoadingBar - 适合一些顯示資料的操作,比如請求清單

LoadingDialog - 适合一些送出資料的操作,比如注冊,登入

Factory - 決定了loading的樣式,自定義樣式隻需實作Factory

快速開始

Android Studio - 在build.gradle中引入

compile 'com.dyhdyh.loadingbar:loadingbar:1.4.7'

//appcompat
implementation "com.android.support:appcompat-v7:xxx"
           

LoadingBar

//預設樣式 loading将會覆寫在parent的内容上面
LoadingBar.make(parent).show();

//自定義樣式
//提供兩種形式,loadingView更簡便,loadingFactory自由度更高
LoadingBar.make(parent,loadingView).show();
LoadingBar.make(parent,loadingFactory).show();

//完全自定義
LoadingBar.make(parent,loadingFactory)
        .setOnClickListener(clickListener)//點選事件
        .setOnLoadingBarListener(loadingBarListener)//當loadingbar取消的時候回調
        .show();
        
//取消Loading
LoadingBar.cancel(parent);
           
LoadingBar - 如何更優雅的使用Loading

LoadingDialog

//預設樣式
LoadingDialog.make(context).show();

//自定義樣式
LoadingDialog.make(context, dialogFactory).show();

//完全自定義
LoadingDialog.make(context, dialogFactory)
           .setMessage(message)//提示消息
           .setCancelable(cancelable)
           .show();

//設定更多屬性
Dialog dialog = LoadingDialog.make(context, dialogFactory)
           .setMessage(message)//提示消息
           .setCancelable(cancelable)
           .create();
dialog.setOnCancelListener(cancelListener);
dialog.set...
dialog.show();
           
//取消Loading
LoadingDialog.cancel();
           
LoadingBar - 如何更優雅的使用Loading

自定義Factory

public class CustomLoadingFactory implements LoadingFactory {

    @Override
    public View onCreateView(ViewGroup parent) {
        View loadingView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_custom, parent,false);
        return loadingView;
    }
}
           

全局配置

//自定義樣式并應用于全局
LoadingConfig.setFactory(loadingFactory,dialogFactory);

           
LoadingBar - 如何更優雅的使用Loading

資源釋放

其實LoadingBar在cancel的時候已經釋放掉了,可以不用手動釋放,但是這裡也提供釋放的方法,根據自己需要選擇

在Activity onDestroy調用,個人建議在BaseActivity,資源釋放隻會釋放無效的資源

源碼解析

定義結構

首先我一開始就想好了,這得有三樣東西,LoadingBar與LoadingDialog,用Factory來生産loading所需要的View和Dialog

定義接口

兩者的共同點是都會有顯示,是以我定義了一個共用的接口

public interface ILoading {
    void show();
}
           

LoadingBar除了有show還得有cancel

public interface ILoadingBar extends ILoading {
    void cancel();
}
           

LoadingDialog最終都是操作Dialog,是以它得有create,再附加一些設定常用屬性的方法

public interface ILoadingDialog extends ILoading {
   
    Dialog create();

    ILoadingDialog setCancelable(boolean flag);

    ILoadingDialog setMessage(CharSequence message);
}
           

LoadingFactory onCreateView傳回的View就決定了Loading長什麼樣

public interface LoadingFactory {

    View onCreateView(ViewGroup parent);
}
           

DialogFactory 主要是onCreateDialog,這個方法決定了Dialog長什麼樣,在這裡實作建立Dialog

public interface DialogFactory {
    /**
     * 建立dialog
     * @param context
     * @return
     */
    Dialog onCreateDialog(Context context);

    /**
     * 設定提示消息
     * @param dialog
     * @param message
     */
    void setMessage(Dialog dialog,CharSequence message);

    /**
     * 進入退出的動畫id
     * @return
     */
    @StyleRes int getAnimateStyleId();
}
           

LoadingBar的實作

其實就是需要兩個View,mView就是factory.onCreateView傳回的LoadingView,mParent就是現實

private LoadingBar(ViewGroup parent, LoadingFactory factory) {
        mParent = parent;
        mView = factory.onCreateView(mParent);
}
           

然後把mView添加到mParent裡,這樣mView就處于最上層,覆寫着内容,這樣就達到了Loading的效果

public void show() {
        if (mView != null) {
            mView.setVisibility(View.VISIBLE);
            if (mView.getParent() != null) {
                mParent.removeView(mView);
            }
            mParent.addView(mView);
        }
    }
           

取消很簡單,就直接把mView移除掉就好了

public void cancel() {
        if (mView != null) {
            mView.setVisibility(View.GONE);
            mParent.removeView(mView);
            mView = null;
            if (this.mListener != null) {
                this.mListener.onCancel(mParent);
            }
        }
    }
           

值得一說的還有findSuitableParent,因為Loading是要在可覆寫的布局上才有作用的,而當parent傳的是非覆寫的布局(例如LinearLayout),這個方法會一直往外層尋找可覆寫的布局

private static ViewGroup findSuitableParent(View parent) {
        if (parent == null) {
            return null;
        }
        View suitableParent = parent;
        do {
            if (suitableParent instanceof FrameLayout || suitableParent instanceof RelativeLayout ||
                    "android.support.v4.widget.DrawerLayout".equals(suitableParent.getClass().getName()) ||
                    "android.support.design.widget.CoordinatorLayout".equals(suitableParent.getClass().getName()) ||
                    "android.support.v7.widget.CardView".equals(suitableParent.getClass().getName())) {
                return (ViewGroup) suitableParent;
            } else {
                final ViewParent viewParent = suitableParent.getParent();
                suitableParent = viewParent instanceof View ? (View) viewParent : null;
                return (ViewGroup) suitableParent;
            }
        } while (suitableParent != null);
    }
           

LoadingDialog的實作

構造方法先用factory建立了dialog,如果有動畫設定動畫

public LoadingDialog(Context context, DialogFactory factory) {
        this.mDialog = factory.onCreateDialog(context);
        this.mFactory = factory;
        int animateStyleId = this.mFactory.getAnimateStyleId();
        if (animateStyleId > 0) {
      this.mDialog.getWindow().setWindowAnimations(animateStyleId);
        }
    }
           

因為Dialog是單例,如果在Activity已經finish了再去操作做個Dialog的話,就會抛異常,是以在show與cancel的時候要先檢查是否能夠操作

public void show() {
        if (isValid() && !mDialog.isShowing()) {
            mDialog.show();
        }
    }

    public void cancelDialog() {
        if (isValid() && mDialog.isShowing()) {
            mDialog.cancel();
        }
    }
    
    private boolean isValid() {
        if (mDialog != null) {
            Context context = mDialog.getContext();
            if (context instanceof ContextWrapper){
                context = ((ContextWrapper) context).getBaseContext();
            }
            if (context instanceof Activity) {
                if (!((Activity) context).isFinishing()) {
                    return true;
                }
            }
        }
        return false;
    }
           

文筆不是很好,有的地方可能不是寫的很清楚,有問題可以提出,看到必回

總結

使用場景不局限于請求,其實還有很多異步操作都能用上

比如壓縮圖檔,也可以用LoadingDialog

更多玩法等你挖掘,有問題可以去Github的issue提出