Android MultipleStatusView 状态页源码阅读

MultipleStatusView项目地址

概述
  1. 该自定义控件继承自RelativeLayout
  2. 内容页展示与其他状态页展示逻辑不同,将围绕这两个来说.
内容页展示逻辑
  1. 当XML加载完成立即展示内容页
@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    // 当XML加载完成,展示内容页.
    showContent();
}
  1. 手动调用展示内容页
@Override public void onClick(View v) {
    switch (v.getId()) {
        case R.id.fab_content:
            // 手动调用展示内容页
            mMultipleStatusView.showContent();
            break;
    }
}
  1. 内容页展示逻辑
/**
 * 显示内容视图
 */
public final void showContent() {
    // 记录界面状态更改后的状态以及回调状态改变监听
    changeViewStatus(STATUS_CONTENT);
    if (null == mContentView && mContentViewResId != NULL_RESOURCE_ID) {
        // 如果内容页为null,且内容页id不为null,那么就inflate出内容页View.
        mContentView = mInflater.inflate(mContentViewResId, null);
        // 将内容页添加进MultipleStatusView中,对应的View索引为0.
        addView(mContentView, 0, DEFAULT_LAYOUT_PARAMS);
    }
    // 该方法用来将非内容页的视图都GONE掉
    showContentView();
}
/**
 * 该方法用来将非内容页的视图都GONE掉
 */
private void showContentView() {
    final int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
        View view = getChildAt(i);
        // mOtherIds 是一个list集合,专门用来存储View的id.
        // 因为内容页View的id没有存入mOtherIds集合中,所以当要显示内容页时,只要将mOtherIds集合中id对应的View都GONE掉就可以了.
        view.setVisibility(mOtherIds.contains(view.getId()) ? View.GONE : View.VISIBLE);
    }
}
/**
 * 1. 每当状态页更改时候调用,将当前状态与即将更改的状态回调出去.
 * 2. 即将更改的状态赋值给mViewStatus,替换掉当前状态.
 *
 * @param newViewStatus 新的视图状态
 */
private void changeViewStatus(int newViewStatus) {
    if (mViewStatus == newViewStatus) {
        return;
    }
    if (null != mViewStatusListener) {
        mViewStatusListener.onChange(mViewStatus, newViewStatus);
    }
    mViewStatus = newViewStatus;
}
其他状态页展示
  • 这里取Empty页面展示来分析
  1. showEmpty(int hintResId, Object... formatArgs)
/**
 * 显示空视图
 *
 * @param hintResId 自定义提示文本内容
 * @param formatArgs 占位符参数
 */
public final void showEmpty(int hintResId, Object... formatArgs) {
    // 往下看
    showEmpty();
    // 设置状态页中提示内容,其中mEmptyView不能为null,否则会抛异常.
    setStatusHintContent(mEmptyView, hintResId, formatArgs);
}

private void setStatusHintContent(View view, int resId, Object... formatArgs) {
    checkNull(view, "Target view is null.");
    setStatusHintContent(view, view.getContext().getString(resId, formatArgs));
}
private void setStatusHintContent(View view, String hint) {
    checkNull(view, "Target view is null.");
    // 各个状态页中,id为status_hint_content的TextView就是用来显示状态提示用的.
    TextView hintView = view.findViewById(R.id.status_hint_content);
    if (null != hintView) {
        // 如果hintView不为null,就将需要告诉用户的状态信息展示出来
        hintView.setText(hint);
    } else {
        // 否则抛出异常,这里就要求了如果是自定义的状态页中的提示TextView,该TextView的id必须为status_hint_content,否则抛出异常.
        throw new NullPointerException("Not find the view ID `status_hint_content`");
    }
}
  1. showEmpty()
public final void showEmpty() {
    // mEmptyViewResId 为空视图的id,在XML中设置,空视图可以自定义.
    showEmpty(mEmptyViewResId, DEFAULT_LAYOUT_PARAMS);
}
public final void showEmpty(int layoutId, ViewGroup.LayoutParams layoutParams) {
    // 首先判断mEmptyView是否为null,当第一次进入的时候空视图还一次未显示,它确实会是null.
    // mEmptyView==null,就使用inflate()获取该空视图.注意,此时的mEmptyView变量并未被赋值,还是未null.
    showEmpty(null == mEmptyView ? inflateView(layoutId) : mEmptyView, layoutParams);
}
public final void showEmpty(View view, ViewGroup.LayoutParams layoutParams) {
    // view参数为上一步通过inflate()获取的空视图View.
    checkNull(view, "Empty view is null.");// view对象存在这些判空不会抛异常
    checkNull(layoutParams, "Layout params is null.");
    // 记录界面状态更改后的状态以及回调状态改变监听
    changeViewStatus(STATUS_EMPTY);
    // 如果是第一次显示空视图,mEmptyView此时是为null.
    if (null == mEmptyView) {
        // 将上一步通过inflate()获取的空视图View赋值给mEmptyView.
        mEmptyView = view;
        // 获取空视图中的重试点击View
        View emptyRetryView = mEmptyView.findViewById(R.id.empty_retry_view);
        if (null != mOnRetryClickListener && null != emptyRetryView) {
            // 这里回调点击事件,在回调中可以重新加载数据之类的.
            emptyRetryView.setOnClickListener(mOnRetryClickListener);
        }
        // 这里是将空视图View的id存入mOtherIds集合中.
        mOtherIds.add(mEmptyView.getId());
        // 将空视图View添加进MultipleStatusView中,对应的View索引为0.
        addView(mEmptyView, 0, layoutParams);
    }
    // 隐藏MultipleStatusView中非空视图的子View.
    showViewById(mEmptyView.getId());
}
private void showViewById(int viewId) {
    final int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
        View view = getChildAt(i);
        // 遍历MultipleStatusView中所有子控件,viewId是空视图id,如果id不为空视图id,这些id对应的View都将被GONE掉.
        view.setVisibility(view.getId() == viewId ? View.VISIBLE : View.GONE);
    }
}
使用
<com.classic.common.MultipleStatusView
    android:id="@+id/SimpleMultiStateView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"                                 
    app:emptyView="@layout/view_empty"
    app:errorView="@layout/view_err"
    app:loadingView="@layout/view_loading"
    app:noNetworkView="@layout/view_nonet">
    <im.delight.android.webview.AdvancedWebView
        android:id="@+id/awv_help"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:overScrollMode="never"
        android:scrollbars="none" />
</com.classic.common.MultipleStatusView>
<!--@layout/view_nonet:view_nonet代表layout布局文件,当网络超时,该布局会显示在屏幕最顶层被用户看到 -->
  • 当使用自定义状态布局得时候,状态布局得父控件必须按照MultipleStatusView 控件得规则来写。
    • 加载中视图父容器id必须为:loading_view
    • 空视图父容器id必须为:empty_view
    • 错误视图父容器id必须为:error_view
    • 无网络视图父容器id必须为:no_network_view
  • 当需要为状态视图中得某控件设置点击事件得时候,被点击的控件的ID命名规则也应该按照MultipleStatusView 控件的规则来。
    • 空视图内被点击view id:empty_retry_view
    • 错误视图内被点击view id:error_retry_view
    • 无网络视图内被点击view id:no_network_retry_view
  • 布局
<!--RelativeLayout 控件得ID需要按照该控件得规则来命名-->   
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/no_network_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@mipmap/refresh_bg">
<!--需要为被点击的控件设置ID,命名规则按照该控件的规则来-->
    <ImageView
        android:id="@+id/no_network_retry_view"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="350dp"
        android:src="@mipmap/refresh_btn" />
</RelativeLayout>
  • 代码
MultipleStatusView multipleStatusView = (MultipleStatusView) findViewById(R.id.SimpleMultiStateView);
//显示加载中视图
multipleStatusView.showLoading();
// multipleStatusView.showLoading(R.layout.xxx, layoutParams);
// multipleStatusView.showLoading(customView, layoutParams);
//显示空视图
multipleStatusView.showEmpty();
// multipleStatusView.showEmpty(R.layout.xxx, layoutParams);
// multipleStatusView.showEmpty(customView, layoutParams);
//显示错误视图
multipleStatusView.showError();
// multipleStatusView.showError(R.layout.xxx, layoutParams);
// multipleStatusView.showError(customView, layoutParams);
//显示无网络视图
//如果需要为该状态视图中的某个控件设置点击事件
//1.按照规则命名
//2.为该控件设置监听
multipleStatusView.showNoNetwork();
mMultiStateView.setOnRetryClickListener(v -> {
         awvHelp.reload();
});
// multipleStatusView.showNoNetwork(R.layout.xxx, layoutParams);
// multipleStatusView.showNoNetwork(customView, layoutParams);
//显示内容视图
multipleStatusView.showContent();
// multipleStatusView.showContent(R.layout.xxx, layoutParams);
// multipleStatusView.showContent(customView, layoutParams);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值