Android 让你的布局飞起来

2,649 阅读3分钟
原文链接: www.jianshu.com

xiaoguo.gif

前言

在Android项目开发中一个界面的显示状态包括好几种:内容界面,loading界面,网络错误界面等等;以前开发的时候都是直接把这些界面include到main界面中,然后动态去切换界面,后来发现这样处理不容易复用到其他项目中,而且在activity中处理这些状态的显示和隐藏比较乱,所以就想着能不能封装一个类来管理这些状态View的切换。

思路

为了让View状态的切换和Activity彻底分离开,必须把这些状态View都封装到一个管理类中,然后暴露出几个方法来实现View之间的切换,因为在不同的项目中可以需要的View也不一样,所以考虑把管理类设计成builder模式来自由的添加需要的状态View。

实现

通常一个界面会包括:内容,空数据,异常错误,加载,网络错误等5种状态view,所以我们就设置这5种View的切换

public static final class Builder {    

        private Context context;    
        private int loadingLayoutResId;    
        private int contentLayoutResId;    
        private ViewStub netWorkErrorVs;    
        private ViewStub emptyDataVs;    
        private ViewStub errorVs;    
        private OnShowHideViewListener onShowHideViewListener;    

        public Builder(Context context) {       
            this.context = context;    
        }    

        public Builder loadingView(@LayoutRes int loadingLayoutResId) {    
            this.loadingLayoutResId = loadingLayoutResId;        
            return this;    
        }    

        public Builder netWorkErrorView(@LayoutRes int newWorkErrorId) {    
            netWorkErrorVs = new ViewStub(context);     
            netWorkErrorVs.setLayoutResource(newWorkErrorId);        
            return this;    
        }    

       public Builder emptyDataView(@LayoutRes int noDataViewId) {    
            emptyDataVs = new ViewStub(context);        
            emptyDataVs.setLayoutResource(noDataViewId);       
            return this;   
       }    

       public Builder errorView(@LayoutRes int errorViewId) {        
            errorVs = new ViewStub(context);   
            errorVs.setLayoutResource(errorViewId);        
            return this;    
       }    

      public Builder contentView(@LayoutRes int contentLayoutResId) {       
            this.contentLayoutResId = contentLayoutResId;        
            return this;    
      }    

      public Builder onShowHideViewListener(OnShowHideViewListener onShowHideViewListener) {       
             this.onShowHideViewListener = onShowHideViewListener;        
             return this;    
      }    

      public StatusLayoutManager build() {        
             return new StatusLayoutManager(this);   
      }
}

上面代码是管理类的builder内部类,总共有7个属性,loadingLayoutResId和contentLayoutResId代表等待加载和显示内容的xml文件;netWorkErrorVs,emptyDataVs,errorVs代表另外几种状态,那为什么这几种状态要用ViewStub,因为在界面状态切换中loading和内容View都是一直需要加载显示的,但是其他的3个只有在没数据或者网络异常的情况下才会加载显示,所以用ViewStub来加载他们可以提高性能。

接下来需要把这些View添加到一个根View中返回给Activity,为了方便显示隐藏这些View,我们在根View中定义一个集合属性,然后把这些View添加到集合当中管理

/** *  存放布局集合 */
private SparseArray<View> layoutSparseArray = new SparseArray();

这个集合Key为id,Value为View,id为根View类内部自定义的id,通过id找到对应的View来显示隐藏View,下面通过一个方法来看下它的切换逻辑

/** *  显示空数据 */
public void showEmptyData() {    
     if(inflateLayout(LAYOUT_EMPTYDATA_ID))      
      showHideViewById(LAYOUT_EMPTYDATA_ID);
}

首先调用inflateLayout方法,方法返回true才会调用下面的方法,
我们来看看这个方法的实现

private boolean inflateLayout(int id) {    
    boolean isShow = true;    
    if(layoutSparseArray.get(id) != null) return isShow;    
    switch (id) {        
       case LAYOUT_NETWORK_ERROR_ID:            
         if(netWorkErrorVs != null) {                
           View view = netWorkErrorVs.inflate();                
           layoutSparseArray.put(id, view);                
           isShow = true;            
         } else {                
           isShow = false;            
         }            
         break;        

       case LAYOUT_ERROR_ID:            
           if(errorVs != null) {               
              View view = errorVs.inflate();                
              layoutSparseArray.put(id, view);                
              isShow = true;            
           } else {                
              isShow = false;           
           }            
           break;        

      case LAYOUT_EMPTYDATA_ID:            
          if(emptyDataVs != null) {                
              View view = emptyDataVs.inflate();                
              layoutSparseArray.put(id, view);                
              isShow = true;            
          } else {                
              isShow = false;            
          }            
          break;    
      }    
      return isShow;
}

方法里面通过id判断来执行不同的代码,首先判断ViewStub是否为空,如果为空就代表没有添加这个View就返回false,不为空就加载View并且添加到集合当中,然后调用showHideViewById方法显示隐藏View

private void showHideViewById(int id) {    
    for(int i = 0; i < layoutSparseArray.size(); i++) {        
        int key = layoutSparseArray.keyAt(i);        
        View valueView = layoutSparseArray.valueAt(i);        
        //显示该view        
        if(key == id) {            
            valueView.setVisibility(View.VISIBLE);            
            if(mOnShowHideListener != null) 
              mOnShowHideListener.onShow(valueView, key);       
         } else {           
             if(valueView.getVisibility() != View.GONE) {     
                 valueView.setVisibility(View.GONE);                
             if(mOnShowHideListener != null) 
                  mOnShowHideListener.onHide(valueView, key);            
         }        
   }    
}}

结束语

至此,核心逻辑和代码都已经分析完成,想看如何调用和源码的朋友可以移步至:github.com/chenpengfei…