自定义LoadingPage实现网络请求显示的四种情况的封装

我们在网络请求数据的时候,通常有四种情况:
1、正在加载
2、加载失败
3、加载成功,但是没有数据
4、加载成功,同时返回数据
这时候需要我们根据这四种情况显示不同的页面视图。
这里我们可以自定义一个LoadingPage类来实现这四种情况的切换。
一、新建一个LoadingPage类继承FrameLayout
1.定义4种不同的显示状态
state_loading=1;
state_error=2;
state_empty=3;
state_success=4;
state_current=1;

2.提供4种不同界面:
正在加载中
加载失败
加载成功,但是数据为空
加载成功,且有数据

3.根据state_current的值,决定显示哪个界面。(初始化时,应该显示:正在加载)

4.在类中,实现联网操作,根据联网的结果,修改state_current的值,决定显示哪个界面。

5.如果是state_current=4,需要考虑如何将数据传递给具体的Fragment.

LoadingPage.class


import android.content.Context;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;
import com.willkong.p2pclient.R;
import com.willkong.p2pclient.util.UIUtils;

/**
 * 作者: willkong on 2017/11/8.
 * 作用:加载页四种情况:1、正在加载 2、加载失败 3、加载成功,但是没有数据 4、加载成功,同时返回数据
 */
public abstract class LoadingPage extends FrameLayout{
    //1.定义4种不同的显示状态
    private static final int STATE_LOADING=1;
    private static final int STATE_ERROR=2;
    private static final int STATE_EMPTY=3;
    private static final int STATE_SUCCESS=4;
    private static int state_current=STATE_LOADING;//默认情况下,当前状态为正在加载

    //2.提供4种不同的界面
    private View view_loading;
    private View view_error;
    private View view_empty;
    private View view_success;
    private LayoutParams params;

    public LoadingPage(@NonNull Context context) {
        this(context,null);

    }

    public LoadingPage(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public LoadingPage(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    //初始化方法
    private void init() {
        //实例化view
        //1、提供布局显示的参数
        params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        if (view_loading==null){
            //2、加载布局
            view_loading = UIUtils.getView(R.layout.page_loading);
            //3、添加到当前的Fragment中
            addView(view_loading, params);
        }
        if (view_empty==null){
            //2、加载布局
            view_empty = UIUtils.getView(R.layout.page_empty);
            //3、添加到当前的Fragment中
            addView(view_empty, params);
        }
        if (view_error==null){
            //2、加载布局
            view_error = UIUtils.getView(R.layout.page_error);
            //3、添加到当前的Fragment中
            addView(view_error, params);
        }

        //3、根据state_current的值,决定显示哪个界面。(初始化时,应该显示:正在加载)
        showSafePage();
    }

    //保证如下的操作在主线程中执行的,更新界面
    private void showSafePage() {
        UIUtils.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //保证runnable中的操作在主线程中执行
                showPage();
            }
        });
    }
    //更新界面
    private void showPage() {
        //根据state_current的值,决定显示哪个view
        view_loading.setVisibility(state_current==STATE_LOADING?VISIBLE:INVISIBLE);
        view_error.setVisibility(state_current==STATE_ERROR?VISIBLE:INVISIBLE);
        view_empty.setVisibility(state_current==STATE_EMPTY?VISIBLE:INVISIBLE);

        if (view_success==null){
            view_success = UIUtils.getView(layoutId());
            addView(view_success,params);
        }
        view_success.setVisibility(state_current==STATE_SUCCESS?VISIBLE:INVISIBLE);
    }

    public abstract int layoutId();

    private ResultState resultState;
    //在show()方法中实现联网加载数据
    public void show(){

        String url = url();
        //不需要联网
        if (TextUtils.isEmpty(url)){
            resultState = ResultState.SUCCESS;
            resultState.setContent("");
            loadImage();
            return;
        }
//4.在类中,实现联网操作,根据联网的结果,修改state_current的值,决定显示哪个界面。
        AsyncHttpClient client = new AsyncHttpClient();
        client.get(url(),params(),new AsyncHttpResponseHandler(){
            @Override
            public void onSuccess(String content) {
                if (TextUtils.isEmpty(content)){
                    resultState = ResultState.EMPTY;
                    resultState.setContent("");
                }else {
                    resultState = ResultState.SUCCESS;
                    resultState.setContent(content);
                }
                loadImage();
            }

            @Override
            public void onFailure(Throwable error, String content) {
                resultState = ResultState.ERROR;
                resultState.setContent("");
                loadImage();
            }
        });
    }

    private void loadImage() {
        switch (resultState){
            case ERROR:
                state_current = STATE_ERROR;
                break;
            case EMPTY:
                state_current = STATE_EMPTY;
                break;
            case SUCCESS:
                state_current = STATE_SUCCESS;
                break;
        }
        //根据修改以后的state_current,更新视图的显示。
        showSafePage();
        //5.如果是state_current=4,需要考虑如何将数据传递给具体的Fragment.如果联网成功了,把内容回调给界面
        if (state_current==STATE_SUCCESS){
            onSuccess(resultState,view_success);
        }
    }

    protected abstract void onSuccess(ResultState resultState, View view_success);

    //提供联网的请求地址
    protected abstract String url();
    //提供联网的请求参数
    protected abstract RequestParams params();

    //提供枚举类,封装联网后的状态值和数据
    public enum ResultState{
        ERROR(2),EMPTY(3),SUCCESS(4);
        int state;
        ResultState(int state){
            this.state = state;
        }
        private String content;

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }
    }
}

BaseFragment.class


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.loopj.android.http.RequestParams;
import com.willkong.p2pclient.ui.LoadingPage;

import butterknife.ButterKnife;

/**
 * 作者: willkong on 2017/11/3.
 * 作用:HomeFragment、InvestFragment、MeFragment、MoreFragment的基类
 */
public abstract class BaseFragment extends Fragment {

    private LoadingPage loadingPage;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        loadingPage = new LoadingPage(container.getContext()){

            @Override
            public int layoutId() {
                return getLayoutId();
            }

            @Override
            protected void onSuccess(ResultState resultState, View view_success) {
                ButterKnife.bind(BaseFragment.this, view_success);
                initTitle();
                initData(resultState.getContent());
            }

            @Override
            protected String url() {
                return getUrl();
            }

            @Override
            protected RequestParams params() {
                return getParams();
            }
        };
        return loadingPage;
    }

    //为了保证loadingPage不为null
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        show();
    }

    protected abstract RequestParams getParams();

    protected abstract String getUrl();

    //初始化界面的数据
    protected abstract void initData(String content);
    //初始化title
    protected abstract void initTitle();
    //提供一个创建方法,提供布局
    public abstract int getLayoutId();


    @Override
    public void onDestroyView() {
        super.onDestroyView();
        ButterKnife.unbind(this);
    }

    //联网请求数据
    public void show(){
        loadingPage.show();
    }
}

MyApplication.class

import android.app.Application;
import android.content.Context;
import android.os.Handler;

/**
 * 作者: willkong on 2017/10/31.
 * 作用:APP全局
 */
public class MyApplication extends Application{
    //在整个应用执行过程中,需要提供的变量
    public static Context context;//需要使用的上下文对象:application实例
    public static Handler handler;//需要使用的handler
    public static Thread mainThread;//提供主线程对象
    public static int mainThreadId;//提供主线程对象的id
    @Override
    public void onCreate() {
        super.onCreate();
        context = this.getApplicationContext();
        handler = new Handler();
        mainThread = Thread.currentThread();//实例化当前Application的线程即为主线程
        mainThreadId = android.os.Process.myTid();//获取当前线程的id
        //设置未捕获异常的处理器
//        CrashHandler.getInstance().init();
    }
}

UIUtils.class


import android.content.Context;
import android.os.Handler;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.widget.Toast;

import com.willkong.p2pclient.common.MyApplication;

/**
 * 作者: willkong on 2017/10/31.
 * 作用:专门提供为处理一些UI相关的问题而创建的工具类,
 *      提供资源获取的通用方法,避免每次都写重复的代码获取结果。
 */
public class UIUtils {
    public static Context getContext(){
        return MyApplication.context;
    }

    public static Handler getHandler(){
        return MyApplication.handler;
    }

    //返回指定colorId对应的颜色值
    public static int getColor(int colorId){
        return ContextCompat.getColor(getContext(),colorId);
    }

    //加载指定viewId的视图对象,并返回
    public static View getView(int viewId){
        View view = View.inflate(getContext(), viewId, null);
        return view;
    }

    public static String[] getStringArr(int strArrId){
        String[] stringArray = getContext().getResources().getStringArray(strArrId);
        return stringArray;
    }

    //将dp转化为px
    public static int dp2px(int dp){
        //获取手机密度
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (dp * density + 0.5);//实现四舍五入
    }

    public static int px2dp(int px){
        //获取手机密度
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (px / density + 0.5);//实现四舍五入
    }

    //保证runnable中的操作在主线程中执行
    public static void runOnUiThread(Runnable runnable) {
        if(isInMainThread()){
            runnable.run();
        }else{
            UIUtils.getHandler().post(runnable);
        }
    }
    //判断当前线程是否是主线程
    private static boolean isInMainThread() {
        int currentThreadId = android.os.Process.myTid();
        return MyApplication.mainThreadId == currentThreadId;

    }

    public static void toast(String message,boolean isLengthLong){
        Toast.makeText(UIUtils.getContext(), message,isLengthLong? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show();
    }
}

Fragment中的调用如:HomeFragment.class


import android.content.Context;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.loopj.android.http.RequestParams;
import com.squareup.picasso.Picasso;
import com.willkong.p2pclient.R;
import com.willkong.p2pclient.bean.Image;
import com.willkong.p2pclient.bean.Index;
import com.willkong.p2pclient.bean.Product;
import com.willkong.p2pclient.common.BaseFragment;
import com.willkong.p2pclient.ui.RoundProgress;
import com.youth.banner.Banner;
import com.youth.banner.BannerConfig;
import com.youth.banner.Transformer;
import com.youth.banner.loader.ImageLoader;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import butterknife.Bind;

/**
 * 作者: willkong on 2017/10/25.
 * 作用:首页
 */
public class HomeFragment extends BaseFragment {

    @Bind(R.id.iv_title_back)
    ImageView ivTitleBack;
    @Bind(R.id.tv_title)
    TextView tvTitle;
    @Bind(R.id.iv_title_setting)
    ImageView ivTitleSetting;
    @Bind(R.id.banner)
    Banner banner;
    @Bind(R.id.tv_home_product)
    TextView tvHomeProduct;
    @Bind(R.id.tv_home_yearrate)
    TextView tvHomeYearrate;
    @Bind(R.id.roundPro_home)
    RoundProgress roundProHome;

    @Override
    protected RequestParams getParams() {
        return null;
    }

    @Override
    protected String getUrl() {
//        return AppNetConfig.INDEX;
        return null;
    }

    private Index index;
    private int currentProgress;

    @Override
    protected void initData(String content) {
        index = new Index();
        if (!TextUtils.isEmpty(content)){
            //解析json数据
            JSONObject jsonObject = JSON.parseObject(content);
            //解析json对象数据
            String proInfo = jsonObject.getString("proInfo");
            Product product = JSON.parseObject(proInfo,Product.class);
            //解析json数组数据
            String imageArr = jsonObject.getString("imageArr");
            List images = jsonObject.parseArray(imageArr, Image.class);
            index.product = product;
            index.images = images;

            //更新页面数据
            tvHomeProduct.setText(product.name);
            tvHomeYearrate.setText(product.yearRate + "%");
            //获取数据中的进度
            currentProgress = Integer.parseInt(index.product.progress);
            //在分线程中实现进度的动态变化
//                new Thread(runnable).start();
            roundProHome.runWithAnimation(100, currentProgress, 50);
            //设置banner样式
            banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE);
            //设置图片加载器
            banner.setImageLoader(new GlideImageLoader());
            //设置图片地址的构成集合
            ArrayList imgesUrl = new ArrayList(index.images.size());
            for (int i = 0; i < index.images.size(); i++) {
                imgesUrl.add(index.images.get(i).IMAURL);
            }
            banner.setImages(imgesUrl);
            //设置banner动画效果
            banner.setBannerAnimation(Transformer.DepthPage);
            //设置标题集合(当banner样式有显示title时)
            String[] titles = new String[]{"分享砍学费", "人脉总动员", "想不到你是这样的app"};
            banner.setBannerTitles(Arrays.asList(titles));
            //设置自动轮播,默认为true
            banner.isAutoPlay(true);
            //设置轮播时间
            banner.setDelayTime(1500);
            //设置指示器位置(当banner模式中有指示器时)
            banner.setIndicatorGravity(BannerConfig.CENTER);
            //banner设置方法全部调用完毕时最后调用
            banner.start();
        }
    }

    @Override
    protected void initTitle() {
        ivTitleBack.setVisibility(View.GONE);
        tvTitle.setText("首页");
        ivTitleSetting.setVisibility(View.GONE);
    }

    @Override
    public int getLayoutId() {
        return R.layout.fragment_home;
    }

    class GlideImageLoader extends ImageLoader {
        @Override
        public void displayImage(Context context, Object path, ImageView imageView) {
            /**
             注意:
             1.图片加载器由自己选择,这里不限制,只是提供几种使用方法
             2.返回的图片路径为Object类型,由于不能确定你到底使用的那种图片加载器,传输的到的是什么格式,那么这种就使用Object接收和返回,你只需要强转成你传输的类型就行,切记不要胡乱强转!
             */

            //Picasso 加载图片简单用法
            Picasso.with(context).load((String) path).into(imageView);
        }
    }
}

你可能感兴趣的:(开发常用功能点)