Android 加载进度条,加载成功,数据为空,加载失败,无网络等状态不同界面的切换

咱们在开发过程中经常需要从网络上获取数据。这个时候为了增强用户体验。就可能有如下需求:在获取的数据的中时候显示加载中的UI(获取数据需要 一定的时间)、如果获取数据失败显示失败的UI、获取的数据为空的时候显示空数据的UI、数据成功获取到的时候才显示获取到的数据列表。总之一句话就是根据不同的状态现实不同的UI。这也是我们这篇文章的重点.接下来咱么就对这一需求做一个简单的封装。

推荐几篇实现该功能比较好的博客

Android中BaseActivity的简单使用,可以切换无数据、网络错误的界面

Android 加载成功、加载失败、加载中、无数据四个不同界面的切换

Android状态页切换(数据加载中,数据加载失败,空数据)

Android开发之常用的loading等待效果实现,仿微博等待动画。两种实现方式详解

方式1:封装到BaseActivity/BaseFragment中

一般来说,项目中都会有多个Activity或者Fragment,每个界面都会有一些相同的地方,比如标题栏、无数据界面、网络错误界面,每个界面都写就会显得很重复,也不便于维护。所以这个时候就有必要写一个基类来进行封装,也就是BaseActivity或者BaseFragment.

无数据和网络错误的情况。对于上边的标题栏,可能有多种情况,有标题栏,无标题栏,是否有返回按钮,右边是否显示文字或图片。这些情况我们都可以写在基类中。这些公用的界面都是写在BaseActivity的对应的xml文件中。

view_base_state_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rootView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/title_bar"
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:gravity="center"
        android:background="@color/white"
        android:text="标题"
        android:textColor="#000000"
        android:textSize="18sp"
        android:textStyle="bold" />

    <!--加载进度条布局-->
    <LinearLayout
        android:id="@+id/view_progress"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/title_bar"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="vertical">

        <ProgressBar
            android:id="@+id/progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/text_loading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="加载中..." />

    </LinearLayout>

    <!--请求成功布局-->
    <FrameLayout
        android:id="@+id/view_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/title_bar"></FrameLayout>

    <!--请求数据为空,加载失败,无网络布局-->
    <LinearLayout
        android:id="@+id/view_empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/title_bar"
        android:gravity="center">

        <TextView
            android:id="@+id/text_tip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="暂无数据" />

    </LinearLayout>

</RelativeLayout>

BaseActivity

/**
 * Cerated by xiaoyehai
 * Create date : 2020/11/11 12:14
 * description :加载进度条,请求成功,请求数据为空,请求失败,无网络等状态不同界面的切换
 */
public abstract class BaseActivity extends AppCompatActivity {

    private RelativeLayout mRootView;
    private View mViewProgress;
    private FrameLayout mViewContent;
    private View mViewEmpty;
    private TextView mTextTip;
    private TextView mTvTitleBar;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.view_base_state_layout);

        findViews();

        init();
    }


    public void initTitleBar(boolean isShow, String title) {
        mTvTitleBar.setText(title);
        if(isShow) {
            mTvTitleBar.setVisibility(View.VISIBLE);
        }else {
            mTvTitleBar.setVisibility(View.GONE);
        }
    }

    public void showProgressView() {
        showView(R.id.view_progress);
    }

    public void showContentView() {
        showView(R.id.view_content);
    }

    public void showEmptyView() {
        showView(R.id.view_empty);
    }

    public void showEmptyView(int resId) {
        showEmptyView();
        mTextTip.setText(resId);
    }

    /**
     * 请求数据为空,请求失败,无网络等状态都用同一个布局
     *
     * @param msg
     */
    public void showEmptyView(String msg) {
        showEmptyView();
        mTextTip.setText(msg);
    }


    public void showView(int viewId) {
        for (int i = 1; i < mRootView.getChildCount(); i++) {
            if (mRootView.getChildAt(i).getId() == viewId) {
                mRootView.getChildAt(i).setVisibility(View.VISIBLE);
            } else {
                mRootView.getChildAt(i).setVisibility(View.GONE);
            }
        }
    }


    private void findViews() {
        mRootView = findViewById(R.id.rootView);
        mTvTitleBar = findViewById(R.id.title_bar);
        mViewProgress = findViewById(R.id.view_progress);
        mViewContent = (FrameLayout) findViewById(R.id.view_content);
        mViewEmpty = findViewById(R.id.view_empty);
        mTextTip = (TextView) findViewById(R.id.text_tip);

        mViewEmpty.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onReload();
            }
        });

        //将当前界面的布局添加到BaseActivity中去
        View realContentView = LayoutInflater.from(this).inflate(getLayoutId(), mViewContent, true);
    }

    public void onReload() {

    }

    protected abstract int getLayoutId();

    protected abstract void init();

}

使用

public class SecondActivity extends BaseActivity {

    @Override
    protected int getLayoutId() {
        return R.layout.activity_second;
    }

    @Override
    protected void init() {
        initTitleBar(true, "SecondActivity");
        loadData();
    }

    //模拟加载数据
    private void loadData() {

        showProgressView();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                int code = 202;
                switch (code) {
                    case 200:
                        showContentView();
                        break;
                    case 201:
                        showEmptyView("暂无数据");
                        break;
                    case 202:
                        showEmptyView("加载失败");
                        break;
                    case 203:
                        showEmptyView("无网络");
                        break;
                }
            }
        }, 2000);

    }

    @Override
    public void onReload() {
        super.onReload();
        loadData();
    }
}

BaseFragment

public abstract class BaseFragment extends Fragment implements View.OnClickListener {

    protected Context mContext;

    private View mViews;
    private FrameLayout mRootView;
    private View mViewProgress;
    private FrameLayout mViewContent;
    private View mViewEmpty;
    private TextView mTextTip;
    private TextView mTvTitleBar;


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mContext = getActivity();
    }


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mViews = inflater.inflate(R.layout.view_base_state_layout, container, false);
        findViews();
        return mViews;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        setRealContentView();

        init();

    }

    public void initTitleBar(boolean isShow, String title) {
        mTvTitleBar.setText(title);
        if (isShow) {
            mTvTitleBar.setVisibility(View.VISIBLE);
        } else {
            mTvTitleBar.setVisibility(View.GONE);
        }
    }


    private void findViews() {
        mRootView = mViews.findViewById(R.id.rootView);
        mTvTitleBar = mViews.findViewById(R.id.title_bar);
        mViewProgress = mViews.findViewById(R.id.view_progress);
        mViewContent = (FrameLayout) mViews.findViewById(R.id.view_content);
        mViewEmpty = mViews.findViewById(R.id.view_empty);
        mTextTip = (TextView) mViews.findViewById(R.id.text_tip);

        mViewEmpty.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onReload();
            }
        });

    }

    protected void onReload() {
    }

    private void setRealContentView() {
        View realContentView = LayoutInflater.from(getActivity()).inflate(getLayoutId(), mViewContent, true);
    }


    public void showProgressView() {
        showView(R.id.view_progress);
    }

    public void showContentView() {
        showView(R.id.view_content);
    }

    public void showEmptyView() {
        showView(R.id.view_empty);
    }


    public void showEmptyView(int resId) {
        showEmptyView();
        mTextTip.setText(resId);
    }

    public void showEmptyView(String msg) {
        showEmptyView();
        mTextTip.setText(msg);
    }


    public void showView(int viewId) {
        for (int i = 1; i < mRootView.getChildCount(); i++) {
            if (mRootView.getChildAt(i).getId() == viewId) {
                mRootView.getChildAt(i).setVisibility(View.VISIBLE);
            } else {
                mRootView.getChildAt(i).setVisibility(View.GONE);
            }
        }
    }


    protected abstract int getLayoutId();

    protected abstract void init();

}

方式2:自定义一个控件来切换布局

Android 加载成功、加载失败、加载中、无数据四个不同界面的切换

ViewStatusLayout

/**
 * Cerated by xiaoyehai
 * Create date : 2020/11/11 14:31
 * description : 自定义一个View来切换布局。
 */
public class ViewStatusLayout {

    private View mVEmptyContainer;
    private ImageView mIvNoDataView;
    private TextView mTvNoDataTip;
    private TextView mTvRetryBtn;
    public Builder mBuilder;
    public View mRoot;
    private View mClLoadingContainer;
    /***
     * 当前状态
     */
    private @ViewStatus int mCurrentStatus;

    private ViewStatusLayout(Context context, ViewGroup parent, Builder builder) {
        if (builder == null) {
            throw new RuntimeException("you must create commonNoDataLayout with Builder");
        }
        mBuilder = builder;
        mRoot = LayoutInflater.from(context).inflate(R.layout.view_status_layout, parent);

        mVEmptyContainer = parent.findViewById(R.id.ll_empty_container);
        mIvNoDataView = parent.findViewById(R.id.iv_no_data_img);
        mTvNoDataTip = parent.findViewById(R.id.tv_no_data_tip);
        mTvRetryBtn = parent.findViewById(R.id.stv_retry);
        mClLoadingContainer = parent.findViewById(R.id.cl_loading_container);

        if (builder.background != 0) {
            mVEmptyContainer.setBackgroundResource(builder.background);
        }

        switchView(mBuilder.viewStatus);

        if (builder.listener == null) {
            mTvRetryBtn.setVisibility(View.GONE);
        } else {
            mTvRetryBtn.setVisibility(View.VISIBLE);
            if (!TextUtils.isEmpty(builder.retryTip)) {
                mTvRetryBtn.setText(builder.getRetryTip());
            }
            mTvRetryBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    builder.listener.click(mCurrentStatus);
                }
            });
        }
    }

    private void setNoDataView() {
        mTvNoDataTip.setText(mBuilder.getNoDataTip());
        mIvNoDataView.setImageResource(mBuilder.noDataResId);
        if (TextUtils.isEmpty(mBuilder.getRetryTip())) {
            mTvRetryBtn.setVisibility(View.GONE);
        } else {
            mTvRetryBtn.setVisibility(View.VISIBLE);
            mTvRetryBtn.setText(mBuilder.getRetryTip());
        }
    }

    private void setNoNetworkView() {
        mTvNoDataTip.setText(mBuilder.getNoNetworkTip());
        mIvNoDataView.setImageResource(mBuilder.getNoNetworkResId());
        if (TextUtils.isEmpty(mBuilder.getNoNetworkBtnTip())) {
            mTvRetryBtn.setVisibility(View.GONE);
        } else {
            mTvRetryBtn.setVisibility(View.VISIBLE);
            mTvRetryBtn.setText(mBuilder.getNoNetworkBtnTip());
        }
    }

    public void switchView(@ViewStatus int viewStatus) {
        switch (viewStatus) {
            case ViewStatus.HASDATA:
                mVEmptyContainer.setVisibility(View.GONE);
                mCurrentStatus &= ~ViewStatus.NODATA;
                mCurrentStatus &= ~ViewStatus.NONETWORK;
                mCurrentStatus &= ~ViewStatus.EMPTY;
                mClLoadingContainer.setVisibility(View.GONE);
                break;
            case ViewStatus.NODATA:
                if(mBuilder.useStatusView){
                    mVEmptyContainer.setVisibility(View.VISIBLE);
                    setNoDataView();
                }
                mCurrentStatus &= ~ViewStatus.HASDATA;
                mCurrentStatus &= ~ViewStatus.NONETWORK;
                mCurrentStatus &= ~ViewStatus.EMPTY;
                mClLoadingContainer.setVisibility(View.GONE);
                break;
            case ViewStatus.NONETWORK:
                if (((mCurrentStatus & 0xfff) & ViewStatus.HASDATA) == ViewStatus.HASDATA) {
                    return;
                }
                mCurrentStatus &= ~ViewStatus.HASDATA;
                mCurrentStatus &= ~ViewStatus.NODATA;
                mCurrentStatus &= ~ViewStatus.EMPTY;
                mClLoadingContainer.setVisibility(View.GONE);
                if(mBuilder.useStatusView) {
                    mVEmptyContainer.setVisibility(View.VISIBLE);
                }
                setNoNetworkView();
                break;
            case ViewStatus.LOADING_START:
                mCurrentStatus &= ~ViewStatus.LOADING_END;
                mClLoadingContainer.setVisibility(View.VISIBLE);
                break;
            case ViewStatus.LOADING_END:
                mCurrentStatus &= ~ViewStatus.LOADING_START;
                mClLoadingContainer.setVisibility(View.GONE);
                break;
            case ViewStatus.EMPTY:
                mVEmptyContainer.setVisibility(View.GONE);
                mCurrentStatus = viewStatus;
                mClLoadingContainer.setVisibility(View.GONE);
                break;
            default:
                break;
        }
        mCurrentStatus = mCurrentStatus | viewStatus;
    }

    public static class Builder {
        private ViewGroup container;//父容器
        private CharSequence noDataTip ;
        private int noDataResId;
        /***
         * 重试按钮文案
         */
        private CharSequence retryTip;
        private IRetryClickListener listener;

        private int noNetworkResId;
        private int background;
        private CharSequence noNetworkTip;
        /***
         * 无网络时的点击重试
         */
        private CharSequence noNetworkBtnTip;
        private @ViewStatus
        int viewStatus = ViewStatus.EMPTY;
        /***
         * 是否使用状态图,有些页面不需要展示无数据也,无网络页
         */
        private boolean useStatusView = true;

        public Builder setBackground(int dr) {
            this.background = dr;
            return this;
        }

        public CharSequence getNoDataTip() {
            return noDataTip;
        }

        public Builder setNoDataTip(CharSequence noDataTip) {
            this.noDataTip = noDataTip;
            return this;
        }

        public int getNoDataResId() {
            return noDataResId;
        }

        public Builder setNoDataResId(int noDataResId) {
            this.noDataResId = noDataResId;
            return this;
        }

        public int getNoNetworkResId() {
            return noNetworkResId;
        }

        public Builder setNoNetworkResId(int noNetworkResId) {
            this.noNetworkResId = noNetworkResId;
            return this;
        }

        public CharSequence getNoNetworkTip() {
            return noNetworkTip;
        }

        public Builder setNoNetworkTip(CharSequence noNetworkTip) {
            this.noNetworkTip = noNetworkTip;
            return this;
        }

        public int getViewStatus() {
            return viewStatus;
        }

        public Builder setViewStatus(int viewStatus) {
            this.viewStatus = viewStatus;
            return this;
        }

        public ViewGroup getContainer() {
            return container;
        }

        public Builder setContainer(ViewGroup container) {
            this.container = container;
            return this;
        }

        public Builder setRetryClickListener(IRetryClickListener listener) {
            this.listener = listener;
            return this;
        }

        public IRetryClickListener getRetryClickListener() {
            return listener;
        }

        public Builder setRetryTip(CharSequence tip) {
            this.retryTip = tip;
            return this;
        }

        public CharSequence getRetryTip() {
            return retryTip;
        }

        public CharSequence getNoNetworkBtnTip() {
            return noNetworkBtnTip;
        }

        public Builder setNoNetworkBtnTip(CharSequence noNetworkBtnTip) {
            this.noNetworkBtnTip = noNetworkBtnTip;
            return this;
        }

        public boolean isUseStatusView() {
            return useStatusView;
        }

        public Builder setUseStatusView(boolean useStatusView) {
            this.useStatusView = useStatusView;
            return this;
        }

        public ViewStatusLayout attach() {
            if (container == null) throw new RuntimeException("you must set container first");
            return new ViewStatusLayout(container.getContext(), container, this);
        }


    }

    public interface IRetryClickListener {
        void click(@ViewStatus int viewStatus);
    }

}

view_status_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/ll_empty_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:gravity="center"
        android:orientation="vertical"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">

        <ImageView
            android:id="@+id/iv_no_data_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintVertical_bias="0.2"
            app:layout_constraintVertical_chainStyle="packed"
            app:layout_constraintTop_toTopOf="parent"
            tools:src="@mipmap/empty_order_list"
            app:layout_constraintBottom_toTopOf="@id/tv_no_data_tip"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

        <TextView
            android:id="@+id/tv_no_data_tip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:textSize="15sp"
            tools:text="暂无数据"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/iv_no_data_img"
            app:layout_constraintBottom_toTopOf="@+id/stv_retry"/>

        <Button
            android:id="@+id/stv_retry"
            android:layout_width="120dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:gravity="center"
            android:textSize="16sp"
            android:textColor="@color/white"
            android:text="重试"
            android:visibility="gone"
            tools:visibility="visible"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_no_data_tip"
            app:layout_constraintBottom_toBottomOf="parent"/>
    </androidx.constraintlayout.widget.ConstraintLayout>


    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cl_loading_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:visibility="visible"
        android:visibility="gone">

        <TextView
            android:id="@+id/rtv_loading_bg"
            android:layout_width="120dp"
            android:layout_height="0dp"
            android:background="@drawable/shape_solid_alpha_4"
            app:layout_constraintDimensionRatio="h,1:1"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>
        <ProgressBar
            android:id="@+id/progress"
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:indeterminateTint="#EA392C"
            android:indeterminateTintMode="src_atop"
            app:layout_constraintVertical_chainStyle="packed"
            app:layout_constraintTop_toTopOf="@id/rtv_loading_bg"
            app:layout_constraintStart_toStartOf="@id/rtv_loading_bg"
            app:layout_constraintBottom_toTopOf="@id/tv_loading_tip"
            app:layout_constraintEnd_toEndOf="@id/rtv_loading_bg"/>

        <TextView
            android:id="@+id/tv_loading_tip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:gravity="center"
            android:text="加载中..."
            android:textColor="#333333"
            android:textSize="14sp"
            android:singleLine="true"
            app:layout_constraintTop_toBottomOf="@id/progress"
            app:layout_constraintBottom_toBottomOf="@id/rtv_loading_bg"
            app:layout_constraintStart_toStartOf="@id/rtv_loading_bg"
            app:layout_constraintEnd_toEndOf="@id/rtv_loading_bg"/>
    </androidx.constraintlayout.widget.ConstraintLayout>



</androidx.constraintlayout.widget.ConstraintLayout>

ViewStatus

@IntDef({ViewStatus.EMPTY,ViewStatus.HASDATA, ViewStatus.NODATA,
        ViewStatus.NONETWORK, ViewStatus.LOADING_START, ViewStatus.LOADING_END})
@Retention(RetentionPolicy.SOURCE)
public @interface ViewStatus {
    int HASDATA = 0x01;
    int NODATA = 0x02;
    int NONETWORK = 0x04;
    int LOADING_START = 0x08;
    int LOADING_END = 0x10;
    int EMPTY = 0x20;
}

ids.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item type="id" name="view_status_container" />
    <item type="id" name="ctb_title" />
</resources>

BaseActivity

public abstract class BaseActivity2 extends AppCompatActivity {

    protected ViewStatusLayout mViewStatusLayout;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());

        init();

        initViewStatusLayout();

        initData();

    }



    /***
     * 是否需要全局空数据页
     */
    protected boolean needViewStatus() {
        return true;
    }

    private void initViewStatusLayout() {
        if (!needViewStatus()) {
            return;
        }
        if (mViewStatusLayout != null) {
            return;
        }

        ViewStatusLayout.Builder builder = createViewStatusBuilder();
        if (builder.getContainer() == null) {
            ViewGroup vp = getViewStatusParent();
            builder.setContainer(vp);  //设置ViewStatusLayout的父容器
        }
        if (builder.getContainer() != null) {
            mViewStatusLayout = builder.attach();
        }
    }

    protected ViewStatusLayout.Builder createViewStatusBuilder() {
        return new ViewStatusLayout.Builder()
                .setNoDataTip("暂无数据")
                .setNoDataResId(R.mipmap.empty_order_list)
                .setNoNetworkTip("无网络,请先检查网络连接")
                .setNoNetworkResId(R.mipmap.no_network)
                .setNoNetworkBtnTip("点击重试");
    }


    /**
     * 获取ViewStatusLayout的父容器
     *
     * @return
     */
    protected ViewGroup getViewStatusParent() {
        //如果子类Activity 指定了具体容器,则直接加载到此容器中
        ViewGroup viewStatusContainer = findViewById(R.id.view_status_container);
        Log.e("xyh", "getViewStatusParent: "+viewStatusContainer );
        if (viewStatusContainer != null) {
            return viewStatusContainer;
        }

        ViewGroup content = findViewById(android.R.id.content);
        if (content.getChildCount() == 0) {
            return content;
        }

        //rootV就是子类 Activity 自己定义的根布局
        View rootV = content.getChildAt(0);  //LinearLayout

        //如果子布局不是viewgroup直接加载到content布局中
        if (!(rootV instanceof ViewGroup)) {
            return content;
        }

        ViewGroup firstVp = (ViewGroup) rootV; //LinearLayout
        //如果根布局下没有别的控件,则加载到跟布局中
        if (firstVp.getChildCount() == 0) {
            return firstVp;
        }

        //标题栏
        ViewParent ctb = firstVp.findViewById(R.id.ctb_title); //RelativeLayout
        if (ctb != null) {
            return createStatusView(firstVp);
        } else {
            return firstVp;
        }
    }

    private FrameLayout createStatusView(ViewGroup rootV) {
        FrameLayout container = new FrameLayout(this);
        container.setId(R.id.view_status_container);
        ViewGroup.LayoutParams lp;
        if (rootV instanceof ConstraintLayout) {
            ConstraintLayout.LayoutParams clp = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT,
                    ConstraintLayout.LayoutParams.MATCH_PARENT);
            clp.topToBottom = R.id.ctb_title;
            clp.bottomToBottom = rootV.getId();
            clp.startToStart = rootV.getId();
            lp = clp;
        } else if (rootV instanceof RelativeLayout) {
            RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
            rlp.addRule(RelativeLayout.BELOW, R.id.ctb_title);
            lp = rlp;
        } else {
            lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        }
        rootV.addView(container, lp);

        return container;
    }

    public void showDialog(String title) {
        if (mViewStatusLayout != null) {
            mViewStatusLayout.switchView(ViewStatus.LOADING_START);
        }
    }

    public void dismissDialog() {
        if (mViewStatusLayout != null) {
            mViewStatusLayout.switchView(ViewStatus.LOADING_END);
        }
    }

    protected abstract int getLayoutId();

    protected abstract void init();

    protected abstract void initData();


}

使用

public class ThirdActivity extends BaseActivity2 {

   private TextView mTvContent;

    private FrameLayout mFlContainer;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_third;
    }

    @Override
    protected void init() {
        //mFlContainer = findViewById(R.id.fl_container);
        mTvContent = findViewById(R.id.tv_content);
    }

    @Override
    protected ViewStatusLayout.Builder createViewStatusBuilder() {
        return super.createViewStatusBuilder()
                //.setContainer(mFlContainer) //设置父容器
                //.setUseStatusView(false) //是否使用状态图,有些页面不需要展示无数据页,无网络页
                .setNoDataTip("暂时没有数据哦")
                .setRetryTip("重新加载")
                .setRetryClickListener(new ViewStatusLayout.IRetryClickListener() {
                    @Override
                    public void click(int viewStatus) {
                        loadData();
                    }
                });
    }

    @Override
    protected void initData() {
        loadData();
    }


    //模拟加载数据
    private void loadData() {
        mViewStatusLayout.switchView(ViewStatus.LOADING_START);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                int code = 202;
                switch (code) {
                    case 200:
                        mViewStatusLayout.switchView(ViewStatus.HASDATA);
                        mTvContent.setText("content");
                        break;
                    case 201:
                        mViewStatusLayout.switchView(ViewStatus.NODATA);
                        break;
                    case 202:
                        mViewStatusLayout.switchView(ViewStatus.NONETWORK);
                        break;
                    case 203:
                        mViewStatusLayout.switchView(ViewStatus.EMPTY);
                        break;
                }
            }
        }, 2000);

    }
}

activity_third.xml

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ThirdActivity">

    <RelativeLayout
        android:id="@+id/ctb_title"
        android:layout_width="match_parent"
        android:layout_height="44dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="44dp"
            android:gravity="center"
            android:text="标题"
            android:layout_centerInParent="true"
            android:textSize="18sp"
            android:textStyle="bold" />

    </RelativeLayout>


    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_below="@id/ctb_title"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="18sp" />


    </RelativeLayout>



</RelativeLayout>

还可以在activity_third.xml加上FrameLayout,id写死为view_status_container,其他都不变

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ThirdActivity">

    <RelativeLayout
        android:id="@+id/ctb_title"
        android:layout_width="match_parent"
        android:layout_height="44dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="44dp"
            android:gravity="center"
            android:text="标题"
            android:layout_centerInParent="true"
            android:textSize="18sp"
            android:textStyle="bold" />

    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_below="@id/ctb_title"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
         
            android:textSize="18sp" />

    </RelativeLayout>

   <FrameLayout
        android:layout_below="@id/ctb_title"
        android:id="@+id/view_status_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

如果FrameLayout的id不是view_status_container,要在代码中设置父容器:

<FrameLayout
        android:layout_below="@id/ctb_title"
        android:id="@+id/fl_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    @Override
    protected void init() {
        mFlContainer = findViewById(R.id.fl_container);
    }

    @Override
    protected ViewStatusLayout.Builder createViewStatusBuilder() {
        return super.createViewStatusBuilder()
                .setContainer(mFlContainer) //设置父容器
                //.setUseStatusView(false) //是否使用状态图,有些页面不需要展示无数据页,无网络页
                .setNoDataTip("暂时没有数据哦")
                .setRetryTip("重新加载")
                .setRetryClickListener(new ViewStatusLayout.IRetryClickListener() {
                    @Override
                    public void click(int viewStatus) {
                        loadData();
                    }
                });
    }

BaseFragment

public class BaseFragment2 extends Fragment {

    protected ViewStatusLayout mViewStatusLayout;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        initViewStatusLayout(getView());
    }

    /***
     * 是否需要全局空数据页
     */
    protected boolean needViewStatus(){
        return true;
    }

    private void initViewStatusLayout(View view){
        if(!needViewStatus()) {
            return;
        }
        if(mViewStatusLayout != null ) {
            return;
        }
        ViewStatusLayout.Builder builder = createViewStatusBuilder();
        if(builder.getContainer() == null){
            ViewGroup vp = getViewStatusParent(view);
            builder.setContainer(vp);
        }
        if (builder.getContainer() != null) {
            mViewStatusLayout = builder.attach();
        }
    }

    protected ViewStatusLayout.Builder createViewStatusBuilder(){
        return new ViewStatusLayout.Builder()
                .setNoDataTip("没有找到相关内容~")
                .setNoDataResId(R.mipmap.empty_order_list)
                .setNoNetworkTip("网络不稳定,请重试")
                .setNoNetworkResId(R.mipmap.no_network);
    }



    protected ViewGroup getViewStatusParent(View view) {
        //如果指定了具体容器,则直接加载到此容器中
        ViewGroup viewStatusContainer = view.findViewById(R.id.view_status_container);
        if (viewStatusContainer != null) {
            return viewStatusContainer;
        }
        if(!(view instanceof ViewGroup) || view instanceof ScrollingView) {
            return null;
        }

        viewStatusContainer = (ViewGroup)view;
        //如果跟布局下没有别的控件,则加载到跟布局中
        if (viewStatusContainer.getChildCount() == 0) {
            return viewStatusContainer;
        }
        ViewParent ctb = viewStatusContainer.findViewById(R.id.ctb_title);
        if (ctb != null) {
            return createStatusView(viewStatusContainer);
        } else {
            return viewStatusContainer;
        }
    }
    private FrameLayout createStatusView(ViewGroup rootV) {
        if(getContext() == null) {
            return null;
        }
        FrameLayout container = new FrameLayout(getContext());
        container.setId(R.id.view_status_container);
        ViewGroup.LayoutParams lp;
        if (rootV instanceof ConstraintLayout) {
            ConstraintLayout.LayoutParams clp = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT);
            clp.topToBottom = R.id.ctb_title;
            clp.startToStart = rootV.getId();
            lp = clp;
        } else if (rootV instanceof RelativeLayout) {
            RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
            rlp.addRule(RelativeLayout.BELOW, R.id.ctb_title);
            lp = rlp;
        } else {
            lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        }
        rootV.addView(container, lp);
        return container;
    }




    public void showDialog(String title) {
        if (mViewStatusLayout != null) {
            mViewStatusLayout.switchView(ViewStatus.LOADING_START);
        }
    }

    public void dismissDialog() {
        if (mViewStatusLayout != null) {
            mViewStatusLayout.switchView(ViewStatus.LOADING_END);
        }
    }

}

代码链接

Github上面的一个实现该功能的开源项目:LoadSir

LoadSir的使用

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值