In Android development, when a list has no data, it usually displays an empty layout. The common practice is to write the list layout (e.g., RecyclerView) and empty layout in the layout file (XML), by hiding/showing the list and empty layout to switch the need to display controls. If we had many pages that needed to display this empty layout, we would have to write repetitive code for each page. This method is not only high coupling degree, but also difficult to implement and maintain. There are also empty layout packaging into public components, by setting the empty layout components to replace the target control, the target control (such as: RecyclerView) to replace the practice. Both work by switching between displaying different controls, and both require us to switch between lists and empty layouts ourselves. Is there a way to make our list automatically display an empty layout without having to switch controls? The answer is certainly there, today I will give you a way to set up an empty layout item to RecyclerView to achieve empty layout display method.

Display list is generally used RecyclerView, RecyclerView display content is provided and managed by Adapter. The Adapter knows whether RecyclerView has data. If there is no data, the Adapter provides an empty layout to RecyclerView display, so that the empty layout function of RecyclerView can be realized. Because RecyclerView supports multiple ViewType items, we can use the empty layout as a ViewType. When there is no data, getItemCount returns 1 and RecyclerView displays an item, which is the empty layout we want to display. The ViewType of item is TYPE_EMPTY(it can be defined arbitrarily, as long as it does not conflict with ordinary items), and the width and height of item is match_parent, so that the empty layout can RecyclerView.

public class EmptyAdapter extends RecyclerView.Adapter<EmptyAdapter.ViewHolder> {

    // Plain item ViewType
    private static final int TYPE_ITEM = 1;
    // Empty layout ViewType
    private static final int TYPE_EMPTY = 2;

    private Context mContext;

    / / data
    private List<String> mList;

    // Whether to display an empty layout
    private boolean showEmptyView = false;

    public EmptyAdapter(Context context) {
        mContext = context;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == TYPE_EMPTY) {
            // Create an empty layout item
            return new ViewHolder(getEmptyView(parent));
        } else {
            // Create a normal item
            View view = LayoutInflater.from(mContext).inflate(R.layout.adapter_item, parent, false);
            return newViewHolder(view); }}/** * get the empty layout */
    private View getEmptyView(ViewGroup parent) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.adapter_empty_view, parent, false);
        Button btnLoadData = view.findViewById(R.id.btn_load_data);
        btnLoadData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) { setList(initData()); }});return view;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        // If it is an empty layout item, no data binding is required
        if (!isEmptyPosition(position)) {
            holder.tvItem.setText(mList.get(position));
        }
    }

    @Override
    public int getItemCount(a) {
        If there is no data and the empty layout needs to be displayed, return 1.
        intcount = mList ! =null ? mList.size() : 0;
        if (count > 0) {
            return count;
        } else if (showEmptyView) {
            // Displays an empty layout
            return 1;
        } else {
            return 0; }}@Override
    public int getItemViewType(int position) {
        if (isEmptyPosition(position)) {
            / / null layout
            return TYPE_EMPTY;
        } else {
            returnTYPE_ITEM; }}public void setList(List<String> list) {
        mList = list;
        notifyDataSetChanged();
    }

    /** * Check if it is empty */
    public boolean isEmptyPosition(int position) {
        intcount = mList ! =null ? mList.size() : 0;
        return position == 0 && showEmptyView && count == 0;
    }

    /** * Sets the empty layout display. */ is not displayed by default
    public void showEmptyView(boolean isShow) {
        if (isShow != showEmptyView) {
            showEmptyView = isShow;
            notifyDataSetChanged();
        }
    }

    public boolean isShowEmptyView(a) {
        return showEmptyView;
    }

    static class ViewHolder extends RecyclerView.ViewHolder {

        TextView tvItem;

        public ViewHolder(@NonNull View itemView) {
            super(itemView); tvItem = itemView.findViewById(R.id.tv_item); }}}Copy the code

Since there will be at most one empty layout in the list, it does not need to rebind data in onBindViewHolder. When we create the empty layout, we can directly set the content to display and set the click events.

In the example above, I provide the empty layout object through the getEmptyView method. This is done to decouple the empty layout from the Adapter, but it is not obvious in this example. If we provide the desired empty layout through a method or interface, then we can have a specific Adapter subclass (or user) try the method (implementation interface), provide the specific empty layout by the subclass or outside, and make the empty layout customizable without affecting the basic functionality of the Adapter. For example, our project will generally encapsulate a BaseAdapter class, or use a third-party open source BaseAdapter, the adapter in the project are inherited from BaseAdapter. We can integrate the functionality of the empty layout into the BaseAdapter and provide the default universal empty layout. Subclasses can then provide their own empty layout by overriding methods, making the empty layout customizable. By integrating the empty layout function into the Adapter and using the Adapter directly, the empty layout is displayed when there is no data, without us having to manually invoke the empty layout show or hide.

mAdapter = new EmptyAdapter(this);
// Displays an empty layout
mAdapter.showEmptyView(true);
rvList.setLayoutManager(new LinearLayoutManager(this));
rvList.setAdapter(mAdapter);
Copy the code

If you use grid layer out manager, setSpanSizeLookup is needed to RecyclerView the empty layout.

    final GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
    layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@override public int getSpanSize(int position) {// If the layout is empty, make it fill a lineif (mAdapter.isEmptyPosition(position)) {
                return layoutManager.getSpanCount();
            }
            return1; }}); rvList.setLayoutManager(layoutManager);Copy the code

At this point, the implementation of the empty layout is complete. For those interested, try out the example I provided on GitHub: EmptyAdapter.

Is considered the empty layout functions are integrated into the adapter, because before I in making open source a grouping RecyclerView adapter: GroupedRecyclerViewAdapter. Later received a lot of feedback, hope to provide GroupedRecyclerViewAdapter empty layout Settings, so I came up with this way of implementation, and give the GroupedRecyclerViewAdapter integrates the function of this. In writing this article today, I hope to share this implementation method with you. Although we are currently implementing an empty layout for RecyclerView Adapter, the same idea applies to ListView and other list controls.