美文网首页我爱编程
窥探状态切换库MultiStateView源码

窥探状态切换库MultiStateView源码

作者: 不需要任何 | 来源:发表于2018-04-16 13:58 被阅读322次

在写APP时候展示数据中,存在数据加载,记载失败,无网络,数据为空等不同的状态,每一个状态动态加载fragment是否很浪费内存呢。这时MultiStateView 比较完美的解决了此问题

用法

用法不多说 比较简单易懂直接贴上地址吧
https://github.com/XuDaojie/MultiStateView/blob/master/app/src/main/java/me/xdj/multistateview/MultiStateFragment.java
主要就是在layout里设置布局,后调用.setViewState(Flag)设置状态的过程

窥探源码

MultiStateView,作为只有一个类的库,源码的解析应该面向大众不难。。主要是其中的思想和处理,是比较符合规范的
但是我把源码拆分为两个类SimpleMultiSateView/MultiSateView,使代码更加符合单一职责的原则,后期可拓展性更大

其中MultiSateView是主要来实现主要和基本功能的类:

public static final int STATE_CONETENT = 1000;
public static final  int STATE_FAIL = 1001;
public static  final  int STATE_LOADING = 1002;
public static  final  int STATE_EMPTY = 1003;
public static  final  int STATE_NONET = 1004;

private SparseArray<View> mStateViewArray = new SparseArray<>();
private SparseArray<Integer> mLayoutIDArray = new SparseArray();
private int mCurrentState = STATE_CONETENT;
private View mContentView = null;
private OnReLoadListener mOnReloadListener;
private OnInflateListener mOnInflateListener;

flag常量其实就是为了听过键值对绑定,设置现在状态;
mStateViewArray是 Flag标签和View绑定的数组,
mLayoutIDArray 是ResId 和 View绑定的数组。

先看源码的重写addview 因为一般情况关于自定义View都会以addView开始的

 @Override
public void addView(View child) {
    validContentView(child);
    super.addView(child);
}

@Override
public void addView(View child, int index) {
    validContentView(child);
    super.addView(child, index);
}

@Override
public void addView(View child, ViewGroup.LayoutParams params) {
    validContentView(child);
    super.addView(child, params);
}

@Override
public void addView(View child, int width, int height) {
    validContentView(child);
    super.addView(child, width, height);
}

@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
    validContentView(child);
    super.addView(child, index, params);
}

可以看到每个addView()方法会调用validContentView(View)方法
那现在看看 validContentView(View):

 private void validContentView(View child) {
    if (isVaildContentView(child)) {
        mContentView = child;
        mStateViewArray.put(STATE_CONETENT,child);
    }
    else if (mCurrentState !=STATE_CONETENT){
        child.setVisibility(GONE);
    }
}
  private boolean isVaildContentView(View child) {
    return mContentView==null&&mStateViewArray.indexOfValue(child)==-1;
}

先看isVaildContentView判断方法,主要判断当前是否是第一次加载ContentView,如果是把它键值对的形式放入mStateViewArray,如果不是则先隐藏。

接下来再来看看,该类最重要的方法

  public void setViewState(int state)
{
    if (state != mCurrentState){
        View view = getView(state);
        getCurrentView().setVisibility(GONE);
        mCurrentState = state;
        if (view !=null){
            view.setVisibility(VISIBLE);
        }
        else {
            int layoutID = (int) mLayoutIDArray.get(state);
            if (layoutID == 0) return;
            view = LayoutInflater.from(getContext()).inflate(layoutID, this, false);
            mStateViewArray.put(state, view);
            addView(view);
            if (state == STATE_FAIL) {
                if (mOnInflateListener != null) {
                    Button button = view.findViewById(mOnReloadListener.getReLayoutID());
                    if (button != null) {
                        button.setOnClickListener(new OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                mOnReloadListener.onReload();
                            }
                        });
                    }
                }
            }
            view.setVisibility(VISIBLE);
            if (mOnInflateListener != null) {
                mOnInflateListener.onInflate(state, view);
            }
        }
    }
}
    public View getView(int state) {
    return mStateViewArray.get(state);
}

第一步它会先检测是不是当前的VIEWSTATE,如果是则状态的转换是多余的。
如果不是则会想通过getView(),在StateViewArray通过STATE这个key寻找View,并且设置当前state,之前的state设置为GONE,最后把设置STATE的View展现出来。
如果StateViewArray没有返回View,说明得通过另外个数组LayoutIDArray,进行View的加载,最后以键值对的形式存放入StateViewArray中,最后设置可见。整个过程就展现完成了。

最后再看看这个类的最后一个方法 addViewbyStateAndLayoutId

 public void addViewbyStateAndLayoutId(int layoutId,int state){
    mLayoutIDArray.put(state,layoutId);
}

这个方法主要作用是,自定义设置状态方法。他源码过分的简单只是单纯的吧,ResId 和 state 以键值对的形式存入。等调用setViewState()方法使才会进行判断View的加载,也就是等我们真真正正要展现的时候在进行View的加载,俗称懒加载,这符合性能优化的原则,如果一个State整个过程中没显示则他也不会加载,只能以键值对的形式躺在LayoutIDArray中。

SimpleMultiStateView

整个MultiStateView介绍完了,而SimpleMultiStateView相对简单我就直接贴源码咯

public class SimpleMultiStateView  extends MultiStateView{

private static final String TAG = SimpleMultiStateView.class.getSimpleName();

private static final int MIN_SHOW_TIME = 600;
private static final  int MIN_DELAY = 600;

private int mTargetState = -1;
private long mLoadingStartTime = -1 ;

private final Runnable mLoadingHide = new Runnable() {
    @Override
    public void run() {
        setViewState(mTargetState);
        mTargetState = -1;
        mLoadingStartTime = -1 ;
    }
};

public SimpleMultiStateView(@NonNull Context context) {
    this(context,null);
}

public SimpleMultiStateView(@NonNull Context context, @Nullable AttributeSet attrs) {
    this(context, attrs,0);
}

public SimpleMultiStateView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initView(context,attrs);
}

private void initView(Context context,AttributeSet attr) {
    TypedArray typedArray = context.obtainStyledAttributes(attr, R.styleable.SimpleMultiStateView);
    int resIdFail = typedArray.getResourceId(R.styleable.SimpleMultiStateView_fail_layout,-1);
    int resIdLoading = typedArray.getResourceId(R.styleable.SimpleMultiStateView_loading_layot,-1);
    int resIdEmpty = typedArray.getResourceId(R.styleable.SimpleMultiStateView_empty_layout,-1);
    int resIdNonet = typedArray.getResourceId(R.styleable.SimpleMultiStateView_nonet_layout,-1);

    if (resIdEmpty != -1) {
        addViewbyStateAndLayoutId(resIdEmpty,MultiStateView.STATE_EMPTY);
    }
    if (resIdFail != -1){
        addViewbyStateAndLayoutId(resIdFail,MultiStateView.STATE_FAIL);
    }
    if (resIdLoading != -1){
        addViewbyStateAndLayoutId(resIdLoading,MultiStateView.STATE_LOADING);
    }
    if (resIdNonet != -1) {
        addViewbyStateAndLayoutId(resIdNonet, MultiStateView.STATE_NONET);
    }
}

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    removeCallbacks(mLoadingHide);
}

@Override
public void setViewState(int state) {
    if (getCurrentViewState() == STATE_LOADING && state != STATE_LOADING){
        long diff = System.currentTimeMillis() - mLoadingStartTime;
        if (diff < MIN_SHOW_TIME){
            postDelayed(mLoadingHide,MIN_DELAY);
        }
        else {
            super.setViewState(state);
        }
    }

    else if (state ==STATE_LOADING){
       mLoadingStartTime =  System.currentTimeMillis();
    }
    super.setViewState(state);
}

}

这个类主要作用

  • 就是对loading加载时间上进行最低限制,使动画不太生硬
  • 解析在XML文件中设置的默认State进行和LayoutId绑定和预放入在LayoutIDArray中。

整个过程就差不多介绍完了,可能大家有疑惑为啥要用SpareArray,而不用hashmap呢,这就的看看这个大佬的解释啦:
https://blog.csdn.net/stzy00/article/details/45035301

相关文章

  • 窥探状态切换库MultiStateView源码

    在写APP时候展示数据中,存在数据加载,记载失败,无网络,数据为空等不同的状态,每一个状态动态加载fragment...

  • Android常用的开源项目库二

    空白页MultiStateView★760 - 基于状态显示不同内容的Android视图progress-acti...

  • FragmentTabHost实现App主界面

    Android重写FragmentTabHost源码来实现状态保存FragmentTabHost在切换Fragme...

  • VS2015下JSON文件的读写

    生成库文件 我这里使用的是jsoncpp 源码可从git上直接下载:jsoncpp 切换到VS可用节点最新的源码中...

  • Oracle数据库DG切换

    [TOC] Oracle数据库切换 检测是否有挂载磁盘 主库操作 关闭主库监听 在主库端检查数据库可切换状态 修改...

  • LINUX

    切换到grid目录下 su - grid 查询监听实例状态 crsctlstatres-t exit 切换到数据库...

  • 11 - mongodb 命令

    MongoDB - 数据库常用命令 创建/切换数据库 显示所有数据库 查看当前所处数据库 显示当前DB状态 查看当...

  • 源码推荐:仿写映客直播 iOS快速切换主题

    源码推荐:仿写映客直播 iOS快速切换主题 源码推荐:仿写映客直播 iOS快速切换主题

  • 源码安装vim

    用git从github上clone一份源码 编译之前下载相关类库 切换到vim目录下 设置编译属性 开始编译安装 ...

  • 状态切换

    最近任务有些多,经常需要在多任务之间切换,工作环境也多次切换,整个人的效率严重下滑。 这个局面该怎么应对呢? 多任...

网友评论

    本文标题:窥探状态切换库MultiStateView源码

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