当前位置: 移动技术网 > 移动技术>移动开发>Android > Android实现加载状态视图切换效果

Android实现加载状态视图切换效果

2019年07月24日  | 移动技术网移动技术  | 我要评论

关于android加载状态视图切换,具体内容如下

1.关于android界面切换状态的介绍

怎样切换界面状态?有些界面想定制自定义状态?状态如何添加点击事件?下面就为解决这些问题!
内容界面
加载数据中
加载数据错误
加载后没有数据
没有网络

2.思路转变,抽取分离类管理几种状态

以前做法:

直接把这些界面include到main界面中,然后动态去切换界面,后来发现这样处理不容易复用到其他项目中,而且在activity中处理这些状态的显示和隐藏比较乱
利用子类继承父类特性,在父类中写切换状态,但有些界面如果没有继承父类,又该如何处理

现在做法:

让view状态的切换和activity彻底分离开,必须把这些状态view都封装到一个管理类中,然后暴露出几个方法来实现view之间的切换。
在不同的项目中可以需要的view也不一样,所以考虑把管理类设计成builder模式来自由的添加需要的状态view

3.关于该状态切换工具优点分析

可以自由切换内容,空数据,异常错误,加载,网络错误等5种状态
父类baseactivity直接暴露5中状态,方便子类统一管理状态切换

/**
* ================================================
* 作  者:杨充
* 版  本:1.0
* 创建日期:2017/7/6
* 描  述:抽取类
* 修订历史:
* ================================================
*/
public abstract class baseactivity extends appcompatactivity {

  protected statuslayoutmanager statuslayoutmanager;

  @override
  protected void oncreate(@nullable bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_base_view);
    initstatuslayout();
    initbaseview();
    inittoolbar();
    initview();
  }

  protected abstract void initstatuslayout();

  protected abstract void initview();

  /**
  * 获取到布局
  */
  private void initbaseview() {
    linearlayout ll_main = (linearlayout) findviewbyid(r.id.ll_main);
    ll_main.addview(statuslayoutmanager.getrootlayout());
  }

  //正常展示数据状态
  protected void showcontent() {
    statuslayoutmanager.showcontent();
  }

  //加载数据为空时状态
  protected void showemptydata() {
    statuslayoutmanager.showemptydata();
  }

  //加载数据错误时状态
  protected void showerror() {
    statuslayoutmanager.showerror();
  }

  //网络错误时状态
  protected void shownetworkerror() {
    statuslayoutmanager.shownetworkerror();
  }

  //正在加载中状态
  protected void showloading() {
    statuslayoutmanager.showloading();
  }
}

当状态是加载数据失败时,点击可以刷新数据;当状态是无网络时,点击可以设置网络

/**
* 点击重新刷新
*/
private void initerrordataview() {
  statuslayoutmanager.showerror();
  linearlayout ll_error_data = (linearlayout) findviewbyid(r.id.ll_error_data);
  ll_error_data.setonclicklistener(new view.onclicklistener() {
    @override
    public void onclick(view view) {
      initdata();
      adapter.notifydatasetchanged();
      showcontent();
    }
  });
}

/**
* 点击设置网络
*/
private void initsettingnetwork() {
  statuslayoutmanager.shownetworkerror();
  linearlayout ll_set_network = (linearlayout) findviewbyid(r.id.ll_set_network);
  ll_set_network.setonclicklistener(new view.onclicklistener() {
    @override
    public void onclick(view view) {
      intent intent = new intent("android.settings.wireless_settings");
      startactivity(intent);
    }
  });
}

倘若有些页面想定制状态布局,也可以自由实现,很简单:

/**
* 自定义加载数据为空时的状态布局
*/
private void initemptydataview() {
  statuslayoutmanager.showemptydata();
  //此处是自己定义的状态布局
  **statuslayoutmanager.showlayoutemptydata(r.layout.activity_emptydata);**
  linearlayout ll_empty_data = (linearlayout) findviewbyid(r.id.ll_empty_data);
  ll_empty_data.setonclicklistener(new view.onclicklistener() {
    @override
    public void onclick(view view) {
      initdata();
      adapter.notifydatasetchanged();
      showcontent();
    }
  });
}

4.如何实现的步骤

1).先看看状态管理器类【builder建造者模式】

loadinglayoutresid和contentlayoutresid代表等待加载和显示内容的xml文件
几种异常状态要用viewstub,因为在界面状态切换中loading和内容view都是一直需要加载显示的,但是其他的3个只有在没数据或者网络异常的情况下才会加载显示,所以用viewstub来加载他们可以提高性能。

public class statelayoutmanager {

  final context context;
  final viewstub networkerrorvs;
  final int networkerrorretryviewid;
  final viewstub emptydatavs;
  final int emptydataretryviewid;
  final viewstub errorvs;
  final int errorretryviewid;
  final int loadinglayoutresid;
  final int contentlayoutresid;
  final int retryviewid;
  final int emptydataiconimageid;
  final int emptydatatexttipid;
  final int erroriconimageid;
  final int errortexttipid;
  final vlayout errorlayout;
  final vlayout emptydatalayout;

  final rootframelayout rootframelayout;
  final onshowhideviewlistener onshowhideviewlistener;
  final onretrylistener onretrylistener;

  public statelayoutmanager(builder builder) {
    this.context = builder.context;
    this.loadinglayoutresid = builder.loadinglayoutresid;
    this.networkerrorvs = builder.networkerrorvs;
    this.networkerrorretryviewid = builder.networkerrorretryviewid;
    this.emptydatavs = builder.emptydatavs;
    this.emptydataretryviewid = builder.emptydataretryviewid;
    this.errorvs = builder.errorvs;
    this.errorretryviewid = builder.errorretryviewid;
    this.contentlayoutresid = builder.contentlayoutresid;
    this.onshowhideviewlistener = builder.onshowhideviewlistener;
    this.retryviewid = builder.retryviewid;
    this.onretrylistener = builder.onretrylistener;
    this.emptydataiconimageid = builder.emptydataiconimageid;
    this.emptydatatexttipid = builder.emptydatatexttipid;
    this.erroriconimageid = builder.erroriconimageid;
    this.errortexttipid = builder.errortexttipid;
    this.errorlayout = builder.errorlayout;
    this.emptydatalayout = builder.emptydatalayout;

    rootframelayout = new rootframelayout(this.context);
    viewgroup.layoutparams layoutparams = new viewgroup.layoutparams(viewgroup.layoutparams.match_parent, viewgroup.layoutparams.match_parent);
    rootframelayout.setlayoutparams(layoutparams);

    rootframelayout.setstatuslayoutmanager(this);
  }

  /**
  * 显示loading
  */
  public void showloading() {
    rootframelayout.showloading();
  }

  /**
  * 显示内容
  */
  public void showcontent() {
    rootframelayout.showcontent();
  }

  /**
  * 显示空数据
  */
  public void showemptydata(int iconimage, string texttip) {
    rootframelayout.showemptydata(iconimage, texttip);
  }

  /**
  * 显示空数据
  */
  public void showemptydata() {
    showemptydata(0, "");
  }

  /**
  * 显示空数据
  */
  public void showlayoutemptydata(object... objects) {
    rootframelayout.showlayoutemptydata(objects);
  }

  /**
  * 显示网络异常
  */
  public void shownetworkerror() {
    rootframelayout.shownetworkerror();
  }

  /**
  * 显示异常
  */
  public void showerror(int iconimage, string texttip) {
    rootframelayout.showerror(iconimage, texttip);
  }

  /**
  * 显示异常
  */
  public void showerror() {
    showerror(0, "");
  }

  public void showlayouterror(object... objects) {
    rootframelayout.showlayouterror(objects);
  }

  /**
  * 得到root 布局
  */
  public view getrootlayout() {
    return rootframelayout;
  }

  public static final class builder {

    private context context;
    private int loadinglayoutresid;
    private int contentlayoutresid;
    private viewstub networkerrorvs;
    private int networkerrorretryviewid;
    private viewstub emptydatavs;
    private int emptydataretryviewid;
    private viewstub errorvs;
    private int errorretryviewid;
    private int retryviewid;
    private int emptydataiconimageid;
    private int emptydatatexttipid;
    private int erroriconimageid;
    private int errortexttipid;
    private vlayout errorlayout;
    private vlayout emptydatalayout;
    private onshowhideviewlistener onshowhideviewlistener;
    private onretrylistener onretrylistener;

    public builder(context context) {
      this.context = context;
    }

    /**
    * 自定义加载布局
    */
    public builder loadingview(@layoutres int loadinglayoutresid) {
      this.loadinglayoutresid = loadinglayoutresid;
      return this;
    }

    /**
    * 自定义网络错误布局
    */
    public builder networkerrorview(@layoutres int newworkerrorid) {
      networkerrorvs = new viewstub(context);
      networkerrorvs.setlayoutresource(newworkerrorid);
      return this;
    }

    /**
    * 自定义加载空数据布局
    */
    public builder emptydataview(@layoutres int nodataviewid) {
      emptydatavs = new viewstub(context);
      emptydatavs.setlayoutresource(nodataviewid);
      return this;
    }

    /**
    * 自定义加载错误布局
    */
    public builder errorview(@layoutres int errorviewid) {
      errorvs = new viewstub(context);
      errorvs.setlayoutresource(errorviewid);
      return this;
    }

    /**
    * 自定义加载内容正常布局
    */
    public builder contentview(@layoutres int contentlayoutresid) {
      this.contentlayoutresid = contentlayoutresid;
      return this;
    }

    public builder errorlayout(vlayout errorlayout) {
      this.errorlayout = errorlayout;
      this.errorvs = errorlayout.getlayoutvs();
      return this;
    }

    public builder emptydatalayout(vlayout emptydatalayout) {
      this.emptydatalayout = emptydatalayout;
      this.emptydatavs = emptydatalayout.getlayoutvs();
      return this;
    }

    public builder networkerrorretryviewid(int networkerrorretryviewid) {
      this.networkerrorretryviewid = networkerrorretryviewid;
      return this;
    }

    public builder emptydataretryviewid(int emptydataretryviewid) {
      this.emptydataretryviewid = emptydataretryviewid;
      return this;
    }

    public builder errorretryviewid(int errorretryviewid) {
      this.errorretryviewid = errorretryviewid;
      return this;
    }

    public builder retryviewid(int retryviewid) {
      this.retryviewid = retryviewid;
      return this;
    }

    public builder emptydataiconimageid(int emptydataiconimageid) {
      this.emptydataiconimageid = emptydataiconimageid;
      return this;
    }

    public builder emptydatatexttipid(int emptydatatexttipid) {
      this.emptydatatexttipid = emptydatatexttipid;
      return this;
    }

    public builder erroriconimageid(int erroriconimageid) {
      this.erroriconimageid = erroriconimageid;
      return this;
    }

    public builder errortexttipid(int errortexttipid) {
      this.errortexttipid = errortexttipid;
      return this;
    }

    public builder onshowhideviewlistener(onshowhideviewlistener onshowhideviewlistener) {
      this.onshowhideviewlistener = onshowhideviewlistener;
      return this;
    }

    public builder onretrylistener(onretrylistener onretrylistener) {
      this.onretrylistener = onretrylistener;
      return this;
    }

    public statelayoutmanager build() {
      return new statelayoutmanager(this);
    }
  }

  public static builder newbuilder(context context) {
    return new builder(context);
  }
}

2).大约5种状态,如何管理这些状态?添加到集合中,android中选用sparsearray比hashmap更省内存,在某些条件下性能更好,主要是因为它避免了对key的自动装箱(int转为integer类型),它内部则是通过两个数组来进行数据存储的,一个存储key,另外一个存储value,为了优化性能,它内部对数据还采取了压缩的方式来表示稀疏数组的数据,从而节约内存空间

/**存放布局集合 */
private sparsearray<view> layoutsparsearray = new sparsearray();
/**将布局添加到集合 */
……
private void addlayoutresid(@layoutres int layoutresid, int id) {
  view resview = layoutinflater.from(mstatuslayoutmanager.context).inflate(layoutresid, null);
  **layoutsparsearray.put(id, resview);**
  addview(resview);
}

3).当显示某个布局时,调用的方法如下

方法里面通过id判断来执行不同的代码,首先判断viewstub是否为空,如果为空就代表没有添加这个view就返回false,不为空就加载view并且添加到集合当中,然后调用showhideviewbyid方法显示隐藏view,retryload方法是给重试按钮添加事件

/**
* 显示loading
*/
public void showloading() {
  if (layoutsparsearray.get(layout_loading_id) != null)
    **showhideviewbyid**(layout_loading_id);
}

/**
* 显示内容
*/
public void showcontent() {
  if (layoutsparsearray.get(layout_content_id) != null)
    **showhideviewbyid**(layout_content_id);
}

/**
* 显示空数据
*/
public void showemptydata(int iconimage, string texttip) {
  if (**inflatelayout**(layout_emptydata_id)) {
    showhideviewbyid(layout_emptydata_id);
    emptydataviewadddata(iconimage, texttip);
  }
}

/**
* 显示网络异常
*/
public void shownetworkerror() {
  if (**inflatelayout**(layout_network_error_id))
    showhideviewbyid(layout_network_error_id);
}

/**
* 显示异常
*/
public void showerror(int iconimage, string texttip) {
  if (**inflatelayout**(layout_error_id)) {
    showhideviewbyid(layout_error_id);
    errorviewadddata(iconimage, texttip);
  }
}

//调用inflatelayout方法,方法返回true然后调用showhideviewbyid方法
private boolean inflatelayout(int id) {
  boolean isshow = true;
  if (layoutsparsearray.get(id) != null) return isshow;
  switch (id) {
    case layout_network_error_id:
      if (mstatuslayoutmanager.networkerrorvs != null) {
        view view = mstatuslayoutmanager.networkerrorvs.inflate();
        retryload(view, mstatuslayoutmanager.networkerrorretryviewid);
        layoutsparsearray.put(id, view);
        isshow = true;
      } else {
        isshow = false;
      }
      break;
    case layout_error_id:
      if (mstatuslayoutmanager.errorvs != null) {
        view view = mstatuslayoutmanager.errorvs.inflate();
        if (mstatuslayoutmanager.errorlayout != null) mstatuslayoutmanager.errorlayout.setview(view);
        retryload(view, mstatuslayoutmanager.errorretryviewid);
        layoutsparsearray.put(id, view);
        isshow = true;
      } else {
        isshow = false;
      }
      break;
    case layout_emptydata_id:
      if (mstatuslayoutmanager.emptydatavs != null) {
        view view = mstatuslayoutmanager.emptydatavs.inflate();
        if (mstatuslayoutmanager.emptydatalayout != null) mstatuslayoutmanager.emptydatalayout.setview(view);
        retryload(view, mstatuslayoutmanager.emptydataretryviewid);
        layoutsparsearray.put(id, view);
        isshow = true;
      } else {
        isshow = false;
      }
      break;
  }
  return isshow;
}

4).然后在根据id隐藏布局

通过id找到需要显示的view并且显示它,隐藏其他view,如果显示隐藏监听事件不为空,就分别调用它的显示和隐藏方法

/**
* 根据id显示隐藏布局
* @param id
*/
private void showhideviewbyid(int id) {
  for (int i = 0; i < layoutsparsearray.size(); i++) {
    int key = layoutsparsearray.keyat(i);
    view valueview = layoutsparsearray.valueat(i);
    //显示该view
    if(key == id) {
      valueview.setvisibility(view.visible);
      if(mstatuslayoutmanager.onshowhideviewlistener != null) mstatuslayoutmanager.onshowhideviewlistener.onshowview(valueview, key);
    } else {
      if(valueview.getvisibility() != view.gone) {
        valueview.setvisibility(view.gone);
        if(mstatuslayoutmanager.onshowhideviewlistener != null) mstatuslayoutmanager.onshowhideviewlistener.onhideview(valueview, key);
      }
    }
  }
}

5).最后看看重新加载方法

/**
* 重试加载
*/
private void retryload(view view, int id) {
  view retryview = view.findviewbyid(mstatuslayoutmanager.retryviewid != 0 ? mstatuslayoutmanager.retryviewid : id);
  if (retryview == null || mstatuslayoutmanager.onretrylistener == null) return;
  retryview.setonclicklistener(new onclicklistener() {
    @override
    public void onclick(view v) {
      mstatuslayoutmanager.onretrylistener.onretry();
    }
  });
}

5.使用方法介绍

1).直接在activity中添加代码

@override
protected void initstatuslayout() {
  statuslayoutmanager = statelayoutmanager.newbuilder(this)
      .contentview(r.layout.activity_content_data)
      .emptydataview(r.layout.activity_empty_data)
      .errorview(r.layout.activity_error_data)
      .loadingview(r.layout.activity_loading_data)
      .networkerrorview(r.layout.activity_networkerror)
      .onretrylistener(new onretrylistener() {
        @override
        public void onretry() {
          //为重试加载按钮的监听事件
        }
      })
      .onshowhideviewlistener(new onshowhideviewlistener() {
        @override
        public void onshowview(view view, int id) {
          //为状态view显示监听事件
        }

        @override
        public void onhideview(view view, int id) {
          //为状态view隐藏监听事件
        }
      })
      .build();
}


2).在父类中重写以下几个方法,子类直接继承就行

//正常展示数据状态
protected void showcontent() {
  statuslayoutmanager.showcontent();
}

//加载数据为空时状态
protected void showemptydata() {
  statuslayoutmanager.showemptydata();
}

//加载数据错误时状态
protected void showerror() {
  statuslayoutmanager.showerror();
}

//网络错误时状态
protected void shownetworkerror() {
  statuslayoutmanager.shownetworkerror();
}

//正在加载中状态
protected void showloading() {
  statuslayoutmanager.showloading();
}

3).更加详细的介绍,可以直接参考demo
https://github.com/yangchong211/ycstatelayout

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网