如何抽取一个通用的Android Loading页面快速实现加载功能

抽取一个通用的Android的Loading页面

最近好懒,这篇博文端午之前就写好了demo,但是就是懒得更博,对自己的这种状态我也好无奈呀!话不多说,我们开始啦!

我们知道,在Android应用程序开发过程中,我们有很多页面需要去请求网络数据,请求网络是一个耗时操作,这时,一般我们不允许用户进行其它操作。为了解决用户在等待中出现不耐烦或者误因为应用假死的情况出现,我们这时需要在请求网络的过程中,显示一个页面提示用户当前的网络请求状态的页面,通常是一个圆形进度条或者比较可爱的卡通动画的一个Loading页面。这样,有助于提升用户体验。那么下面,我们来讨论一下,如何抽取一个通用的Loading页。

我们将从以下几方面进行讨论:

  • 分析网络请求的各种状态
  • 创建不同状态下对应的页面
  • 抽取加载网络的抽象方法
  • 抽取请求网络成功时构建成功页面的抽象方法
  • 执行顺序分析及实际开发中优化提示
  • demo示例

1、分析网络请求的各种状态

一般来说,网络请求的过程,存在着很大不确定的因素影响我们最终的请求结果。作为一个成熟可商用的APP,我们必须充分考虑到所有请求过程中,可能出现的问题,并且进行相应容错、差异化处理。在网络请求的过程中,主要可能出现以下几种可能性:

1、请求成功
2、请求失败
3、请求成功,但服务器无数据可供显示
4、网络错误

请求成功,这个很好理解,成功了就需要展示相应的数据。请求失败时,从上面的分析可以看到,我把它分又为了三种状态,我们先讲请求成功,但是服务器无数据显示的情况,这时,我们需要明确地告诉用户,当前是请求成功的,但是没有内容可以显示,无需继续进行请求操作了;网络错误,可能是用户没有把数据打开,或是wifi没有连接时,我们需要提醒用户打开数据功能或者连接一个可用的wifi热点,然后重新刷新页面请求数据;剩下的情况我们把它归结为一类请求失败,这时,可能是因为网络质量差、服务器返回一个异常的code码、又或者连接了一个无Internet网络连接的一个wifi热点,我们这时,可用给一个比较友好的提示告知当前的状态,并且,允许用户进行刷新操作重试。

所以,我们先创建一个Java类,我把它叫做LoadingFrame。首先我们分析一下这个类的写法,我们知道,一个Activity的页面内容填充,是通过setContentView,传递一个View对象用于显示的,最终,我们是要将我们抽取的这个类,作为一个View,显示到Activity中的,那么我们的LoadingFrame这个类,必须就是一个View对象。我们可以考虑继承自View或者View的之类对象,后期我们使用时,我们很容易就直接将我们创建的LoadingFrame对象作为一个View设置给contentView显示。那么我们根据上面的分析,考虑到,这个类,需要显示几种状态,每种状态对应的页面效果也是不一样的,我们可以考虑让LoadingFrame继承ViewGroup或者其子类,我们这里选用让它继承一个比较简单的FrameLayout,一帧一帧地居中显示。至于为什么要写成抽象类,后面你就知道了。

然后我们可以讲刚刚我们分析的这几种状态值给定义成常量,并且创建一个成员变量,记录当前的状态(currentState)

代码如下:

public abstract class LoadingFrame extends FrameLayout {

    private  Context mContext;

    private static final int LOADING = 1;//加载中
    private static final int LOADERROR = 2;//加载失败
    private static final int NETERROR = 3;//网络错误
    private static final int LOADED = 4;//加载完成
    private static final int NODATA = 5;//无数据可显示

    //当前的状态值,用于记录当前网络请求的状态
    private int currentState = LOADING;

    public LoadingFrame(Context context) {
        super(context);
        this.mContext = context;
    }

    public LoadingFrame(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
    }

    public LoadingFrame(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
    }
}

上面,我通过分析,定义了5种不同的状态值,并且记录了当前的状态初始化为加载中,重写了三个构造方法用于结束context上下文对象。

那么现在,我们就来构建这几个状态对应的不同的页面吧。


2、创建不同状态下对应的页面

通过上面的分析,我们很容易地写出其对应的页面,那么,我们开始吧,首先,我们构建一个加载中的页面。

先看代码:

private void createLoadingView() {
        mlinearLayoutLoading  = new LinearLayout(mContext);
        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
        linearLayoutParams.gravity = Gravity.CENTER;
        mlinearLayoutLoading.setOrientation(LinearLayout.VERTICAL);

        loadingView = new ImageView(mContext);
        loadingView.setImageResource(R.drawable.loading);
        AnimationDrawable animationDrawable = (AnimationDrawable) loadingView.getDrawable();
        animationDrawable.start();
        mlinearLayoutLoading.addView(loadingView,linearLayoutParams);
        TextView textView = new TextView(mContext);
        textView.setText("正在加载中");
        mlinearLayoutLoading.addView(textView,linearLayoutParams);
        mlinearLayoutLoading.setVisibility(View.GONE);
    }

看看最终页面效果:
这里写图片描述
我们可以看到,布局非常简单,上面一个ImageView,下面有个TextView构成的一个页面。由于布局过于简单时,我习惯通过代码来写布局,读者见谅。所以,我新建了一个LinearLayout,往这个线性布局里面加了ImageView和TextView两个子View,并且设置垂直居中显示。

上面的ImageView其实是一个动画,实际效果是一个卡通人物在拼命奔跑的帧动画。在这里,还要感谢我亲爱的老婆大人,为我本次更博,熬夜绘制了几个非常可爱的卡通(不加这句,回去要跪搓衣板!)。动画我用xml定义出来了,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
                android:oneshot="false"
    >
    <item
        android:drawable="@drawable/loading1"
        android:duration="200"></item>
    <item
        android:drawable="@drawable/loading2"
        android:duration="200"></item>
</animation-list>

读者可以看到,非常简单,就是一个两帧重复无限循环的帧动画。读者们如果想尝试写,可以不必用动画,毕竟,在公司,有UI设计师提供,不是每一个程序员都有一个UI设计的老婆的,我是幸运的!

加载失败、网络错误、无数据的页面布局与加载中几乎一致,仅仅是换了张图片和文字而已,我就不做分析,直接贴出相应的代码

无数据

private void createNoDataView() {
        mlinearLayoutNoData  = new LinearLayout(mContext);
        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
        linearLayoutParams.gravity = Gravity.CENTER;
        mlinearLayoutNoData.setOrientation(LinearLayout.VERTICAL);

        noDataView = new ImageView(mContext);
        noDataView.setImageResource(R.drawable.nodata);
        mlinearLayoutNoData.addView(noDataView,linearLayoutParams);
        TextView textView = new TextView(mContext);
        textView.setText("没有数据可供显示!");
        mlinearLayoutNoData.addView(textView,linearLayoutParams);
        mlinearLayoutNoData.setVisibility(View.GONE);
    }

加载错误

private void createLoadedErrorView() {
        mlinearLayoutLoadError  = new LinearLayout(mContext);
        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
        linearLayoutParams.gravity = Gravity.CENTER;
        mlinearLayoutLoadError.setOrientation(LinearLayout.VERTICAL);

        noDataView = new ImageView(mContext);
        noDataView.setImageResource(R.drawable.nodata);
        mlinearLayoutLoadError.addView(noDataView,linearLayoutParams);
        TextView textView = new TextView(mContext);
        textView.setText("加载失败!点击重试");
        textView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //点击操作的响应
            }
        });
        mlinearLayoutLoadError.addView(textView,linearLayoutParams);
        mlinearLayoutLoadError.setVisibility(View.GONE);
    }

无网络

private void createNetErrorView() {
        mlinearLayoutNetError  = new LinearLayout(mContext);
        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
        linearLayoutParams.gravity = Gravity.CENTER;
        mlinearLayoutNetError.setOrientation(LinearLayout.VERTICAL);

        netErrorView = new ImageView(mContext);
        netErrorView.setImageResource(R.drawable.net_error);

        mlinearLayoutNetError.addView(netErrorView,linearLayoutParams);
        TextView textView = new TextView(mContext);
        textView.setText("网络错误,检查您的网络或点击重试");
        textView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //点击操作的响应
            }
        });

需要指出的是,按照我们一开始的分析,无网络和请求失败时,我们需要提供重新请求网络的操作,所以我们在TextView上面,设置了一个点击监听。以上,我们就讲完了页面的改造啦!逻辑清晰的读者会说,等等,还有一个加载成功的页面呢,为什么就讲完了?
不要着急,下面会单独讲。
我们可以提供一个createView方法,在构造方法里面就调用一次,把这上面几个View创建出来,如下:

private void createView() {
        params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
        params.gravity = Gravity.CENTER;

        createLoadingView();

        createNoDataView();

        createNetErrorView();

        createLoadedErrorView();

        addView(mlinearLayoutLoading, params);
        addView(mlinearLayoutNoData, params);
        addView(mlinearLayoutNetError, params);
        addView(mlinearLayoutLoadError, params);
        refreshView();
    }

在代码的后面,我调用refreshView()方法,其实这个方法,就是根据当前的状态去刷新界面的方法:

private void refreshView() {
        mlinearLayoutLoading.setVisibility(currentState == LOADING ? View.VISIBLE : View.GONE);
        mlinearLayoutNoData.setVisibility(currentState == NODATA ? View.VISIBLE : View.GONE);
        mlinearLayoutNetError.setVisibility(currentState == NETERROR ? View.VISIBLE : View.GONE);
        mlinearLayoutLoadError.setVisibility(currentState == LOADERROR ? View.VISIBLE : View.GONE);
    }

3、抽取加载网络的抽象方法

在讲加载成功页面的创建之前,我们需要先知道,本次网络请求的状态到底是什么?我们可以想想,每个页面请求网络的URL地址都是不一致的,我们在这个抽象类里面还不能确定请求的地址,所以我们需要有一个抽象方法,让子类去具体实现请求网络的操作,这就是一开始我们把这个类定义成为抽象类的原因啦!这个方法,我们只关注返回值的结果,这里,我让它直接返回的int类型的code码,纯粹为了方便而且。实际开发中,我们应该限制这个返回值的类型,比如定义一个枚举类,这个读者自行实现。
我们知道,请求返回的code常见的有200、201、404等等,每种code码有其特定的含义,在公司开发中,服务端同学也会定义一些具有特定含义的code码,不同的公司有不一样的规则定义,具体可根据协议文档来写。
所以我们定义一个加载数据的方法,这里我们调用我们刚刚定义的onLoad这个抽象方法,然后根据code来维护currentState这个状态值。

private void initData() {
        currentState = LOADING;
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(3000);
                int code = onLoad();
                if (code == 200) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = LOADED;
                            //加载成功的逻辑
                        }
                    });
                } else if (code == 201) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = NODATA;
                            //无的逻辑
                        }
                    });

                }else if (code == 404) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = LOADERROR;
                            //加载失败的逻辑
                        }
                    });

                } else if (code == -1) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = NETERROR;
                            //无网络的逻辑
                        }
                    });

                }
            }
        }).start();
    }

因为网络请求是耗时操作,不能放在UI线程中进行,不然会报错,所以我们开了一个线程,在调用onLoad方法之前,还给了3秒的阻塞时间,用于模拟请求网络的过程。然后后面,根据onLoad的返回结果,分别处理,这是,涉及到界面UI的操作,必须在主线程中进行,所以我们有回到主线程中处理UI问题,并给currentState 赋值。


4、抽取请求网络成功时构建成功页面的抽象方法

在上面initData方法中,code码为200时,我们需要创建一个请求成功正常显示的页面,当然,这个页面也是无法在此时就确定下来的,所以我们也要提供一个抽象方法比如叫onSuccessView(),返回类型是一个View对象。当请求成功时,我们可以调用这个方法,为加载成功的页面对象赋值为这个返回值。所以我们定义一个成员变量successView,当请求成功时,我们就可以调用这个方法为successView赋值了。所以,initData里面的200分支的判断逻辑可以这样写:

currentState = LOADED;
                            successView = onSuccessView();
                            addView(successView, params);
                            refreshView();

注意,后面我们又调用了一次refreshView方法刷新当前页面,但是,我们前面并没有对状态为LOADED做刷新,我们需要改造一下refreshView方法:

private void refreshView() {
        mlinearLayoutLoading.setVisibility(currentState == LOADING ? View.VISIBLE : View.GONE);
        mlinearLayoutNoData.setVisibility(currentState == NODATA ? View.VISIBLE : View.GONE);
        mlinearLayoutNetError.setVisibility(currentState == NETERROR ? View.VISIBLE : View.GONE);
        mlinearLayoutLoadError.setVisibility(currentState == LOADERROR ? View.VISIBLE : View.GONE);
        if (successView != null) {
            successView.setVisibility(currentState == LOADED ? View.VISIBLE : View.GONE);
        }
    }

这里的这个非空判断为什么要加呢?因为这个方法,我们在构造器里面了调用了一次,这时,加载成功的View还没有被创建出来呢,所以这里需要做个非空判断。我们可以想到,是不是所有的分支后面都需要refreshView呢,所以我们干脆这样,把这个refreshView直接放在后面,这句话必定要执行的,所以initData这个方法最终写成这样:

private void initData() {
        currentState = LOADING;
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(3000);
                int code = onLoad();
                if (code == 200) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = LOADED;
                            successView = onSuccessView();
                            addView(successView, params);
                        }
                    });
                } else if (code == 201) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = NODATA;
                        }
                    });

                }else if (code == 404) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = LOADERROR;
                        }
                    });

                } else if (code == -1) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = NETERROR;
                        }
                    });
                }
                refreshView();
            }
        }).start();
    }

到这里,我们就完成了几乎全部的代码编写任务,接下来,我们只需要,将执行顺序捋清楚就可以了。


5、执行顺序分析及实际开发中优化提示

我们知道,假如不考虑静态代码块的话,Java一开始执行的方法就是构造方法,所以我们在构造方法中就调用了createView方法,接着createView方法里面构建了4种状态的View界面,最后调用了一次刷新方法初始化显示View。到这里,前面我们的代码调用就断开了,View创建好了,什么时候赋值呢?所以,我们可以提供一个show()方法,show方法里面就只调用initData方法。

public void show() {
        initData();
    }

这个方法我们给public权限,在要显示界面的时候,再用LoadingFrame的对象去调用。这时候,我们就整个串起来了,只有调用show方法的时候,显示页面并且去请求网络,最后根据请求结果,再刷新一次页面。到这里,我们不要忘记了,我们在加载失败和网络错误时的两个点击事件还没有实现呢!其实很简单,我们只需要把状态查询赋值无加载中,并且调用一次show()方法和refreshView()方法就可以了。这时候,我们才终于写完了这个类,就可以写一个demo测试一下啦。
不过我们最后再测试,这里,我们再谈几点优化的问题,因为在实际开发过程中,框架写成这样,随随便便new Thread,会被别人骂死的。我们可以做以下改造

1、考虑将initData方法改造成为用Asynctask来实现;
2、文本提示提供set方法设置;
3、加强兼容性,比如提供离线页面等;
4、数据从有到无或者从无到有的方法设置;
5、refreshView中只是把View的可见性设置为GONE,但是实际View对象还是存在于内存中的,我们可以考虑直接removeView方法来代替。
6、限定onLoad的返回结果;
7、优化之路永无止境……

最后贴出完整代码:


public abstract class LoadingFrame extends FrameLayout {

    private  Context mContext;

    private static final int LOADING = 1;
    private static final int LOADERROR = 2;
    private static final int NETERROR = 3;
    private static final int LOADED = 4;
    private static final int NODATA = 5;

    private ImageView loadingView;
    private LinearLayout mlinearLayoutLoading;
    private ImageView noDataView;
    private LinearLayout mlinearLayoutNoData;
    private LinearLayout mlinearLayoutLoadError;
    private ImageView netErrorView;
    private LinearLayout mlinearLayoutNetError;
    private View successView;

    private int currentState = LOADING;
    private FrameLayout.LayoutParams params;

    public LoadingFrame(Context context) {
        super(context);
        this.mContext = context;
        createView();
    }

    public LoadingFrame(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        createView();
    }

    public LoadingFrame(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void initData() {
        currentState = LOADING;
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(3000);
                int code = onLoad();
                if (code == 200) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = LOADED;
                            successView = onSuccessView();
                            addView(successView, params);
                        }
                    });
                } else if (code == 201) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = NODATA;
                        }
                    });

                }else if (code == 404) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = LOADERROR;
                        }
                    });

                } else if (code == -1) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            currentState = NETERROR;
                        }
                    });
                }
                refreshView();
            }
        }).start();
    }

    private void refreshView() {
        mlinearLayoutLoading.setVisibility(currentState == LOADING ? View.VISIBLE : View.GONE);
        mlinearLayoutNoData.setVisibility(currentState == NODATA ? View.VISIBLE : View.GONE);
        mlinearLayoutNetError.setVisibility(currentState == NETERROR ? View.VISIBLE : View.GONE);
        mlinearLayoutLoadError.setVisibility(currentState == LOADERROR ? View.VISIBLE : View.GONE);
        if (successView != null) {
            successView.setVisibility(currentState == LOADED ? View.VISIBLE : View.GONE);
        }
    }

    public void show() {
        initData();
    }

    private void createView() {
        params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
        params.gravity = Gravity.CENTER;

        createLoadingView();

        createNoDataView();

        createNetErrorView();

        createLoadedErrorView();

        addView(mlinearLayoutLoading, params);
        addView(mlinearLayoutNoData, params);
        addView(mlinearLayoutNetError, params);
        addView(mlinearLayoutLoadError, params);
        refreshView();
    }

    private void createNetErrorView() {
        mlinearLayoutNetError  = new LinearLayout(mContext);
        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
        linearLayoutParams.gravity = Gravity.CENTER;
        mlinearLayoutNetError.setOrientation(LinearLayout.VERTICAL);

        netErrorView = new ImageView(mContext);
        netErrorView.setImageResource(R.drawable.net_error);

        mlinearLayoutNetError.addView(netErrorView,linearLayoutParams);
        TextView textView = new TextView(mContext);
        textView.setText("网络错误,检查您的网络或点击重试");
        textView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                currentState = LOADING;
                show();
                refreshView();
            }
        });

        mlinearLayoutNetError.addView(textView,linearLayoutParams);

        mlinearLayoutNetError.setVisibility(View.GONE);
    }

    private void createNoDataView() {
        mlinearLayoutNoData  = new LinearLayout(mContext);
        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
        linearLayoutParams.gravity = Gravity.CENTER;
        mlinearLayoutNoData.setOrientation(LinearLayout.VERTICAL);

        noDataView = new ImageView(mContext);
        noDataView.setImageResource(R.drawable.nodata);
        mlinearLayoutNoData.addView(noDataView,linearLayoutParams);
        TextView textView = new TextView(mContext);
        textView.setText("没有数据可供显示!");
        mlinearLayoutNoData.addView(textView,linearLayoutParams);
        mlinearLayoutNoData.setVisibility(View.GONE);
    }
    private void createLoadedErrorView() {
        mlinearLayoutLoadError  = new LinearLayout(mContext);
        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
        linearLayoutParams.gravity = Gravity.CENTER;
        mlinearLayoutLoadError.setOrientation(LinearLayout.VERTICAL);

        noDataView = new ImageView(mContext);
        noDataView.setImageResource(R.drawable.nodata);
        mlinearLayoutLoadError.addView(noDataView,linearLayoutParams);
        TextView textView = new TextView(mContext);
        textView.setText("加载失败!点击重试");
        textView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                currentState = LOADING;
                show();
                refreshView();
            }
        });
        mlinearLayoutLoadError.addView(textView,linearLayoutParams);
        mlinearLayoutLoadError.setVisibility(View.GONE);
    }

    private void createLoadingView() {
        mlinearLayoutLoading  = new LinearLayout(mContext);
        LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
        linearLayoutParams.gravity = Gravity.CENTER;
        mlinearLayoutLoading.setOrientation(LinearLayout.VERTICAL);

        loadingView = new ImageView(mContext);
        loadingView.setImageResource(R.drawable.loading);
        AnimationDrawable animationDrawable = (AnimationDrawable) loadingView.getDrawable();
        animationDrawable.start();
        mlinearLayoutLoading.addView(loadingView,linearLayoutParams);
        TextView textView = new TextView(mContext);
        textView.setText("正在加载中");
        mlinearLayoutLoading.addView(textView,linearLayoutParams);
        mlinearLayoutLoading.setVisibility(View.GONE);
    }

    public abstract View onSuccessView();

    public abstract int onLoad();


}

6、demo示例

这里写图片描述

demo老规矩,代码不贴,仅看效果图!谢谢阅读!

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值