美文网首页
Android通用的EmptyLayout-展示不用状态的界面

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

作者: Goslin | 来源:发表于2018-10-29 12:01 被阅读0次

前言

我们平时在使用许多app时,会发现列表数据为空的时候展示的空布局,网络异常展示网络异常布局,数据加载中的加载布局,即现在携程、美团、饿了么等主流app所使用布局层级,下图是公司项目的截图
简书图片_20181029155450.jpg
简书图片_20181029155455.jpg
简书图片_20181029155459.jpg

下来我们就一步步来实现类似的布局层级。

1.为了灵活性,我自定义属性来添加所需要的布局,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>

2.自定义View
接下来就是重头戏 开始编码了 ,首先我们需要继承FrameLayout来实现这样的布局,然后我们找到第一步的布局,并且添加进去

/**
* Created by Chengxl on 2018/7/25/025.
*  email: robin124@sina.cn
*  qq: 2276794669
*/

public class EmptyLayout extends FrameLayout {

    private  Context mContext;

    private View mEmptyView;

    private View mBindView;

    private View mErrorView;

    private TextView mBtnReset;

    private View mLoadingView;

    private View loadingView;

    private TextView mEmptyText;

    private TextView tvLoadingText;

    private TextView ivImg;

    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.MATCH_PARENT, LayoutParams.MATCH_PARENT);

        //居中

        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);

        ivImg =(TextView)mEmptyView.findViewById(R.id.iv_img);

        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 =(TextView) mErrorView.findViewById(R.id.btnReset);

        addView(mErrorView, params);

        //全部隐藏

        setGone();

    }

    /**

    * 全部隐藏

    */

    private void setGone() {

        mEmptyView.setVisibility(View.GONE);

        mErrorView.setVisibility(View.GONE);

        mLoadingView.setVisibility(View.GONE);

    }

    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);

    }

    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 showEmpty(String text,int imaSrc) {

        if (mBindView != null) mBindView.setVisibility(View.GONE);

        if (!TextUtils.isEmpty(text)) mEmptyText.setText(text);

        ivImg.setBackgroundResource(imaSrc);

        setGone();

        mEmptyView.setVisibility(View.VISIBLE);

    }

    public void showSuccess() {

        if (mBindView != null) mBindView.setVisibility(View.VISIBLE);

        setGone();

    }

    public void bindView(View view) {

        mBindView = view;

    }

    public void setOnButtonClick(OnClickListener listener) {

        mBtnReset.setOnClickListener(listener);

    }

}

3.简单说下几个变量的作用

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就诞生了,

4.接下来我们来看看怎么使用

先看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();就这么简单,来看看项目效果

2fa35bcf10fb800ab6088509a2d03890.gif

作者:Goslin
来源:简书

相关文章

网友评论

      本文标题:Android通用的EmptyLayout-展示不用状态的界面

      本文链接:https://www.haomeiwen.com/subject/lfhbtqtx.html