当前位置:   article > 正文

Android 让你的布局飞起来 -View切换显示不同的状态

Android 让你的布局飞起来 -View切换显示不同的状态

Android 让你的布局飞起来

144
作者 伪文艺大叔
2017.01.09 16:29* 字数 1003 阅读 2728 评论 10

xiaoguo.gif
前言

在Android项目开发中一个界面的显示状态包括好几种:内容界面,loading界面,网络错误界面等等;以前开发的时候都是直接把这些界面include到main界面中,然后动态去切换界面,后来发现这样处理不容易复用到其他项目中,而且在activity中处理这些状态的显示和隐藏比较乱,所以就想着能不能封装一个类来管理这些状态View的切换。

思路

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

实现

通常一个界面会包括:内容,空数据,异常错误,加载,网络错误等5种状态的View,所以我们就设置这5种状态View的切换

  1. public static final class Builder {
  2. private Context context;
  3. private int loadingLayoutResId;
  4. private int contentLayoutResId;
  5. private ViewStub netWorkErrorVs;
  6. private int netWorkErrorRetryViewId;
  7. private ViewStub emptyDataVs;
  8. private int emptyDataRetryViewId;
  9. private ViewStub errorVs;
  10. private int errorRetryViewId;
  11. private int retryViewId;
  12. private OnShowHideViewListener onShowHideViewListener;
  13. private OnRetryListener onRetryListener;
  14. public Builder(Context context) {
  15. this.context = context;
  16. }
  17. public Builder loadingView(@LayoutRes int loadingLayoutResId) {
  18. this.loadingLayoutResId = loadingLayoutResId;
  19. return this;
  20. }
  21. public Builder netWorkErrorView(@LayoutRes int newWorkErrorId) {
  22. netWorkErrorVs = new ViewStub(context);
  23. netWorkErrorVs.setLayoutResource(newWorkErrorId);
  24. return this;
  25. }
  26. public Builder emptyDataView(@LayoutRes int noDataViewId) {
  27. emptyDataVs = new ViewStub(context);
  28. emptyDataVs.setLayoutResource(noDataViewId);
  29. return this;
  30. }
  31. public Builder errorView(@LayoutRes int errorViewId) {
  32. errorVs = new ViewStub(context);
  33. errorVs.setLayoutResource(errorViewId);
  34. return this;
  35. }
  36. public Builder contentView(@LayoutRes int contentLayoutResId) {
  37. this.contentLayoutResId = contentLayoutResId;
  38. return this;
  39. }
  40. public Builder netWorkErrorRetryViewId(int netWorkErrorRetryViewId) {
  41. this.netWorkErrorRetryViewId = netWorkErrorRetryViewId;
  42. return this;
  43. }
  44. public Builder emptyDataRetryViewId(int emptyDataRetryViewId) {
  45. this.emptyDataRetryViewId = emptyDataRetryViewId;
  46. return this;
  47. }
  48. public Builder errorRetryViewId(int errorRetryViewId) {
  49. this.errorRetryViewId = errorRetryViewId;
  50. return this;
  51. }
  52. public Builder retryViewId(int retryViewId) {
  53. this.retryViewId = retryViewId;
  54. return this;
  55. }
  56. public Builder onShowHideViewListener(OnShowHideViewListener onShowHideViewListener) {
  57. this.onShowHideViewListener = onShowHideViewListener;
  58. return this;
  59. }
  60. public Builder onRetryListener(OnRetryListener onRetryListener) {
  61. this.onRetryListener = onRetryListener;
  62. return this;
  63. }
  64. public StatusLayoutManager build() {
  65. return new StatusLayoutManager(this);
  66. }
  67. }

状态管理类用到了建造者模式,上面是builder内部类,总共有11个属性,loadingLayoutResId和contentLayoutResId代表等待加载和显示内容的xml文件;netWorkErrorVs,emptyDataVs,errorVs代表另外几种异常状态,那为什么这几种状态要用ViewStub,因为在界面状态切换中loading和内容View都是一直需要加载显示的,但是其他的3个只有在没数据或者网络异常的情况下才会加载显示,所以用ViewStub来加载他们可以提高性能。

在错误的几个界面需要重试按钮重新加载数据,netWorkErrorRetryViewId, emptyDataRetryViewId, errorRetryViewId分别为几个状态界面重试按钮的id, 如果这几个按钮的id是一样的话就直接给retryViewId属性赋值即可,retryViewId优先级最高。

onShowHideViewListener为状态View显示隐藏监听事件
onRetryListener为重试加载按钮的监听事件

接下来需要把这些View添加到一个根View中返回给Activity,为了方便显示隐藏这些View,我们在根View中定义一个集合属性,然后把这些View添加到集合当中管理

  1. /** * 存放布局集合 */
  2. private SparseArray<View> layoutSparseArray = new SparseArray();

这个集合Key为id,Value为View,id为根View类内部自定义的id,通过id找到对应的View来显示隐藏View,下面通过一个方法来看下它的切换逻辑

  1. /** * 显示空数据 */
  2. public void showEmptyData() {
  3. if(inflateLayout(LAYOUT_EMPTYDATA_ID))
  4. showHideViewById(LAYOUT_EMPTYDATA_ID);
  5. }

首先调用inflateLayout方法,方法返回true然后调用showHideViewById方法,
下面来看看inflateLayout方法的实现

  1. private boolean inflateLayout(int id) {
  2. boolean isShow = true;
  3. if(layoutSparseArray.get(id) != null) return isShow;
  4. switch (id) {
  5. case LAYOUT_NETWORK_ERROR_ID:
  6. if(mStatusLayoutManager.netWorkErrorVs != null) {
  7. View view = mStatusLayoutManager.netWorkErrorVs.inflate();
  8. retryLoad(view, mStatusLayoutManager.netWorkErrorRetryViewId);
  9. layoutSparseArray.put(id, view);
  10. isShow = true;
  11. } else {
  12. isShow = false;
  13. }
  14. break;
  15. case LAYOUT_ERROR_ID:
  16. if(mStatusLayoutManager.errorVs != null) {
  17. View view = mStatusLayoutManager.errorVs.inflate();
  18. retryLoad(view, mStatusLayoutManager.errorRetryViewId);
  19. layoutSparseArray.put(id, view);
  20. isShow = true;
  21. } else {
  22. isShow = false;
  23. }
  24. break;
  25. case LAYOUT_EMPTYDATA_ID:
  26. if(mStatusLayoutManager.emptyDataVs != null) {
  27. View view = mStatusLayoutManager.emptyDataVs.inflate();
  28. retryLoad(view, mStatusLayoutManager.emptyDataRetryViewId);
  29. layoutSparseArray.put(id, view);
  30. isShow = true;
  31. } else {
  32. isShow = false;
  33. }
  34. break;
  35. }
  36. return isShow;
  37. }

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

  1. private void showHideViewById(int id) {
  2. for(int i = 0; i < layoutSparseArray.size(); i++) {
  3. int key = layoutSparseArray.keyAt(i);
  4. View valueView = layoutSparseArray.valueAt(i);
  5. //显示该view
  6. if(key == id) {
  7. valueView.setVisibility(View.VISIBLE);
  8. if(mStatusLayoutManager.onShowHideViewListener != null)
  9. mStatusLayoutManager.onShowHideViewListener.onShowView(valueView, key);
  10. } else {
  11. if(valueView.getVisibility() != View.GONE) {
  12. valueView.setVisibility(View.GONE);
  13. if(mStatusLayoutManager.onShowHideViewListener != null)
  14. mStatusLayoutManager.onShowHideViewListener.onHideView(valueView, key);
  15. }
  16. }
  17. }
  18. }

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

  1. public void retryLoad(View view, int id) {
  2. View retryView = view.findViewById(mStatusLayoutManager.retryViewId != 0 ? mStatusLayoutManager.retryViewId : id);
  3. if(retryView == null || mStatusLayoutManager.onRetryListener == null) return;
  4. retryView.setOnClickListener(new OnClickListener() {
  5. @Override
  6. public void onClick(View v) {
  7. mStatusLayoutManager.onRetryListener.onRetry();
  8. }
  9. });
  10. }

可以看出retryViewId 的优先级最好,如果它不为0,就用它去查找View实例,然后如果View实例和重试监听都不为空就添加点击事件,点击事件里调用onRetryListener监听的onRetry方法。

使用
  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.activity_main);
  5. initToolBar();
  6. LinearLayout mainLinearLayout = (LinearLayout) findViewById(R.id.main_rl);
  7. statusLayoutManager = StatusLayoutManager.newBuilder(this)
  8. .contentView(R.layout.activity_content)
  9. .emptyDataView(R.layout.activity_emptydata)
  10. .errorView(R.layout.activity_error)
  11. .loadingView(R.layout.activity_loading)
  12. .netWorkErrorView(R.layout.activity_networkerror)
  13. .retryViewId(R.id.button_try)
  14. .onShowHideViewListener(new OnShowHideViewListener() {
  15. @Override
  16. public void onShowView(View view, int id) {
  17. }
  18. @Override
  19. public void onHideView(View view, int id) {
  20. }
  21. }).onRetryListener(new OnRetryListener() {
  22. @Override
  23. public void onRetry() {
  24. statusLayoutManager.showLoading();
  25. new Thread(new Runnable() {
  26. @Override
  27. public void run() {
  28. try {
  29. Thread.sleep(1000);
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. runOnUiThread(new Runnable() {
  34. @Override
  35. public void run() {
  36. statusLayoutManager.showContent();
  37. }
  38. });
  39. }
  40. }).start();
  41. }
  42. }).build();
  43. mainLinearLayout.addView(statusLayoutManager.getRootLayout(), 1);
  44. statusLayoutManager.showLoading();
  45. }

StatusLayoutManager提供了一系列的方法来显示不同布局View之间的切换

  1. statusLayoutManager.showLoading(); 显示loading加载view
  2. statusLayoutManager.showContent(); 显示你的内容view
  3. statusLayoutManager.showEmptyData(); 显示空数据view
  4. statusLayoutManager.showError(); 显示error view
  5. statusLayoutManager.showNetWorkError(); 显示网络异常view
结束语

至此,核心逻辑和代码都已经分析完成,想看如何调用和源码的朋友可以移步至:https://github.com/chenpengfei88/StatusLayout

Android自定义View技术分享
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/242699
推荐阅读
相关标签
  

闽ICP备14008679号