轉載請注明出處:
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);
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();
自定義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在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提出