美文网首页Android收藏文章
Android你一定会用到的多状态layout

Android你一定会用到的多状态layout

作者: 莫比乌丝环丶 | 来源:发表于2017-04-14 17:54 被阅读173次

    为什么会有这篇文章?

    答:因为项目中肯定会用到。

    为什么会有这个控件?

    • 项目中肯定会遇到"加载中","加载失败","网络错误","数据为空"这四种情况,如果不封装处理起来很不方便。
    • 最重要的是上面说的这几种情况,只可能同时出现一种,按照我们平常的写法,每次加载布局的时候都会去加载这些布局,很显然这就降低了一个页面加载的性能。

    基于这两点MultiStatusLayout应运而生,首先说一下我自己的思路吧,既然要处理这么多状态他肯定是一个ViewGroup了,为了能适用所有的布局文件,它继承自RelativeLayout。不多哔哔,直接撸起-.-

    MultiStatusLayout优点

    • 用SparseArray代替HashMap来存储每个View。SparseArray比HashMap更省内存,在某些条件下性能更好,主要是因为它避免了对key的自动装箱(int转为Integer类型),它内部则是通过两个数组来进行数据存储的,一个存储key,另外一个存储value,为了优化性能,它内部对数据还采取了压缩的方式来表示稀疏数组的数据,从而节约内存空间
    • 实现按需加载,当你需要做某种操作的时候才会去加载相对应的布局。这样就解决了,多布局堆在一起导致的页面加载效率降低、便于管理多种情况显示的页面的问题。

    使用:

    在project目录下的build.gradle下添加:

        allprojects {
            repositories {
                ...
                maven { url 'https://jitpack.io' }
            }
        }
    

    在app目录下的build.gradle下添加:

        dependencies {
            compile 'com.github.Walll-E:MultiStatusLayout:v1.0'
        }
    

    在values下面建立attrs资源文件定义所需要的属性

    <declare-styleable name="MultiStatusLayout">
        <attr name="loadLayout" format="reference" />
        <attr name="emptyLayout" format="reference" />
        <attr name="netErrorLayout" format="reference" />
        <attr name="errorLayout" format="reference" />
        <attr name="otherLayout" format="reference"/>
        <attr name="targetViewId" format="reference"/>
      </declare-styleable>
    

    关于自定义View或者ViewGroup的东西,这里不多介绍,自行百度吧,这里只贴出关键代码,其他代码在我github,文末会有链接

    一、获取自定义属性:

    • mNetErrorLayout:网络错误时显示的布局id
    • mLoadingLayout :加载中显示的布局id
    • mErrorLayout : 请求错误时显示的布局id
    • mEmptyLayout :请求数据为空时显示的布局id
    • mOtherLayout : 此布局id作为扩充以上四种情况不足以满足实际需求时显示的布局id
    • mTargetViewId :此id是显示扩充布局时不隐藏View的id(可以是界面title的id)
    public MultiStatusLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mContext = context;
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MultiStatusLayout, defStyleAttr, 0);
            mNetErrorLayout = array.getResourceId(R.styleable.MultiStatusLayout_netErrorLayout, mNetErrorLayout);
            mLoadingLayout = array.getResourceId(R.styleable.MultiStatusLayout_loadLayout, mLoadingLayout);
            mErrorLayout = array.getResourceId(R.styleable.MultiStatusLayout_errorLayout, mErrorLayout);
            mEmptyLayout = array.getResourceId(R.styleable.MultiStatusLayout_emptyLayout, mEmptyLayout);
            mOtherLayout = array.getResourceId(R.styleable.MultiStatusLayout_otherLayout, mOtherLayout);
            mTargetViewId = array.getResourceId(R.styleable.MultiStatusLayout_targetViewId, -1);
            array.recycle();
        }
    

    二、加载布局中包裹的每个View,并且将每个View添加至SparseArray

       /**
         * 重写此方法,获取到其中的子控件个数、子控件将其添加至SparseArray(用于管理不同情况View的显示)中。
         */
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            int childCount = mContentViewCount = getChildCount();
            mContentViews = new SparseArray<>();
            //隐藏布局中的子控件并且添加至mContentViews
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                mContentViews.put(i, childView);
            }
        }
    

    显示加载中的布局

       /**
         * 显示加载中布局
         */
        public void showLoading() {
            //先从中获取加载中的布局
            View view = mContentViews.get(mContentViewCount);
            //显示加载中的布局
            showView(view,mLoadingLayout,mContentViewCount);
        }
    

    显示网络错误布局

     /**
         * 显示网络错误的布局
         */
        public void showNetError() {
            View view = mContentViews.get(mContentViewCount + 1);
            showView(view, mNetErrorLayout, mContentViewCount + 1);
        }
    

    显示列表数据为空的布局

    /**
         * 列表数据为空时显示此布局
         */
        public void showEmpty() {
            View view = mContentViews.get(mContentViewCount + 2);
            showView(view, mEmptyLayout, mContentViewCount + 2);
        }
    

    显示数据错误或服务器错误的布局

     /**
         * 加载数据错误时显示此布局
         */
        public void showError() {
            View view = mContentViews.get(mContentViewCount + 3);
            showView(view, mErrorLayout, mContentViewCount + 3);
        }
    

    显示扩充布局

     /**
         * 显示扩充布局
         */
        public void showOther() {
            //先从中获取加载中的布局
            View view = mContentViews.get(mContentViewCount + 4);
            //显示加载中的布局
            showView(view, mOtherLayout, mContentViewCount + 4);
        }
    

    显示View,如果View为null时去加载并且添加至此layout中,并且添加至SparseArray中

     /**
         * 显示View,如果View为null时去加载添加至此layout中,并且添加至SparseArray中
         * @param view
         * @param layoutId
         * @param index
         */
        private void showView(View view,int layoutId,int index){
            //如果子控件处于显示状态先隐藏所有的子控件
            hideViews();
            if (view==null){
                view = inflate(mContext,layoutId,null);
                addView(view,layoutParams);
                mContentViews.put(index,view);
            }else {
                view.setVisibility(VISIBLE);
            }
            //设置网络错误或者数据错误布局的点击事件
            if (index == mContentViewCount + 1 || index == mContentViewCount + 3) {
                view.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (onReloadDataListener != null) onReloadDataListener.reloadData();
                    }
                });
            }
        }
    

    隐藏所有的布局

    /**
         * 除了title之外,隐藏所有显示的View
         */
        private void hideViews() {
            for (int i = 0; i < mContentViews.size(); i++) {
                View view = mContentViews.valueAt(i);
                if (mTargetViewId != view.getId() && view.getVisibility() != GONE) {
                    view.setVisibility(GONE);
                }
            }
        }
    

    获取相应的布局,做你想做的具体操作

    /**
         * 获取加载布局
         * @return
         */
        public View getLoadingView(){
            View view = mContentViews.get(mContentViewCount);
            if (view==null) {
                //如果加载中布局为null,证明还未加载此布局
                view = inflate(mContext, mLoadingLayout, null);
                mContentViews.put(mContentViewCount, view);
            }
            return view;
        }
    

    设置重新加载数据的监听(一般情况下只有网络错误或者服务器错误时需要重新加载数据)

       /**
         * 设置重新加载数据的监听
         */
           public void setOnReloadDataListener(final OnReloadDataListener onReloadDataListener) {
            this.onReloadDataListener = onReloadDataListener;
        }
      /**
         * 重新加载的监听器
         */
        public interface OnReloadDataListener {
            void reloadData();
        }
    

    代码已上传至github

    相关文章

      网友评论

        本文标题:Android你一定会用到的多状态layout

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