android 加载成功失败,Android :Activity中切换不同状态页:加载中,加载失败,数据页,空页面……...

在Android应用中,基本每个应用都会有网络加载数据的实现,也基本上需要实现在网络加载数据后出现的不同页面,一般有4种,分别是加载中页面 加载失败显示的页面,加载成功数据的页面 以及 加载没有数据的空页面。如果采用对每一个页面加载数据进行处理的方法,就会很麻烦,接下来就介绍两种方式来解决这个问题,第一种,半彻底式,第二种,彻底式。

先来看第一种方式

1.EmptyLayout(自定义ViewGroup):

先来看看4种效果:分别是 加载错误 空页面 加载有数据 以及加载错误点击重新加载 (设置出来为加载中)

004887bde8c9

GIF.gif

上面简单模拟了下几种状态,就是一个自定义ViewGroup,里面有一个ProgressBar ImageVIew 和一个TextView,先看一下布局:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center"

android:orientation="vertical">

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal">

android:id="@+id/img_error_layout"

android:layout_width="100dp"

android:layout_height="100dp"

android:contentDescription="@null"

android:visibility="gone" />

android:id="@+id/animProgress"

android:layout_width="30dip"

android:layout_height="30dip" />

android:id="@+id/tv_error_layout"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:layout_marginTop="10.0dip"

android:gravity="center"

android:lines="2"

android:textColor="@android:color/darker_gray"

android:textSize="17sp" />

布局很简单,接下来看一下EmptyLayout的代码:

public class EmptyView extends LinearLayout implements View.OnClickListener {

public static final int NETWORK_LOADING = 1; // 加载中

public static final int NODATA = 2; // 没有数据

public static final int NETWORK_ERROR = 3; // 网络错误

public static final int HIDE_LAYOUT = 4; // 隐藏

private int mErrorState = NETWORK_LOADING;//初始化为加载状态

private ProgressBar animProgress;

private ImageView img;

private TextView tv;

private String strNoDataContent;

private String strErrorContent;

private int imgNoDataImage = -1;

private int imgErrorImage = -1;

private OnClickListener listener;

public EmptyView(Context context) {

this(context, null);

}

public EmptyView(Context context, AttributeSet attrs) {

super(context, attrs);

init();

}

private void init() {

View view = LayoutInflater.from(getContext()).inflate(R.layout.view_empty, null);

animProgress = (ProgressBar) view.findViewById(R.id.animProgress);

img = (ImageView) view.findViewById(R.id.img_error_layout);

tv = (TextView) view.findViewById(R.id.tv_error_layout);

//初始化设置

if (getVisibility() == View.GONE) {

setErrorType(HIDE_LAYOUT);

} else {

setErrorType(NETWORK_LOADING);

}

setOnClickListener(this);

//图片去触发点击事件

img.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View view) {

if (listener != null) {

listener.onClick(view);

}

}

});

addView(view);

}

//自定义点击监听(图片会拦截 EmptyVIew的点击事件,所以也需要对图片进行设置点击事件)

public void setOnLayoutClickListener(OnClickListener listener) {

this.listener = listener;

}

//整个EmptyVIew去触发点击事件

@Override

public void onClick(View view) {

if (listener != null) {

listener.onClick(view);

}

}

//判断3种状态

public boolean isLoadError() {

return mErrorState == NETWORK_ERROR;

}

public boolean isLoading() {

return mErrorState == NETWORK_LOADING;

}

public boolean isLoadingNoData() {

return mErrorState == NODATA;

}

//传入不同状态的图片 文字

public void setErrorImag(int imgResource) {

imgErrorImage = imgResource;

}

public void setNoDataImag(int imgResource) {

imgNoDataImage = imgResource;

}

public void setErrorContent(String msg) {

strErrorContent = msg;

}

public void setNoDataContent(String noDataContent) {

strNoDataContent = noDataContent;

}

public void setErrorType(int type) {

setVisibility(View.VISIBLE);

mErrorState = type;

switch (type) {

case NETWORK_LOADING:

animProgress.setVisibility(View.VISIBLE);

img.setVisibility(View.GONE);

tv.setVisibility(View.VISIBLE);

tv.setText("正在加载...");

setVisibility(View.VISIBLE);

break;

case NODATA:

animProgress.setVisibility(View.GONE);

img.setImageResource(imgNoDataImage == -1 ? R.mipmap.empty : imgNoDataImage);

img.setVisibility(View.VISIBLE);

tv.setText(strNoDataContent == null ? "点击屏幕,重新加载" : strNoDataContent);

tv.setVisibility(View.VISIBLE);

setVisibility(View.VISIBLE);

break;

case NETWORK_ERROR:

animProgress.setVisibility(View.GONE);

img.setImageResource(imgErrorImage == -1 ? R.mipmap.error_no_wifi : imgErrorImage);

img.setVisibility(View.VISIBLE);

tv.setText(strErrorContent == null ? "点击屏幕,重新加载" : strErrorContent);

tv.setVisibility(View.VISIBLE);

setVisibility(View.VISIBLE);

break;

case HIDE_LAYOUT:

setVisibility(View.GONE);

break;

default:

break;

}

}

@Override

public void setVisibility(int visibility) {

if (visibility == View.GONE) {

mErrorState = HIDE_LAYOUT;

}

super.setVisibility(visibility);

}

}

也很简单,主要是注意一下逻辑清晰,实现还是蛮容易的,注释都有,就不多做解释了,接下来来看一下对EmptyLayout空间的使用

xml布局中:

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/bill_navi_layout"

android:layout_width="match_parent"

android:layout_height="40dp"

android:background="#EFEFF4"

android:gravity="center_vertical"

android:orientation="horizontal"

android:paddingLeft="10dp">

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="我是有内容的"/>

android:id="@+id/empty"

android:layout_centerInParent="true"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

上面的线性布局简单代表了一个有内容的页面,将EmptyLayout放置在整个布局的中间,操作VIew的Visiable和Gone来显示不同的页面

使用示例:

public class EmptyActivity extends TitleActivity {

@BindView(R.id.empty)

EmptyView empty;

@BindView(R.id.bill_navi_layout)

LinearLayout billNaviLayout;

@Override

public void onInitView(Bundle savedInstanceState) {

super.onInitView(savedInstanceState);

setContentView(R.layout.activity_empty);

ButterKnife.bind(this);

billNaviLayout.setVisibility(View.GONE);//初始化内容页面为不显示

new Thread(new Runnable() {

@Override

public void run() {

try {

Thread.sleep(2000);//模拟加载过程

runOnUiThread(new Runnable() {

@Override

public void run() {

//NETWORK_ERROR NODATA HIDE_LAYOUT NETWORK_LOADING

empty.setErrorType(EmptyView.NETWORK_ERROR);//错误页面

// empty.setErrorType(EmptyView.NODATA);//空页面

// empty.setErrorType(EmptyView.HIDE_LAYOUT);//有内容页面

// billNaviLayout.setVisibility(View.Visiable);//设置为显示

}

});

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}).start();

//点击事件

empty.setOnLayoutClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

empty.setErrorType(EmptyView.NETWORK_LOADING);

}

});

}

}

2.StateViewHelper(基类实现,一劳永逸)

第二种实现方式逻辑比较第一种较为复杂一点,但是在后续的使用上却比第一种更加灵活以及简便,且更符合Android封装的特点,实现始难后易。

看一下很挫的动画吧,功能实现妥妥的,妈的,就是不会写博客啊。。。

004887bde8c9

GIF.gif

上面一样是每一个状态延迟2s后实现,和网络加载后显示不同的VIew一一对应,和EmptyView一样

public class EmptyActivity extends TitleActivity {

@BindView(R.id.empty)

EmptyView empty;

@BindView(R.id.bill_navi_layout)

LinearLayout billNaviLayout;

@Override

public void onInitView(Bundle savedInstanceState) {

super.onInitView(savedInstanceState);

setContentView(R.layout.activity_empty);

ButterKnife.bind(this);

billNaviLayout.setVisibility(View.VISIBLE);//初始化内容页面为不显示

empty.setVisibility(View.GONE);

new Thread(new Runnable() {

@Override

public void run() {

try {

Thread.sleep(2000);//模拟加载过程

runOnUiThread(new Runnable() {

@Override

public void run() {

// setMode(StateViewHelper.MODE_LOADING);

// setMode(StateViewHelper.MODE_ERROR);

setMode(StateViewHelper.MODE_EMPTY);

}

});

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}).start();

}

}

都是先展示自己的ContentView,然后各自设置不同网络加载后的状态VIew,接下来看具体的封装。

首先上自定义的这个StateViewHelper类

public abstract class StateViewHelper {

public static final int MODE_LOADING = 0;

public static final int MODE_CONTENT = 1;

public static final int MODE_EMPTY = 2;

public static final int MODE_ERROR = 3;

private int mMode = MODE_CONTENT;

private View[] mModeViews = new View[4];

private ViewGroup.LayoutParams[] mModeParams = new ViewGroup.LayoutParams[4];

private Context mContext;

private ViewGroup mContent ;

public StateViewHelper(Context context){

super();

this.mContext = context;

}

//将父View添加进来

public void setContentRoot(ViewGroup contentView) {

this.mContent = contentView;

}

//根据Id设置View到父View里面

public void setContentView(int layoutResID) {

View view = LayoutInflater.from(mContext).inflate(layoutResID, mContent, false);

ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT,

ViewGroup.LayoutParams.MATCH_PARENT

);

setContentView(view, params);

}

//设置View到父View里面

public void setContentView(View view) {

ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT,

ViewGroup.LayoutParams.WRAP_CONTENT

);

setContentView(view, params);

}

//根据布局参数添加到父View里面

public void setContentView(View view, ViewGroup.LayoutParams params) {

setModeView(view, params, MODE_CONTENT);

}

//添加四种布局 的View 和 Mode

public void setModeView(View view, int mode) {

ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT,

ViewGroup.LayoutParams.MATCH_PARENT

);

setModeView(view, params, mode);

}

public void setModeView(int view, int mode) {

View subView = LayoutInflater.from(mContext).inflate(view, mContent, false);

ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT,

ViewGroup.LayoutParams.MATCH_PARENT

);

setModeView(subView, params, mode);

}

public void setModeView(View view, ViewGroup.LayoutParams params, int mode) {

checkMode(mode);//检查,只允许4种View中的mode存在

mModeViews[mode] = view;//添加mode 和 Params到数组中

mModeParams[mode] = params;

if(mMode == mode&&mContent!=null){//如果是ContentVIew的话 就添加到父VIew中

clearContentView();

mContent.addView(view,params);

}

}

//通过父View去查找子VIew

public View findViewById(int id){

if(mContent!=null){

View view = mContent.findViewById(id);

return view;

}

return null;

}

//清楚父VIew中的子VIew

private void clearContentView() {

if( mContent!=null){

for (int i = 0, size = mContent.getChildCount(); i < size; i++) {

View subView = mContent.getChildAt(i);

if (onViewClear(subView)) {

mContent.removeView(subView);

}

}

}

}

//子类实现,如果子View不等于null 并且 不等于Toolbar,因为子VIew里面也会包含Toolbar,所以需要去掉

public abstract boolean onViewClear(View subView);

//根据逻辑设置不同的mode,然后去改变子VIew

public void setMode(int mode){

if(mode == mMode) return;

checkMode(mode);

mMode = mode;

clearContentView();

View mModeView = mModeViews[mode];

if (mModeView == null) return;

ViewGroup.LayoutParams mModeParam = mModeParams[mode];

if (mModeParam == null) {

mModeParam = new ViewGroup.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT,

ViewGroup.LayoutParams.WRAP_CONTENT

);

}

if (mContent == null) return;

mContent.addView(mModeView,mModeParam);

//如果是加载的View,就添加动画

if(mode==MODE_LOADING){

final ImageView mAnimationView = (ImageView) findViewById(R.id.h_x_loading);

Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(

mContext, R.anim.loading_animation);

mAnimationView.startAnimation(hyperspaceJumpAnimation);

}

}

private void checkMode(int mode) {

if (mode != MODE_LOADING && mode != MODE_ERROR

&& mode != MODE_EMPTY && mode != MODE_CONTENT) {

throw new IllegalStateException("illegal mode for content, please check StateViewHelper");

}

}

public int getMode() {

return mMode;

}

}

代码注释每一个都写好了,不讲解了,懒。。。

再来看下第2 基类,之所以说是第2基类,就是任何项目都可以去基础的类叫基类,第2基类是说在某一个项目中需要使用到这个功能,就可以定义在第2基类中。

public class TitleActivity extends BaseActivity {

private StateViewHelper stateViewHelper;

private Toolbar mToolbar;

private ActionBar mActionBar;

@Override

public void onInitView(Bundle savedInstanceState) {

super.onInitView(savedInstanceState);

super.setContentView(R.layout.common_title_bar);

mToolbar = (Toolbar) findViewById(R.id.toolbar);

setSupportActionBar(mToolbar);

mActionBar = getSupportActionBar();

if (mActionBar != null) {

mActionBar.setDisplayHomeAsUpEnabled(true);

}

//初始化 为ContentView

stateViewHelper = new BaseStateViewWrapper(this);

setMode(StateViewHelper.MODE_CONTENT);

stateViewHelper.setContentRoot((ViewGroup) findViewById(R.id.container));

//添加三种mode的View进去

stateViewHelper.setModeView(R.layout.view_activity_loading, StateViewHelper.MODE_LOADING);

stateViewHelper.setModeView(R.layout.view_activity_error, StateViewHelper.MODE_ERROR);

stateViewHelper.setModeView(R.layout.view_activity_empty, StateViewHelper.MODE_EMPTY);

}

//先去找父View一个级别的view,如果为null,则去找子VIew里面的view

public View findViewById(int id){

View view = super.findViewById(id);

if(view!=null){

return view;

} else {

return stateViewHelper.findViewById(id);

}

}

public Toolbar getToolbar() {

mToolbar = (Toolbar) findViewById(R.id.toolbar);

return mToolbar;

}

@Override

public void setTitle(CharSequence title) {

super.setTitle(title);

mToolbar.setTitle(title);

}

@Override

public void setTitle(int titleId) {

super.setTitle(titleId);

mToolbar.setTitle(titleId);

}

public void hideTitleBar() {

if (mActionBar != null) {

mActionBar.hide();

}

}

//设置ContentVIew,子类调用

public void setContentView(int layoutResID) {

stateViewHelper.setContentView(layoutResID);

}

public void setContentView(View view) {

stateViewHelper.setContentView(view);

}

public void setContentView(View view, ViewGroup.LayoutParams params) {

stateViewHelper.setContentView(view, params);

}

public void setModeView(View view, int mode) {

stateViewHelper.setModeView(view, mode);

}

public void setModeView(int view, int mode) {

stateViewHelper.setModeView(view, mode);

}

public void setMode(int mode) {

stateViewHelper.setMode(mode);

}

public int getMode() {

return stateViewHelper.getMode();

}

private class BaseStateViewWrapper extends StateViewHelper {

public BaseStateViewWrapper(Context context) {

super(context);

}

@Override

public boolean onViewClear(View subView) {

return subView != null && subView != mToolbar;

}

}

}

第2基类的xml布局:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

android:background="?attr/colorPrimary"

android:fitsSystemWindows="true"

android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

android:id="@+id/container"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:fitsSystemWindows="true"

android:background="@color/theme_color"/>

就是一个Toolbar和一个FrameLayout,FrameLayout用来填充子VIew的。经过这样封装之后,需要改变View,直接使用SetMode(mode),就可以来实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值