写一个多状态View

作者: AmatorLee | 来源:发表于2017-07-13 10:51 被阅读2795次

    在app开发过程中很多时候我们需要访问网络,访问数据库等,此时app将会有多种状态,比如加载中,加载成功,加载失败,没有网络等等,此时如果我们使用弹出的方式告知用户,非常不友好。之前被产品爸爸说有种笨重的感觉,但是笨重这么抽象你告诉我我TM该怎么理解呢?
    经过leader的暗示解答,终于知道原来他想要那种感觉,就是那种看起来不笨重的感觉,翻译过来就是使用多状态,那么应该怎么做呢?看到过有人封装在BaseFragment和BaseActivity,但是这样子做耦合度太高,而且看起来代码也比较笨重,是的又回到了笨重这个话题。

    效果演示

    那么笨重feel是怎样的呢?

    笨重feel

    那么一般我们应该怎么做呢?
    看看下面的小图图:

    status

    整体结构

    那么它的整体结构是怎样的呢?

    整体结构

    从上面的整体结构i图可以知道主要有以下方法:

    createView()  构造方法,这里重载了两个,分别使用默认的view以及使用you like view
    onLoad()      加载方法,显示loadingview
    onEmpty()     空数据,显示emptyView
    onError()     加载出错,显示errorView
    onNoNet()     没有网络,显示noNetView
    onSuccess     加载成功,显示构造方法传入的contentView
    onDestory()   资源回收
    setOnRetryClick()  提供点击重新加载的接口
    

    使用

    使用方法很简单,根据上面的整体结构

    1. 构造StatusViewManager manager = StatusViewManager.createView(Context, ContentView);
    2. 加载manager.onLoad()
    3. 根据情况调用各种状态

    代码实现

    StatusViewManager
    /**
     * Created by AmatorLee on 2017/7/12.
     */
    
    public class StatusViewManager extends StatusInterface implements View.OnClickListener {
    
        private LayoutInflater mInflater;
        /**
         * 加载view
         */
        private View mLoadView;
        /**
         * 错误view
         */
        private View mErrorView;
        /**
         * 无数据view
         */
        private View mEmptyView;
        /**
         * 实际view
         */
        private View mContentView;
        /**
         * 无网络链接时得view
         */
        private View mNoNetView;
    
        private RelativeLayout.LayoutParams mParams;
        /**
         * 保存状态view的container
         */
        private RelativeLayout mStatusContainer;
    
        private Context mContext;
        /**
         * 避免重复添加
         */
        private boolean isAddLoad, isAddEmpty, isAddNoNet, isAddError;
        /**
         * 可见状态
         */
        public static final int V = View.VISIBLE;
        /***
         * 不可见状态
         */
        public static final int G = View.GONE;
        /**
         * 重新加载接口
         */
        private onRetryClick mOnRetryClick;
        /**
         * 切换到主线程改变view的状态
         */
        private Handler mMainThreadHandler;
    
    
        private int empty_layout_id = -1;
        private int error_layout_id = -1;
        private int no_net_layout_id = -1;
        private int loading_layout_id = -1;
    
        public static final String ERROR = "error";
        public static final String EMPTY = "empty";
        public static final String NONET = "nonet";
        public static final String LOAD = "load";
    
    
        public void setOnRetryClick(onRetryClick onRetryClick) {
            mOnRetryClick = onRetryClick;
        }
    
        private StatusViewManager(Context context, View contentView, int loading_layout_id, int empty_layout_id, int error_layout_id, int no_net_layout_id) {
            super();
            this.loading_layout_id = loading_layout_id;
            this.empty_layout_id = empty_layout_id;
            this.error_layout_id = error_layout_id;
            this.no_net_layout_id = no_net_layout_id;
            mContentView = contentView;
            mMainThreadHandler = new Handler(Looper.getMainLooper());
            mContext = context;
            mInflater = LayoutInflater.from(context);
            mParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
            mParams.addRule(RelativeLayout.CENTER_IN_PARENT);
            initView();
            setOnClick();
            initContainer();
        }
    
        private void setOnClick() {
            if (mEmptyView != null)
                mEmptyView.setOnClickListener(this);
            if (mNoNetView != null)
                mNoNetView.setOnClickListener(this);
            if (mErrorView != null)
                mErrorView.setOnClickListener(this);
        }
    
        public static StatusViewManager createView(Context context, View ContentView) {
            return new StatusViewManager(context, ContentView, -1, -1, -1, -1);
        }
    
        public static StatusViewManager createView(Context context, View contentView, int loading_layout_id, int empty_layout_id, int error_layout_id, int no_net_layout_id) {
            return new StatusViewManager(context, contentView, loading_layout_id, empty_layout_id, error_layout_id, no_net_layout_id);
        }
    
    
        @Override
        protected void initView() {
            if (loading_layout_id == -1) {
                loading_layout_id = R.layout.layout_loading;
            }
            if (empty_layout_id == -1) {
                empty_layout_id = R.layout.layout_empty;
            }
            if (error_layout_id == -1) {
                error_layout_id = R.layout.layout_error;
            }
            if (no_net_layout_id == -1) {
                no_net_layout_id = R.layout.layout_no_net;
            }
            try {
                mLoadView = mInflater.inflate(loading_layout_id, null);
                mLoadView.setTag(LOAD);
                mErrorView = mInflater.inflate(error_layout_id, null);
                mErrorView.setTag(ERROR);
                mEmptyView = mInflater.inflate(empty_layout_id, null);
                mEmptyView.setTag(EMPTY);
                mNoNetView = mInflater.inflate(no_net_layout_id, null);
                mNoNetView.setTag(NONET);
            } finally {
                mInflater = null;
            }
        }
    
        @Override
        public void initContainer() {
            mStatusContainer = new RelativeLayout(mContext);
            mStatusContainer.setLayoutParams(mParams);
            ViewGroup parent = (ViewGroup) mContentView.getParent();
            parent.addView(mStatusContainer);
        }
    
        @Override
        public void onLoad() {
            mMainThreadHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (mLoadView != null && !isAddLoad) {
                        isAddLoad = true;
                        mStatusContainer.addView(mLoadView, mParams);
                    }
                    show(STATUS.LOADING);
                }
            });
        }
    
        private void show(STATUS result) {
            switch (result) {
                case SUCCESS:
                    changeVisiable(V, G, G, G, G);
                    break;
                case LOADING:
                    changeVisiable(G, V, G, G, G);
                    break;
                case NONET:
                    changeVisiable(G, G, G, G, V);
                    break;
                case ERROR:
                    changeVisiable(G, G, G, V, G);
                    break;
                case EMPTY:
                    changeVisiable(G, G, V, G, G);
                    break;
            }
        }
    
        private void changeVisiable(final int contentStatus, final int loadStatus, final int emptyStatus, final int errorStatus, final int nonetStatus) {
            if (mContentView != null) {
                mContentView.setVisibility(contentStatus);
            }
            if (mLoadView != null) {
                mLoadView.setVisibility(loadStatus);
            }
            if (mEmptyView != null) {
                mEmptyView.setVisibility(emptyStatus);
            }
            if (mNoNetView != null) {
                mNoNetView.setVisibility(nonetStatus);
            }
            if (mErrorView != null) {
                mErrorView.setVisibility(errorStatus);
            }
        }
    
        @Override
        public void onSuccess() {
            mMainThreadHandler.post(new Runnable() {
                @Override
                public void run() {
                    show(STATUS.SUCCESS);
                }
            });
        }
    
        @Override
        public void onNoNet() {
            mMainThreadHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (!isAddNoNet && mNoNetView != null) {
                        mStatusContainer.addView(mNoNetView, mParams);
                        isAddNoNet = true;
                    }
                    show(STATUS.NONET);
                }
            });
        }
    
        @Override
        public void onError() {
            mMainThreadHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (!isAddError && mErrorView != null) {
                        mStatusContainer.addView(mErrorView, mParams);
                        isAddError = true;
                    }
                    show(STATUS.ERROR);
                }
            });
        }
    
        @Override
        public void onEmpty() {
            mMainThreadHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (!isAddEmpty && mEmptyView != null) {
                        mStatusContainer.addView(mEmptyView, mParams);
                        isAddEmpty = true;
                    }
                    show(STATUS.EMPTY);
                }
            });
        }
    
        @Override
        public void onClick(View view) {
            if (mOnRetryClick != null) {
                mOnRetryClick.onRetryLoad();
            }
        }
    
    
        public enum STATUS {
            LOADING,
            EMPTY,
            ERROR,
            SUCCESS,
            NONET
        }
    
    
        @Override
        public void onDestory() {
    
            isAddNoNet = false;
            isAddEmpty = false;
            isAddLoad = false;
            isAddError = false;
    
            mContext = null;
    
            if (mLoadView != null) {
                mLoadView = null;
            }
            if (mContentView != null) {
                mContentView = null;
            }
            if (mEmptyView != null) {
                mEmptyView = null;
            }
            if (mErrorView != null) {
                mErrorView = null;
            }
            if (mNoNetView != null) {
                mNoNetView = null;
            }
            if (mParams != null) {
                mParams = null;
            }
            for (int i = 0; i < mStatusContainer.getChildCount(); i++) {
                mStatusContainer.removeViewAt(i);
            }
            mStatusContainer = null;
        }
    
        interface onRetryClick {
            void onRetryLoad();
        }
    
    }```
    #####StatusInterface
    

    /**

    • Created by AmatorLee on 2017/7/12.
      */

    public abstract class StatusInterface {

    protected abstract void initView();
    
    
    protected abstract void initContainer();
    
    
    protected abstract void onLoad();
    
    
    protected abstract void onSuccess();
    
    
    protected abstract void onNoNet();
    
    
    protected abstract void onError();
    
    
    protected abstract void onEmpty();
    
    protected abstract void onDestory();
    

    }你可能会问为什么要提供一个StatusInterface```,但是我不告诉你。

    总结

    现在流行的直播平台大都是这个小技巧,虽然简单但是常用,所有自己简单的写一下,如果你发现有bug,尽情反馈,同时我也会自我完善。
    最后,provide a gayhub demo

    相关文章

      网友评论

      • mrzhqiang:关于这个,在最新的26SDK上,已经将progressdialog标记为 过时的。从stackoverflow上找到还算靠谱的答案:即使在后台进行任务,也不希望阻止用户交互,只能眼巴巴看着屏幕等待任务结束,而应该是可以进行任何操作。因此,不希望progressdialog带来任何交互上的困扰,尽管目前还是可以用它做类似的事情。所以,这篇文章走在奥利奥前面。
      • 80ac41b8844a:Fragment中能用吗
        AmatorLee:@80ac41b8844a 嗯嗯

      本文标题:写一个多状态View

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