关于Android加载状态视图切换
目录介绍
1.关于Android界面切换状态的介绍
2.思路转变,抽取分离类管理几种状态
3.关于该状态切换工具优点分析
4.如何实现的步骤
5.使用方法介绍和Demo下载

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

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 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中添加代码

@Overrideprotected 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

更多相关文章

  1. “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
  2. Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
  3. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  4. Android(安卓)SQlite数据库常规操作
  5. Android层接收串口数据
  6. android 4大组件介绍
  7. Android(安卓)App开发架构之:MVC
  8. Android搭建Ftp服务器监听文件传输状态
  9. Android中适配器getView()原理和ListView加载多个Item.

随机推荐

  1. Android   收集索引贴
  2. Android电话秀(二)
  3. Android计算缓存大小、清空缓存
  4. Android取消EditText自动获取焦点
  5. Android开发中Android与网页(JavaScrip)t的
  6. Android 实现 选择文件对话框
  7. Android Studio 报错小记
  8. 6.1.5 GridView详解
  9. 设置Android程序的默认安装位置
  10. Android 手机震动 设置震动时间