自定義ProgressStateLayout實現網絡請求狀態間的相互切換

前言

在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的極深奧義尚未了解,通過對在開發中的問題進行整理總結,希望對開發中遇到同樣問題的小伙伴有所幫助,第一次在這里寫文章,文中有很多瑕疵,還請多多關照。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容