Android各种长见识涨姿势各种第三方

自定义ProgressStateLayout实现网络请求状态间的

2016-10-19  本文已影响919人  Mersens

前言

在Android开发中我们经常遇到这样的场景:去加载网络数据时需要显示进度条,提示用户正在加载,加载失败需要给用户提示加载失败,还需要能够重新加载,数据为空也要给用户相应的提示。对于一个初入编程之世的程序员来说这肯定是令人头(dan)疼(teng)的问题,在开发中他们会这么做:在一个XML布局文件中添加加载成功,失败,数据为空的布局,在请求过程中通过设置View的“显示”和“隐藏”来达到这种效果,每当看到这样写,我的内心是崩溃的(当然,之前我也是这样的O.o),后来随着公司的项目功能扩展也来越庞大,我觉得不能再用这样扯淡的方法来解决这种操蛋的问题,就决定自定义一个控件解决该问题,经过几天的思考ProgressStateLayout诞生了。

思路

提起自定义控件,我们首先就会想到自定义控件的基本流程:onFinishInflate(),onMeasure(),onLayout(),onDraw(),onAttachedToWindow()当然,有这样的想法很正确,但 不一定每个方法都要重写,这需要结合实际的业务需求。在ProgressStateLayout中需要封装四中状态(四个View),这时简单的View已经不能满足我们的需求,我们需要通过ViewGroup来实现该功能。

1:ProgressStateLayout需要继承RelativeLayout。

2:通过LayoutInflater实例化失,数据为空,加载进度的View。

3:重写addView()方法,把View添加到ViewGroup中。

4:通过switchState()方法来实现不同状态的相互切换。

具体实现

ProgressStateLayout需要继承RelativeLayout,然后修改里面的构造方法。让一个参数的调用两个参数的,两个参数的调用三个参数的,在三个参数的构造方法中添加一个Init()方法用于一些初始化操作。

private LayoutParamslayoutParams;

private LayoutInflaterinflater;

private Listviews=null;

private static final StringTAG_LOADING="loading";

private static final StringTAG_EMPTY="empty";

private static final StringTAG_ERROR="error";

private View viewLoading,viewEmpty,viewError;

一个方参数的构造方法

public ProgressStateLayout(Context context) {

this(context,null);

}

两个参数的构造方法

public ProgressStateLayout(Contextcontext, AttributeSet attrs){

this(context, attrs,0);

}

三个参数的构造方法

public ProgressStateLayout(Context context, AttributeSet attrs,intdefStyleAttr) {

super(context, attrs, defStyleAttr);

init();

}


写个接口,用于“重新加载”按钮的点击回调

private ReloadListenerlistener;

//重新加载按钮的接口,用于监听重新加载按钮的监听回调

public interface ReloadListener {

void onClick();

}

枚举类型用于标示四种不同的状态(当然你也可以用int,String类型来表示,这取决于个人的习惯,枚举类型能够很好的规范参数的形式,个人比较喜欢用枚举,至少代码风格会显得高大上一点哈哈)

//四种不同状态

private enum Type {

LOADING,EMPTY,CONTENT,ERROR;

}

init()初始化方法,你会注意到这里实例化了一个List集合,你会想这个集合什么鬼?,当然有用了,别想多了,这个List不是用来保存四个状态的View,是用来保存ViewGroup里面加载成功的View除了正在加载,数据为空,加载失败

/**

*初始化操作

*/

public voidinit() {

views=newArrayList();

inflater= (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

layoutParams=newLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,

ViewGroup.LayoutParams.MATCH_PARENT);

layoutParams.addRule(CENTER_IN_PARENT);

}

设置“正在加载”的View

private void setLoadingView() {

if(viewLoading==null) {

viewLoading=inflater.inflate(R.layout.layout_loading,null);

viewLoading.setTag(TAG_LOADING);

viewLoading.requestLayout();

addView(viewLoading,layoutParams);

}else{

viewLoading.setVisibility(View.VISIBLE);

}}

设置“数据为空”的View

private void setEmptyView(intresid, String msg) {

if(viewEmpty==null) {

viewEmpty=inflater.inflate(R.layout.layout_empty,null);

if(resid !=0) {

ImageView imageView = (ImageView)viewEmpty.findViewById(R.id.img_nodata);

imageView.setImageResource(resid);}

if(!TextUtils.isEmpty(msg)) {

TextView tv_msg = (TextView) findViewById(R.id.text_nodata_tips);

tv_msg.setText(msg);}

viewEmpty.setTag(TAG_EMPTY);

viewEmpty.requestLayout();

addView(viewEmpty,layoutParams);}else{

viewEmpty.setVisibility(View.VISIBLE);

}}

设置“加载失败”的View

private void setErrorView(int resid, String title, String msg, String btntext) {

if (viewError == null) {

viewError = inflater.inflate(R.layout.layout_error, null);

if (resid != 0) {

ImageView img = (ImageView) findViewById(R.id.img_nodata);

img.setImageResource(resid);}

if (!TextUtils.isEmpty(title)) {

TextView tv_title = (TextView) findViewById(R.id.tv_title);

tv_title.setText(title);}

if (!TextUtils.isEmpty(msg)) {

TextView tv_msg = (TextView) findViewById(R.id.tv_msg);

tv_msg.setText(title);}

Button btn_reload = (Button) viewError.findViewById(R.id.btn_reload);

if (!TextUtils.isEmpty(btntext)) {

btn_reload.setText(btntext);}

btn_reload.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

if (listener != null) {

listener.onClick();}}});

viewError.requestLayout();

viewError.setTag(TAG_ERROR);

addView(viewError, layoutParams);} else {

viewError.setVisibility(View.VISIBLE);}

}

设置“加载成功”后ViewGroup里面内容

private void setContentView(booleanflag) {

for(View v :views) {

v.setVisibility(flag ? View.VISIBLE: View.GONE);

}}

设置类型状态的隐藏

private void hideLoadingView() {

if(viewLoading!=null) {

viewLoading.setVisibility(View.GONE);}}

private void hideEmptyView() {

if(viewEmpty!=null) {

viewEmpty.setVisibility(View.GONE);}}

private void hideErrorView() {

if(viewError!=null) {

viewError.setVisibility(View.GONE);}}

对外提供方法用于类型的切换,可以传递信息参数,如果不传,使用默认

public void showLoading() {

switchState(Type.LOADING,

0,null,null,null);}

public void showError(intresid, String title, String msg,

String btntext, ReloadListener listener) {

this.listener= listener;

switchState(Type.ERROR, resid, title, msg, btntext);}

public void showEmpty(intresid, String msg) {

switchState(Type.EMPTY, resid, msg,null,null);}

public void showContent() {

switchState(Type.CONTENT,0,null,null,null);}

public void showError(ReloadListener listener) {

this.listener= listener;

switchState(Type.ERROR,0,null,null,null);}

public void showEmpty() {

switchState(

Type.EMPTY,0,null,null,null);

}

切换状态方法switchState()

/**

*改变状态方法

*@paramtype

*/

private void switchState(Type type,intresid, String title, String msg, String btntext) {

switch(type) {

caseLOADING:

hideEmptyView();

hideErrorView();

setContentView(false);

setLoadingView();

break;

caseEMPTY:

hideErrorView();

hideLoadingView();

setContentView(false);

setEmptyView(resid, title);

break;

caseERROR:

hideEmptyView();

hideLoadingView();

setContentView(false);

setErrorView(resid, title, msg, btntext);

break;

caseCONTENT:

hideEmptyView();

hideLoadingView();

hideErrorView();

setContentView(true);

break;}}

重写addView()方法,这里需要为不同类型的View设置Tag标示,用于区分不同状态的view和父布局内的控件。

@Override

public void addView(View child, ViewGroup.LayoutParams params) {

super.addView(child, params);

//把ProgressStateView内的子控件内容添加到list集合中,保证不同状态间相互切换内容的隐藏与显示

if(child.getTag() ==null|| (!child.getTag().equals(TAG_LOADING) &&

!child.getTag().equals(TAG_EMPTY) && !child.getTag().equals(TAG_ERROR))) {

views.add(child);}

}

how to use

把它当作相对布局使用

截图:

正在加载:

正在加载,progressbar没有显示完整

数据为空:

没有找到数据

请求失败:

加载失败,点击重试

请求成功:

数据请求成功


示例代码请点击:ProgressLayout

结束语

本人是一个技术渣渣,自学Android起步,对Android的极深奥义尚未了解,通过对在开发中的问题进行整理总结,希望对开发中遇到同样问题的小伙伴有所帮助,第一次在这里写文章,文中有很多瑕疵,还请多多关照。

上一篇下一篇

猜你喜欢

热点阅读