在写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
网友评论