LoadingBar - 如何更优雅的使用Loading

版权声明:欢迎关注我的Github:https://github.com/dengyuhan,转载请注明出处,欢迎转载 https://blog.csdn.net/aa464971/article/details/70197394

转载请注明出处:
http://blog.csdn.net/aa464971/article/details/70197394
*本篇文章已授权微信公众号 __guolin_blog (郭霖)__独家发布

Github地址:
https://github.com/dengyuhan/LoadingBar

前言

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

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();

LoadingDialog

自定义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.release();

源码解析

定义结构

首先我一开始就想好了,这得有三样东西,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提出

阅读更多

没有更多推荐了,返回首页