Android通用的EmptyLayout-展示不用状态的界面

原创 2016年08月31日 16:45:17

转载请标明出处: http://www.weyye.me/detail/empty-layout/
本文出自:【Wey Ye的博客】

前言

在做项目的时候,经常会遇到列表数据为空的时候展示的空布局,如果你用的是ListView ,目测会经常使用ListView的一个方法setEmptyView ,如果你用的是RecyclerView,你也许会用自定义View来实现,但是,这些方法虽然使用起来简单,但是如果你提供一个复杂的布局,例如:

在数据加载失败后,添加一个Button让用户可以选择重新加载数据。

你肯定会说,findviewbyId找到这个button,给它设置点击事件,一个两个可以接受,但是,界面多了呢? 那你说了那么多,有没有好的解决办法呢? 当然有 而且是几行代码搞定的

自定义View

接下来就是重头戏 开始编码了 ,首先我们需要继承FrameLayout来实现这样的布局


public class EmptyLayout extends FrameLayout {

    public EmptyLayout(Context context) {
        this(context, null);
    }

    public EmptyLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public EmptyLayout(Context context, AttributeSet attrs, int defStyleAttr) {

    }
}

为了灵活性,我自定义属性来添加所需要的布局,values下面新建attrs.xml


<resources>
    <declare-styleable name="EmptyLayout">
        <attr name="elEmptyLayout"  format="reference"/>
        <attr name="elErrorLayout"  format="reference"/>
        <attr name="elLoadingLayout"  format="reference"/>
    </declare-styleable>
</resources>

然后我们以此找到这些布局,并且添加进去


public class EmptyLayout extends FrameLayout {

    private  Context mContext;
    private View mEmptyView;
    private View mBindView;
    private View mErrorView;
    private Button mBtnReset;
    private View mLoadingView;
    private View loadingView;
    private TextView mEmptyText;
    private TextView tvLoadingText;

    public EmptyLayout(Context context) {
        this(context, null);
    }

    public EmptyLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public EmptyLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext=context;

        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        //居中
        params.gravity = Gravity.CENTER;

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.EmptyLayout, 0, 0);
        //数据为空时的布局
        int emptyLayout = ta.getResourceId(R.styleable.EmptyLayout_elEmptyLayout, R.layout.layout_empty);
        mEmptyView = View.inflate(context, emptyLayout, null);
        mEmptyText =(TextView)mEmptyView.findViewById(R.id.tvEmptyText);
        addView(mEmptyView,params);

        //加载中的布局
        int loadingLayout = ta.getResourceId(R.styleable.EmptyLayout_elLoadingLayout, R.layout.layout_loading);
        mLoadingView = View.inflate(context, loadingLayout, null);
        tvLoadingText =(TextView)mLoadingView.findViewById(R.id.tvLoadingText);
        addView(mLoadingView,params);

        //错误时的布局
        int errorLayout = ta.getResourceId(R.styleable.EmptyLayout_elErrorLayout, R.layout.layout_error);
        mErrorView = View.inflate(context, errorLayout, null);
        mBtnReset =(Button)mErrorView.findViewById(R.id.btnReset);
        addView(mErrorView, params);

        //全部隐藏
        setGone();
    }

    /**
    * 全部隐藏
    */
    private void setGone() {
        mEmptyView.setVisibility(View.GONE);
        mErrorView.setVisibility(View.GONE);
        mLoadingView.setVisibility(View.GONE);
    }
}

简单说下几个变量的作用
mEmptyView 表示数据为空的时候展示给用户
mEmptyText 数据为空提示的文字
mErrorView 加载错误展示给用户
mBtnReset 加载错误重新加载的按钮
mLoadingView 加载中展示给用户
tvLoadingText 加载中提示的文字
mBindView 我们要绑定的view

好了,首先我们找到布局,然后添加进去,如果没有,添加默认的布局。至此,布局已经完成,那怎么控制呢?我们想要的是什么效果呢?

在数据正在加载的时候调用loading方法,显示正在加载中的文本。
在数据加载成,隐藏该view。
在数据加载失败,显示加载失败的文本,并提供一个按钮去刷新数据。

ok,我们按照这个条目一个个的来实现,首先是loading。


    public void showLoading(String text) {
        if (mBindView != null) mBindView.setVisibility(View.GONE);
        if (!TextUtils.isEmpty(text)) tvLoadingText.setText(text);
        setGone();
        mLoadingView.setVisibility(View.VISIBLE);
    }

    public void showLoading() {
        showLoading(null);
    }

首先判断下我们要绑定view是不是为空,不为空则隐藏它,隐藏其他布局,然后展示loadingview

那加载失败了呢?同样简单!

    public void showError() {
        showError(null);
    }

    public void showError(String text) {
        if (mBindView != null) mBindView.setVisibility(View.GONE);
        if (!TextUtils.isEmpty(text)) mBtnReset.setText(text);
        setGone();
        mErrorView.setVisibility(View.VISIBLE);
    }

这个同上

继续看看加载成功的方法,这个更简单啦。


    public void showSuccess() {
        if (mBindView != null) mBindView.setVisibility(View.VISIBLE);
        setGone();
    }

至此,我们整个效果就完成了,在加载数据的时候调用showLoading方法来显示加载中的文本,加载失败后,调用showError来显示加载失败的文本和刷新的按钮,在加载成功后直接隐藏控件

控件倒是完成了,我们还不知道mBindView怎么来的,其实也很简单。我们在代码中需要调用bindView(View view)方法来指定。


    public void bindView(View view) {
        mBindView = view;
    }

那么问题来了,我加载失败后,按钮的点击事件怎么做呢?有人会说用反射,这样既省了代码行数,看着又舒服,但是这样是有个问题存在的,大家都知道,一个项目的上线,都会进行混淆代码的,为了就是防止他人剽窃我们的劳动成果,可是混淆过后哪些class全部变成a,b,c ,这样如果用反射的话就会导致点击事件失效,因为找不到这个类,所以,我们还是老老实实的用onclick事件吧


    public void setOnButtonClick(OnClickListener listener) {
        mBtnReset.setOnClickListener(listener);
    }

这样,一个简单的EmptyLayout就诞生了,接下来我们来看看怎么使用

先看xml布局


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context="me.weyye.emptylayout.MainActivity">

    <me.weyye.library.EmptyLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/emptyLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:elEmptyLayout="@layout/layout_empty"
        app:elErrorLayout="@layout/layout_error"
        app:elLoadingLayout="@layout/layout_loading">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
    </me.weyye.library.EmptyLayout>

</RelativeLayout>

在看看Activity中怎么调用


public class MainActivity extends Activity {

    private EmptyLayout emptyLayout;
    private RecyclerView recyclerView;
    private List<String> list = new ArrayList<>();
    private MyAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        loadData();
    }

    private Handler mHandler = new Handler();


    private void initView() {
        emptyLayout = (EmptyLayout) findViewById(R.id.emptyLayout);
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        recyclerView.setAdapter(adapter = new MyAdapter(list));
        //绑定
        emptyLayout.bindView(recyclerView);
        emptyLayout.setOnButtonClick(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
        //重新加载数据
                loadData();
            }
        });
    }

    private void loadData() {
        //模拟加载数据
        emptyLayout.showLoading("正在加载,请稍后");
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Random r = new Random();
                int res = r.nextInt(10);

                if (res % 2 == 0) {
                    // 失败
                    emptyLayout.showError("加载失败,点击重新加载"); // 显示失败
                } else {
                    // 成功
                    emptyLayout.showSuccess();
                    for (int i = 0; i < 15; i++) {
                        list.add("测试" + i);
                    }
                    adapter.notifyDataSetChanged();
                }
            }
        }, 3000);
    }
}

首页我们找到控件,然后给recyclerView设置adapter,然后我们调用emptyLayout.bindView(recyclerView);来设置要绑定的view,当然这里是recyclerView,接下来,通过emptyLayout.setOnButtonClick()来设置重新加载的时候执行哪个方法,在loadData()中延迟3秒获取数据,数据成功失败都是随机的,当失败的时候会调用emptyLayout.showError(),成功就调用emptyLayout.showSuccess();就这么简单,来看看运行效果

效果展示

效果展示

——————————-Demo下载地址↓——————————-

学习理解并整理下来的笔记。
希望大家能够指点或提出宝贵意见,谢谢!一起学习。
转载请注明出处:http://www.weyye.me/detail/empty-layout/
个人站点:http://weyye.me

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android通用的EmptyLayout-展示不用状态的界面

前言 在做项目的时候,经常会遇到列表数据为空的时候展示的空布局,如果你用的是ListView ,目测会经常使用ListView的一个方法setEmptyView ,如果你用的是RecyclerV...

Android-->打造流行的无数据空布局页面

看图:实现方式,就是在一个自定义View里面, 绘制一个大的圆角矩形, 和三个小的圆角矩形.支持的属性, ...
  • angcyo
  • angcyo
  • 2017年01月01日 17:19
  • 886

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

RecyclerView的加载显示多种布局

RecyclerView是对ListView的封装,所以ListView上能用的方法对RecyclerView同样适用,并且会更简单在实际开发中,我们可能需要一个列表,显示多种布局,getItemVi...

Android打造不一样的EmptyView

大家都对ListView非常熟悉,目测也会经常使用ListView的一个方法setEmptyView,来设置当数据加载中或者数据加载失败的一个提醒的效果,这个方法虽然使用起来简单,但是如果你提供一个复...

Android ListView setEmptyView

http://my.eoe.cn/yaming/archive/879.html 1 当我们使用ListView或GridView的时候,当列...
  • Hknock
  • Hknock
  • 2014年01月09日 18:08
  • 20220

为RecyclerView设置emptyView

RecyclerView不像ListView,它没有提供emptyView的支持,但我们可以自己来实现这个功能。解决思路通过监听Adapter中数据的变化,当数据为空时让我们自定义的emptyView...

[Android] 快速实现一个通用EmptyView

好的APP应当具备良好的交互, 最好能贴心的满足用户的需求. 而人性化的提醒就是其中之一. 某些APP中经常会看到这样的场景, 当加载内容失败, 或者获取内容失败时, 界面会变成一个可与用后交互的场景...
  • stzy00
  • stzy00
  • 2015年08月18日 22:14
  • 1283

Android 加载成功、加载失败、加载中、无数据四个不同界面的切换

自定义一个Framelayout来切换布局。 1.首先自定义一个Framelayout,用addview()把加载失败、加载中、无数据添加到Framelayout;因为加载成功的vi...

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

发现现在主流的APP上,一个页面都会有好几种显示状态,一开始,我是将这些都放在xml中,然后显示隐藏来实现的,感觉很麻烦。 介绍一个方法工具: @Override protect...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android通用的EmptyLayout-展示不用状态的界面
举报原因:
原因补充:

(最多只允许输入30个字)