美文网首页Android各种长见识涨姿势各种第三方
自定义ProgressStateLayout实现网络请求状态间的

自定义ProgressStateLayout实现网络请求状态间的

作者: Mersens | 来源:发表于2016-10-19 00:44 被阅读919次

    前言

    在Android开发中我们经常遇到这样的场景:去加载网络数据时需要显示进度条,提示用户正在加载,加载失败需要给用户提示加载失败,还需要能够重新加载,数据为空也要给用户相应的提示。对于一个初入编程之世的程序员来说这肯定是令人头(dan)疼(teng)的问题,在开发中他们会这么做:在一个XML布局文件中添加加载成功,失败,数据为空的布局,在请求过程中通过设置View的“显示”和“隐藏”来达到这种效果,每当看到这样写,我的内心是崩溃的(当然,之前我也是这样的O.o),后来随着公司的项目功能扩展也来越庞大,我觉得不能再用这样扯淡的方法来解决这种操蛋的问题,就决定自定义一个控件解决该问题,经过几天的思考ProgressStateLayout诞生了。

    思路

    提起自定义控件,我们首先就会想到自定义控件的基本流程:onFinishInflate(),onMeasure(),onLayout(),onDraw(),onAttachedToWindow()当然,有这样的想法很正确,但 不一定每个方法都要重写,这需要结合实际的业务需求。在ProgressStateLayout中需要封装四中状态(四个View),这时简单的View已经不能满足我们的需求,我们需要通过ViewGroup来实现该功能。

    1:ProgressStateLayout需要继承RelativeLayout。

    2:通过LayoutInflater实例化失,数据为空,加载进度的View。

    3:重写addView()方法,把View添加到ViewGroup中。

    4:通过switchState()方法来实现不同状态的相互切换。

    具体实现

    ProgressStateLayout需要继承RelativeLayout,然后修改里面的构造方法。让一个参数的调用两个参数的,两个参数的调用三个参数的,在三个参数的构造方法中添加一个Init()方法用于一些初始化操作。

    private LayoutParamslayoutParams;

    private LayoutInflaterinflater;

    private Listviews=null;

    private static final StringTAG_LOADING="loading";

    private static final StringTAG_EMPTY="empty";

    private static final StringTAG_ERROR="error";

    private View viewLoading,viewEmpty,viewError;

    一个方参数的构造方法

    public ProgressStateLayout(Context context) {

    this(context,null);

    }

    两个参数的构造方法

    public ProgressStateLayout(Contextcontext, AttributeSet attrs){

    this(context, attrs,0);

    }

    三个参数的构造方法

    public ProgressStateLayout(Context context, AttributeSet attrs,intdefStyleAttr) {

    super(context, attrs, defStyleAttr);

    init();

    }


    写个接口,用于“重新加载”按钮的点击回调

    private ReloadListenerlistener;

    //重新加载按钮的接口,用于监听重新加载按钮的监听回调

    public interface ReloadListener {

    void onClick();

    }

    枚举类型用于标示四种不同的状态(当然你也可以用int,String类型来表示,这取决于个人的习惯,枚举类型能够很好的规范参数的形式,个人比较喜欢用枚举,至少代码风格会显得高大上一点哈哈)

    //四种不同状态

    private enum Type {

    LOADING,EMPTY,CONTENT,ERROR;

    }

    init()初始化方法,你会注意到这里实例化了一个List集合,你会想这个集合什么鬼?,当然有用了,别想多了,这个List不是用来保存四个状态的View,是用来保存ViewGroup里面加载成功的View除了正在加载,数据为空,加载失败

    /**

    *初始化操作

    */

    public voidinit() {

    views=newArrayList();

    inflater= (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    layoutParams=newLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,

    ViewGroup.LayoutParams.MATCH_PARENT);

    layoutParams.addRule(CENTER_IN_PARENT);

    }

    设置“正在加载”的View

    private void setLoadingView() {

    if(viewLoading==null) {

    viewLoading=inflater.inflate(R.layout.layout_loading,null);

    viewLoading.setTag(TAG_LOADING);

    viewLoading.requestLayout();

    addView(viewLoading,layoutParams);

    }else{

    viewLoading.setVisibility(View.VISIBLE);

    }}

    设置“数据为空”的View

    private void setEmptyView(intresid, String msg) {

    if(viewEmpty==null) {

    viewEmpty=inflater.inflate(R.layout.layout_empty,null);

    if(resid !=0) {

    ImageView imageView = (ImageView)viewEmpty.findViewById(R.id.img_nodata);

    imageView.setImageResource(resid);}

    if(!TextUtils.isEmpty(msg)) {

    TextView tv_msg = (TextView) findViewById(R.id.text_nodata_tips);

    tv_msg.setText(msg);}

    viewEmpty.setTag(TAG_EMPTY);

    viewEmpty.requestLayout();

    addView(viewEmpty,layoutParams);}else{

    viewEmpty.setVisibility(View.VISIBLE);

    }}

    设置“加载失败”的View

    private void setErrorView(int resid, String title, String msg, String btntext) {

    if (viewError == null) {

    viewError = inflater.inflate(R.layout.layout_error, null);

    if (resid != 0) {

    ImageView img = (ImageView) findViewById(R.id.img_nodata);

    img.setImageResource(resid);}

    if (!TextUtils.isEmpty(title)) {

    TextView tv_title = (TextView) findViewById(R.id.tv_title);

    tv_title.setText(title);}

    if (!TextUtils.isEmpty(msg)) {

    TextView tv_msg = (TextView) findViewById(R.id.tv_msg);

    tv_msg.setText(title);}

    Button btn_reload = (Button) viewError.findViewById(R.id.btn_reload);

    if (!TextUtils.isEmpty(btntext)) {

    btn_reload.setText(btntext);}

    btn_reload.setOnClickListener(new OnClickListener() {

    @Override

    public void onClick(View v) {

    if (listener != null) {

    listener.onClick();}}});

    viewError.requestLayout();

    viewError.setTag(TAG_ERROR);

    addView(viewError, layoutParams);} else {

    viewError.setVisibility(View.VISIBLE);}

    }

    设置“加载成功”后ViewGroup里面内容

    private void setContentView(booleanflag) {

    for(View v :views) {

    v.setVisibility(flag ? View.VISIBLE: View.GONE);

    }}

    设置类型状态的隐藏

    private void hideLoadingView() {

    if(viewLoading!=null) {

    viewLoading.setVisibility(View.GONE);}}

    private void hideEmptyView() {

    if(viewEmpty!=null) {

    viewEmpty.setVisibility(View.GONE);}}

    private void hideErrorView() {

    if(viewError!=null) {

    viewError.setVisibility(View.GONE);}}

    对外提供方法用于类型的切换,可以传递信息参数,如果不传,使用默认

    public void showLoading() {

    switchState(Type.LOADING,

    0,null,null,null);}

    public void showError(intresid, String title, String msg,

    String btntext, ReloadListener listener) {

    this.listener= listener;

    switchState(Type.ERROR, resid, title, msg, btntext);}

    public void showEmpty(intresid, String msg) {

    switchState(Type.EMPTY, resid, msg,null,null);}

    public void showContent() {

    switchState(Type.CONTENT,0,null,null,null);}

    public void showError(ReloadListener listener) {

    this.listener= listener;

    switchState(Type.ERROR,0,null,null,null);}

    public void showEmpty() {

    switchState(

    Type.EMPTY,0,null,null,null);

    }

    切换状态方法switchState()

    /**

    *改变状态方法

    *@paramtype

    */

    private void switchState(Type type,intresid, String title, String msg, String btntext) {

    switch(type) {

    caseLOADING:

    hideEmptyView();

    hideErrorView();

    setContentView(false);

    setLoadingView();

    break;

    caseEMPTY:

    hideErrorView();

    hideLoadingView();

    setContentView(false);

    setEmptyView(resid, title);

    break;

    caseERROR:

    hideEmptyView();

    hideLoadingView();

    setContentView(false);

    setErrorView(resid, title, msg, btntext);

    break;

    caseCONTENT:

    hideEmptyView();

    hideLoadingView();

    hideErrorView();

    setContentView(true);

    break;}}

    重写addView()方法,这里需要为不同类型的View设置Tag标示,用于区分不同状态的view和父布局内的控件。

    @Override

    public void addView(View child, ViewGroup.LayoutParams params) {

    super.addView(child, params);

    //把ProgressStateView内的子控件内容添加到list集合中,保证不同状态间相互切换内容的隐藏与显示

    if(child.getTag() ==null|| (!child.getTag().equals(TAG_LOADING) &&

    !child.getTag().equals(TAG_EMPTY) && !child.getTag().equals(TAG_ERROR))) {

    views.add(child);}

    }

    how to use

    把它当作相对布局使用

    截图:

    正在加载:

    正在加载,progressbar没有显示完整

    数据为空:

    没有找到数据

    请求失败:

    加载失败,点击重试

    请求成功:

    数据请求成功


    示例代码请点击:ProgressLayout

    结束语

    本人是一个技术渣渣,自学Android起步,对Android的极深奥义尚未了解,通过对在开发中的问题进行整理总结,希望对开发中遇到同样问题的小伙伴有所帮助,第一次在这里写文章,文中有很多瑕疵,还请多多关照。

    相关文章

      网友评论

      本文标题:自定义ProgressStateLayout实现网络请求状态间的

      本文链接:https://www.haomeiwen.com/subject/ncututtx.html