在最近公布的比赛框架中,发现了页面加载管理类,觉得挺有用的,所以做个简单的笔记。
什么是页面加载管理类呢?(大佬可直接跳过翻看实现过程)
如果能有这个问题,那么很好,哈哈哈,你和我一样,刚开始都挺疑惑的。
我们一般在写网络请求的时候,如果不涉及什么MVP,或者别的,就一个简单网络请求,然后再成功的结果里刷新View,请求过程中总不能白屏吧,所以有些人可能会让转一个圈,或者显示加载中的布局,然后等成功后再隐藏掉,显示具体的布局view。这样的话,也没什么问题,但是如果你的状态需要多个,这个时候就很烦了。总不能每个状态的判断一下吧。再者说这样也不利于你解耦。
出于上面的需求,我们用下面的demo,来解决问题,先用一张图来看效果吧。
我们来具体看一下实现过程
/**
 * 页面加载管理类,根据不同的状态显示不同的view
 */
public abstract class ContentPage extends FrameLayout{
  /**加载中的view*/
  private View loadingView;
  /**加载失败的view*/
  private View errorView;
  /**加载数据为空的view*/
  private View emptyView;
  /**加载成功的view*/
  private View successView;
  /**默认是加载中的状态*/
  private PageState mState = PageState.STATE_LOADING;
  /**
   * 定义页面状态常量
   *
   */
  public enum PageState{
    STATE_LOADING(0),/*加载中的状态*/
    STATE_SUCCESS(1),/*加载成功的状态*/
    STATE_ERROR(2),/*加载失败的状态*/
    STATE_EMPTY(3);/*加载数据为空的状态*/
    private int value;
    PageState(int value){
      this.value = value;
    }
    public int getValue(){
      return value;
    }
  }
  public ContentPage(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initPage();
  }
  public ContentPage(Context context, AttributeSet attrs) {
    super(context, attrs);
    initPage();
  }
  public ContentPage(Context context) {
    super(context);
    initPage();
  }
  /**
   * 初始化Page
   */
  private void initPage(){
    LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
    if(loadingView==null){/*天然往ContentPage中添加4个状态对应的view,然后根据不同状态,显示不同的view,添加LoadingView*/
      loadingView = View.inflate(getContext(), R.layout.page_loading, null);
    }
    addView(loadingView, params);
    if(errorView==null){/*添加ErrorView*/
      errorView = View.inflate(getContext(), R.layout.page_error, null);
      Button btn_reload = errorView.findViewById(R.id.btn_reload);
      btn_reload.setOnClickListener(v -> {
        mState = PageState.STATE_LOADING;
        showPage();
        loadDataAndRefreshPage();/*重新加载*/
      });
    }
    addView(errorView, params);
    if(emptyView==null){/*添加EmptyView*/
      emptyView = View.inflate(getContext(), R.layout.page_empty, null);
    }
    addView(emptyView, params);
    if(successView==null){/*添加SuccessView*/
      successView = createSuccessView();
    }
    if(successView==null){
      throw new IllegalArgumentException("The method createSuccessView() can not return null!");
    }else {
      addView(successView, params);
    }
    showPage();/*根据不同的state显示不同的view*/
    loadDataAndRefreshPage();/*请求数据然后刷新View*/
  }
  /**
   * 请求服务器的数据,然后根据加载的数据刷新View
   */
  private void loadDataAndRefreshPage(){
    new Thread(){
      public void run() {
        Object result = loadData();/*获取加载完成的数据*/
        mState = checkData(result);/*根据数据判断当前page的状态*/
        /*根据最新state,刷新View*/
        CommonUtil.runOnUIThread(() -> showPage());
      }
    }.start();
  }
  /**
   * 根据数据检查对应的状态
   * @return
   */
  private PageState checkData(Object result){
    if(result!=null){
      if(result instanceof List){
        List list = (List) result;
        if(list.size()==0){
          return PageState.STATE_EMPTY;/*加载数据为空*/
        }else {
          return PageState.STATE_SUCCESS;/*加载成功*/
        }
      }else {
        return PageState.STATE_SUCCESS;/*加载成功*/
      }
    }else {
      return PageState.STATE_ERROR;/*加载失败*/
    }
  }
  public void refreshPage(Object o) {
    if (o == null) {
      //说明木有数据,那么对应的state应该是error
      mState = PageState.STATE_ERROR;
    } else {
      //说明请求回来的有数据,那么对应的state应该是success
      mState = PageState.STATE_SUCCESS;
    }
    showPage();
  }
  /**
   * 根据不同的state显示不同的view
   */
  private void showPage(){
    loadingView.setVisibility(mState== PageState.STATE_LOADING?View.VISIBLE:View.INVISIBLE);
    errorView.setVisibility(mState== PageState.STATE_ERROR?View.VISIBLE:View.INVISIBLE);
    emptyView.setVisibility(mState== PageState.STATE_EMPTY?View.VISIBLE:View.INVISIBLE);
    successView.setVisibility(mState== PageState.STATE_SUCCESS?View.VISIBLE:View.INVISIBLE);
  }
  /**
   * 每个界面的成功view都不一样,应该由每个界面自己提供
   * @return
   */
  public abstract View createSuccessView();
  /**
   * 由于每个界面加载数据的过程不一样,我只需要关心它加载回来之后的数据,然后根据数据刷新View
   * @return
   */
  public abstract Object loadData();
}现在看一下它的四种不同状态view
加载中 Loading...

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ProgressBar
        style="@android:style/Widget.ProgressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:indeterminateDrawable="@drawable/indeterminate_drawable" />
</FrameLayout>加载失败

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center" >
        <ImageView
            android:id="@+id/page_iv"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_centerHorizontal="true"
            android:scaleType="centerInside"
            android:src="@drawable/ic_error_page" />
        <Button
            android:id="@+id/btn_reload"
            android:layout_width="wrap_content"
            android:layout_height="34dp"
            android:layout_below="@id/page_iv"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="10dp"
            android:background="@drawable/btn_bg"
            android:ellipsize="end"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:singleLine="true"
            android:text="加载失败,点击重试"
            android:textColor="#ff717171"
            android:textSize="16sp" />
    </RelativeLayout>
</FrameLayout>数据为null

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/image"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/tree"
        android:contentDescription="@string/no_data" />
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="@string/no_data"
        app:layout_constraintTop_toBottomOf="@id/image"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        />
</android.support.constraint.ConstraintLayout>加载成功
这个布局就不用写了,就是你自己要显示的布局
那么具体在代码中如何使用呢,我们看下面这个Demo。
先是一个BaseFragment的基类。
/**
 * @author Petterp
 * @date 2019/5/21.
 */
public abstract class BaseFragment extends SupportFragment implements View.OnClickListener {
    public ContentPage contentPage;
    public ProgressDialog pdLoading;
    protected Activity mActivity;
    protected Context mContext;
    private Unbinder mUnBinder;
    @Override
    public void onAttach(Context context) {
        mActivity = (Activity) context;
        mContext = context;
        super.onAttach(context);
    }
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        /*
         * 初始化pdLoading
         */
        pdLoading = new ProgressDialog(getActivity());
        pdLoading.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        pdLoading.setMessage("请稍后");
        pdLoading.setCanceledOnTouchOutside(false);
        pdLoading.setCancelable(true);
        if (contentPage == null) {
            contentPage = new ContentPage(getActivity()) {
                @Override
                public Object loadData() {
                    return requestData();
                }
                @Override
                public View createSuccessView() {
                    return getSuccessView();
                }
            };
        } else {
          removeSelfFromParent(contentPage);
        }
        return contentPage;
    }
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mUnBinder = ButterKnife.bind(this, view);
    }
    /**
     * 初始化 Toolbar
     */
    protected void initToolBar(Toolbar toolbar, boolean homeAsUpEnabled, String title) {
        ((BaseActivity) getActivity()).initToolBar(toolbar, homeAsUpEnabled, title);
    }
    /**
     * 刷新状态
     *
     */
    public void refreshPage(Object o) {
        contentPage.refreshPage(o);
    }
    /**
     * 返回据的fragment填充的具体View
     */
    protected abstract View getSuccessView();
    /**
     * 返回请求服务器的数据
     */
    protected abstract Object requestData();
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mUnBinder.unbind();
    }
    public void removeSelfFromParent(View child) {
        // 获取父view
        if (child != null) {
            ViewParent parent = child.getParent();
            if (parent instanceof ViewGroup) {
                ViewGroup viewGroup = (ViewGroup) parent;
                // 将自己移除
                viewGroup.removeView(child);
            }
        }
    }
}然后是一个模拟Fragment
/**
 * 模拟Fragment
 */
public class FirstFragment extends BaseFragment {
    @Override
    protected View getSuccessView() {
        TextView textView = new TextView(getActivity());
        textView.setText("第一页");
        return textView;
    }
    @Override
    protected Object requestData() {
        SystemClock.sleep(1000);/*模拟请求服务器的延时过程*/
        return "";/*加载成功*/
    }
    @Override
    public void onClick(View view) {
    }
    /**
     * 类似于 Activity的 onNewIntent()
     */
    @Override
    public void onNewBundle(Bundle args) {
        super.onNewBundle(args);
    }
}好啦,具体就是这样呢,相应的注释都挺详细的,如果有什么地方不对,也欢迎大家指出。










