Android空数据页面提示控件

前言

通常,当我们的应用页面没有数据可以显示时,我们需要给予用户一些界面提示,以避免空空如也的页面带来较差的用户体验。一般这样的页面都为ListView, ListView 很贴心的提供了

    setEmptyView(View emptyView);

方法来保证用户体验,但是如果你有2年以上的开发经验,你肯定知道,系统的原生控件是永远都满足不了需求的。 setEmptyView 的Bug之一:一般来说现在的应用ListView都会配备下拉刷新,那么当下拉刷新时,ListView是要被清空的,所以你会发现:空数据提示信息忽隐忽现。再者,当刚进页面,数据还没有加载完成时,空数据提示信息已经显示了,当数据加载完之后空数据提示信息又隐藏,说实话,这样的体验真的很差,你应该没有看到哪个应用的效果是这样的吧?

那么好,今天带来的一个自定义控件,就是为了解决这个需求的,同时,它不止满足于ListView,可以应对所有你想出现的页面。

首先看下效果
这里写代码片`

在看代码之前,要说一个开发的技巧:避免相同一段逻辑重复出现两次以上,当一段逻辑代码出现2次以上,你很难保证将来它不会出现3次,4次 ,10次。

所以去规范你的代码,让你的类、方法单一。既可以调高代码可读性,维护性,又可以实现代码复用,最重要的,你的代码可测试了。

实现的技巧:将你的代码按照数据接收、数据转换、逻辑判断、数据输出等进行区分,放到不同方法里。下面的代码算是一个实现,虽然有很多的重载方法,但是你看不到相同的逻辑代码段,修改时,只需要修改一个方法就好了,不需要去每个地方改。

看下类结构:这里写图片描述

然后看代码,先自定义控件的实现。判断一个ListView是否为空,可以通过数据集和Adapter来判断,所以这里写了几个重载方法接收数据集或Adapter。

Message的作用是判断是否什么原因导致无数据的:数据库空、无网络、服务器请求失败。

如果你有不同的需求,只要继承这个类,实现你自己的方法就OK。

/**
 * Created by zhaoxuan.li on 2015/8/28.
 * 无数据时提示的自定义控件
 */
public class NoDataTipsWidget extends RelativeLayout {
    protected static final int TASK_COMPLETED = 1;//网络请求成功
    protected static final int TASK_FAILED = -1;//服务器连接失败
    protected static final int TASK_NONETWORK = -2;//无网络
    private ImageView tipImage;
    private TextView tipText;
    public TipsOnClickListener tipsOnClickListener;

public NoDataTipsWidget(Context context) {
    super(context);
    init(context, null);
}

public NoDataTipsWidget(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs);
}

/**
 * 对控件进行初始化,并取得xml文件里给予的资源
 * @param context
 * @param attrs
 */
private void init(Context context, AttributeSet attrs){
    if(attrs ==null)
        return;
    LayoutInflater.from(context).inflate(R.layout.nodata_tipsview, this, true);
    tipImage = (ImageView)findViewById(R.id.tipImage);
    tipText = (TextView)findViewById(R.id.tipText);

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NoDataTipsWidget);
    CharSequence text = a.getText(R.styleable.NoDataTipsWidget_android_text);
    if(text!=null) tipText.setText(text);
    Drawable drawable = a.getDrawable(R.styleable.NoDataTipsWidget_android_src);
    if(drawable != null) tipImage.setImageDrawable(drawable);
    a.recycle();
    //设置图片的点击事件,通常我们在这里设置点击图片刷新
    tipImage.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {
            if (tipsOnClickListener != null)
                tipsOnClickListener.OnClick();
        }
    });
}


/**
 * 设置提示图片的监听事件,一个事件接口,需要调用处来实现
 * @param onClickListener
 */
public void setTipsClickListener(TipsOnClickListener onClickListener){
    tipsOnClickListener = onClickListener;
}
/**
 * 设置提示文字
 * @param str
 */
public void setText(String str){
    tipText.setText(str);
}

/**
 * 设置提示图片
 * @param drawable
 */
public void setImage(Drawable drawable){
    tipImage.setImageDrawable(drawable);
}

/**
 * 图片点击事件回调接口
 */
public interface TipsOnClickListener{
    void OnClick();
}


/**
 * 重載方法,當無Adapter可以加入時,使用List判斷
 * @param msg   handler Message
 * @param dto   List 数据集
 */
public void doTipsView(Message msg , List dto){ doTipsView(msg, isEmpty(dto));}
/**
 * 重載方法  仅根据List 数据集判断提示样式
 * @param dto   List 数据集
 */
public void doTipsView(List dto){   doTipsView(null,isEmpty(dto));}
/**
 * 重載方法 仅根据Adapter设置提示样式
 * @param adapter   Adapter
 */
public void doTipsView(Adapter adapter){    doTipsView(null,isEmpty(adapter));}
/**
 * 根据 Message  和 Adapter 设置提示样式
 * @param msg   handler Message
 * @param adapter   Adapter 数据集
 */
public void doTipsView(Message msg , Adapter adapter){
    doTipsView(msg, isEmpty(adapter));
}
/**
 * 重载方法仅用于数据接收,具体的判断逻辑由此方法统一处理
 * @param msg
 * @param noData
 */
private  void doTipsView(Message msg , boolean  noData){
    if(!noData){   //如果数据集不为空,就隐藏
        this.setVisibility(View.GONE);
        return ;
    }else{
        if(msg==null){
            showTip_noData();
            return;
        }
        switch (msg.what){
            case TASK_COMPLETED:  //网络请求成功
                showTip_noData();return;
            case TASK_FAILED:   //网络请求失败
                showTip_noServer();return;
            case TASK_NONETWORK:
                showTip_noConnect();return;
        }
    }
}

private boolean isEmpty(Adapter adapter){
    if(adapter == null)
        return true;
    return adapter.isEmpty();
}
private boolean isEmpty(List list){
    if(list == null)
        return true;
    return list.isEmpty();
}

/**
 * 设置样式为无数据样式
 */
public void showTip_noData(){
    tipImage.setImageResource(R.drawable.nodatatip_nodata);
    tipText.setText("无任何数据");
    this.setVisibility(View.VISIBLE);
}
/**
 * 设置样式为无连接样式
 */
public void showTip_noConnect(){
    tipImage.setImageResource(R.drawable.nodatatip_noserver);
    tipText.setText("无网络连接");
    this.setVisibility(View.VISIBLE);
}
/**
 * 设置样式为无服务器样式
 */
public void showTip_noServer(){
    tipImage.setImageResource(R.drawable.nodatatip_noserver);
    tipText.setText("暂时无法连接服务器,请稍后再试");
    this.setVisibility(View.VISIBLE);
}
}

实际上就是一个继承相对布局的View,只不过这个View可以自己控制自己是否显示。View的布局文件很简单一个ImageView一个TextView,这里就不放了。

注释已经写得很清楚了,这里不在详细解释。

另外需要在你要的布局里引入控件:

<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" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:background="#436EEE"
tools:context=".MainActivity">

<ListView
    android:id="@+id/listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

<com.example.zhaoxuanli.nodatatipdemo.widget.NoDataTipsWidget
    android:id="@+id/nodata_tipsview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone"/>

这里有一个限制,就是根布局只能是相对布局或帧布局

Activity中引用。一般情况下,noDataTipsView.doTipsView(msg, list);的调用处实在 数据接收完毕后,或刷新处调用。
目的就是给noDataTipsView最新的数据集让他判断。

public class MainActivity extends AppCompatActivity {
    protected static final int TASK_COMPLETED = 1; //网络请求成功
    protected static final int TASK_FAILED = -1;  //服务器连接失败
    protected static final int TASK_NONETWORK = -2;//无网络

private NoDataTipsWidget noDataTipsView;

/**
 * 因为是一个简单的Demo,空数据提示是肯定要显示的,ListView我们也用不到,就不初始化了
 * @param savedInstanceState
 */
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    noDataTipsView = (NoDataTipsWidget)findViewById(R.id.nodata_tipsview);
    new Timer().schedule(new TimerTask() {

        @Override
        public void run() {
            handler.sendEmptyMessage(-1);// 测试是请求失败的情况
        }
    }, 2000);// 延迟1秒,然后加载
}

Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        ArrayList list = new ArrayList(); //List是空的,所以应该显示空数据提示
        switch (msg.what){
            case TASK_COMPLETED:  //请求成功后处理
                break;
            case TASK_FAILED:  //请求失败后处理
                break;
            case TASK_NONETWORK:  //无网络情况处理
                break;
        }
        //所有状况处理完毕后,调用noDataTipsView.doTipsView()方法 adapter 或List都可以
        noDataTipsView.doTipsView(msg, list);

    };
};

}

最后是源代码 :http://download.csdn.net/detail/u010255127/9198155

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值