在最近公布的比赛框架中,发现了页面加载管理类,觉得挺有用的,所以做个简单的笔记。
什么是页面加载管理类呢?(大佬可直接跳过翻看实现过程)
如果能有这个问题,那么很好,哈哈哈,你和我一样,刚开始都挺疑惑的。 我们一般在写网络请求的时候,如果不涉及什么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();
}
<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>
<?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);
}
}
好啦,具体就是这样呢,相应的注释都挺详细的,如果有什么地方不对,也欢迎大家指出。