Android收藏文章

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

2017-04-14  本文已影响173人  莫比乌丝环丶

为什么会有这篇文章?

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

为什么会有这个控件?

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

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

MultiStatusLayout优点

使用:

在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,文末会有链接

一、获取自定义属性:

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

上一篇 下一篇

猜你喜欢

热点阅读