Android

iOS转android之Loading

2019-01-09  本文已影响113人  移动端_小刚哥

前言

无论iOS还是Android进行网络请求时Loading都是不可或缺的控件,使用频率也是很高的,如果操作不当出现问题的可能性也就比较大,例如一个页面有多个网络请求那么这些网络请求对Loading的操作是容易出现冲突,见图片

Loading常见问题

一、Android的Loading调研

根据iOS的使用习惯,Loading应该是一个单例类,使用KeyWindow控制其显示和隐藏,loading和Controller是没有任何依赖关系的,View或者Model都可以很容易控制Loading的显示和隐藏,那么这种用法就比较容易实现Loading和网络封装的结合,开始网络请求时显示Loading,结束时隐藏Loading,但是经过两天的调研我发现Android中很难实现这种效果,那个大佬可以实现请告诉我。为什么Android实现不了呢,因为Android中每一个Activity都相当于一个Window,没有全局的KeyWindow(以我目前理解,不对麻烦纠正谢谢🙏)
Window 有三种类型

如果想要实现显示在所有的Window上边的Loading就只能使用系统Window,但是我在了解过程中发现没有使用系统Window来显示Loading的,我申请权限没有成功,所以也没有尝试。
那么Android中实现Loading的方法就要依赖BaseActivity来实现,在基类中添加Loading,在子类中控制显示和消失

二、实现

在BaseActivity的布局文件中添加Loading控件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    tools:context=".base.activity.BaseActivity"
    android:id="@+id/main_root_layout"
    >


<!--基类中其他布局-->
    
    <!--Loading-->
    <RelativeLayout
        android:id="@+id/rl_progress_bar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#00000000"
        android:visibility="gone">

        <ProgressBar
            android:id="@+id/progress_bar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true" />

    </RelativeLayout>
</RelativeLayout>
    private Handler loadingHandler;
    private RelativeLayout mRelativeLayoutLoading;

    /**
     * 显示和隐藏loadingView
     * @param isShow true显示loading false隐藏loadingview
     */
    public void showLoadingView(boolean isShow){
        if (isShow){ //显示loading
            loadingHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mRelativeLayoutLoading.bringToFront();
                    mRelativeLayoutLoading.setVisibility(View.VISIBLE);
                }
            },1);
        }else {//隐藏laoding
            loadingHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mRelativeLayoutLoading.bringToFront();
                    mRelativeLayoutLoading.setVisibility(View.GONE);
                }
            },1);
        }
    }

在BaseActivity的子类中使用也很简单

        showLoadingView(true); //显示loading
        showLoadingView(false); //隐藏loading

三、结合网络请求封装使用

在之前封装好的网络方法中添加两个参数

    /**
     *
     * @param url 地址
     * @param paraDic 参数
     * @param method post/get
     * @param baseActivity 请求网络请求的context
     * @param isLoading 是否显示loading
     * @param <T>
     */
    public static <T extends BaseDao> void request( final String url, final HashMap<String,Object> paraDic, String method, final BaseActivity baseActivity, final boolean isLoading){

        if (isLoading){
                    baseActivity.showLoadingView(true); //显示loading
                }
        call.enqueue(new Callback() {

            @Override
            public void onFailure( Call call, IOException e ) {
                    baseActivity.showLoadingView(false); //隐藏loading
            }


            @Override
            public void onResponse( Call call, Response response ) throws IOException {
                    baseActivity.showLoadingView(false); //隐藏loading
    }

四、问题解决方案

Loading常见问题

一个Activity中有多个网络请求方法对Loading的显示是有影响的,那么怎么解决这个问题呢?在iOS开发中有一个很重要的概念——引用计数,每一个对象都有一个引用计数,被持有一次引用计数+1,被释放一次引用计数-1,直到处于可被回收的状态(引用计数为0并不一定马上被销毁),在iOS中一个app只有一个Loading的单例对象,声明一个数字num,Loading被启动一次num += 1,loading被停止一次num -= 1,如果num == 0那么loading停止。

Android中没有全局的Loading对象,而是每一个activity对象中有一个Loading对象,那么我们将每一个activity对象都像iOS那样处理一下

      /**
     * loading队列对象类
     */
    private static class  LoadingCountDownManager{
        private BaseActivity activity; //进行网络请求的activity对象
        private int countDown = 0; //当前activity的loading引用计数,如果>0那么显示loading,否则不显示loading


        public int getCountDown() { //获取当前activity对象的loading引用计数值
            return countDown;
        }

        /**
         * 赋值
         * @param activity
         */
        public void setActivity( BaseActivity activity ) {
            this.activity = activity;
        }

        public BaseActivity getActivity() {
            return activity;
        }

        /**
         * 开始或者结束loading
         * @param isAdd 
         * true当前activity对象开始了一次Loading
         * true当前activity对象结束了一次Loading
         */
        public void addOne( boolean isAdd){
            if (isAdd){
                countDown += 1;
            }else {
                countDown -= 1;
                if (countDown < 0){
                    countDown = 0;
                }
            }

            if (activity != null){
                if (countDown > 0){ //当前activity显示loading
                    activity.showLoadingView(true);
                }else { //当前activity不显示loading
                    activity.showLoadingView(false);
                }
            }
        }
    }

声明队列

    //loading队列
    private static ArrayList<LoadingCountDownManager> loadingArrayList = new ArrayList<LoadingCountDownManager>();
      /**
     * 为了解决同一个页面多个网络请求对loading显示效果的影响,使用引用计数的方法来管理每个Activity中的loading
     * @param activity
     * @param isBeginLoading
     */
    private static void loadingManager( BaseActivity activity, boolean isBeginLoading ){

        if (activity != null){
            int exitIndex = -1; //如果当前activity已经存在于loading队列中,那么获取其位置
            for (int i=0; i<loadingArrayList.size(); i++){
                if (loadingArrayList.get(i).getActivity() != null){
                    if (loadingArrayList.get(i).getActivity().getClass().getName() == activity.getClass().getName()){
                        loadingArrayList.get(i).addOne(isBeginLoading);
                        exitIndex = I;
                    }
                }
            }

            if (exitIndex >= 0){
                if (loadingArrayList.get(exitIndex).getCountDown() == 0){ //对应activity的loading要消失
                    loadingArrayList.remove(exitIndex); //将没有loading的activity移除队列
                }
            }else {//当前activity是第一次开始loading,需要插入队列
                LoadingCountDownManager loadingCountDownManager = new LoadingCountDownManager();
                loadingCountDownManager.setActivity(activity);
                loadingCountDownManager.addOne(true);
                loadingArrayList.add(loadingCountDownManager);
            }
        }
    }

使用方法

     /**
     *
     * @param url 地址
     * @param paraDic 参数
     * @param method post/get
     * @param baseActivity 请求网络请求的context
     * @param isLoading 是否显示loading
     * @param <T>
     */
    public static <T extends BaseDao> void request( final String url, final HashMap<String,Object> paraDic, String method, final BaseActivity baseActivity, final boolean isLoading){

              if (isLoading){
                    loadingManager(baseActivity,true); //显示loading
                }

        call.enqueue(new Callback() {

            @Override
            public void onFailure( Call call, IOException e ) {
                     loadingManager(baseActivity,false); //隐藏loading
            }


            @Override
            public void onResponse( Call call, Response response ) throws IOException {
                    loadingManager(baseActivity,false); //隐藏loading
    }

参考文章
https://blog.csdn.net/yhaolpz/article/details/68936932
https://www.jianshu.com/p/66026e9aa734
https://blog.csdn.net/dr_abandon/article/details/52493917
https://blog.csdn.net/android_cmos/article/details/73382573

上一篇下一篇

猜你喜欢

热点阅读