it-swarm.com.ru

Как показать пустой вид с RecyclerView?

Я привык помещать специальное представление в файл макета как описано в документации ListActivity для отображения при отсутствии данных . Это представление имеет идентификатор "Android:id/empty".

<TextView
    Android:id="@Android:id/empty"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:text="@string/no_data" />

Интересно, как это можно сделать с новым RecyclerView ?

225
JJD

В том же макете, где определено RecyclerView, добавьте TextView:

<Android.support.v7.widget.RecyclerView
    Android:id="@+id/recycler_view"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:scrollbars="vertical" />

<TextView
    Android:id="@+id/empty_view"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:gravity="center"
    Android:visibility="gone"
    Android:text="@string/no_data_available" />

При onCreate или соответствующем обратном вызове вы проверяете, является ли набор данных, который передает ваше RecyclerView, пустым. Если набор данных пуст, RecyclerView также будет пустым. В этом случае сообщение появляется на экране. Если нет, измените его видимость:

private RecyclerView recyclerView;
private TextView emptyView;

// ...

recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
emptyView = (TextView) rootView.findViewById(R.id.empty_view);

// ...

if (dataset.isEmpty()) {
    recyclerView.setVisibility(View.GONE);
    emptyView.setVisibility(View.VISIBLE);
}
else {
    recyclerView.setVisibility(View.VISIBLE);
    emptyView.setVisibility(View.GONE);
}
262
slellis

Для своих проектов я сделал это решение (RecyclerView с методом setEmptyView):

public class RecyclerViewEmptySupport extends RecyclerView {
    private View emptyView;

    private AdapterDataObserver emptyObserver = new AdapterDataObserver() {


        @Override
        public void onChanged() {
            Adapter<?> adapter =  getAdapter();
            if(adapter != null && emptyView != null) {
                if(adapter.getItemCount() == 0) {
                    emptyView.setVisibility(View.VISIBLE);
                    RecyclerViewEmptySupport.this.setVisibility(View.GONE);
                }
                else {
                    emptyView.setVisibility(View.GONE);
                    RecyclerViewEmptySupport.this.setVisibility(View.VISIBLE);
                }
            }

        }
    };

    public RecyclerViewEmptySupport(Context context) {
        super(context);
    }

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

    public RecyclerViewEmptySupport(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);

        if(adapter != null) {
            adapter.registerAdapterDataObserver(emptyObserver);
        }

        emptyObserver.onChanged();
    }

    public void setEmptyView(View emptyView) {
        this.emptyView = emptyView;
    }
}

И вы должны использовать его вместо класса RecyclerView:

<com.maff.utils.RecyclerViewEmptySupport Android:id="@+id/list1"
    Android:layout_height="match_parent"
    Android:layout_width="match_parent"
    />

<TextView Android:id="@+id/list_empty"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:text="Empty"
    />

а также

RecyclerViewEmptySupport list = 
    (RecyclerViewEmptySupport)rootView.findViewById(R.id.list1);
list.setLayoutManager(new LinearLayoutManager(context));
list.setEmptyView(rootView.findViewById(R.id.list_empty));
185
maff91

Вот решение, использующее только пользовательский адаптер с другим типом представления для пустой ситуации.

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

    private static final int VIEW_TYPE_EVENT = 0;
    private static final int VIEW_TYPE_DATE = 1;
    private static final int VIEW_TYPE_EMPTY = 2;

    private ArrayList items;

    public EventAdapter(ArrayList items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        if(items.size() == 0){
            return 1;
        }else {
            return items.size();
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (items.size() == 0) {
            return VIEW_TYPE_EMPTY;
        }else{
            Object item = items.get(position);
            if (item instanceof Event) {
                return VIEW_TYPE_EVENT;
            } else {
                return VIEW_TYPE_DATE;
            }
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        ViewHolder vh;
        if (viewType == VIEW_TYPE_EVENT) {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event, parent, false);
            vh = new ViewHolderEvent(v);
        } else if (viewType == VIEW_TYPE_DATE) {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event_date, parent, false);
            vh = new ViewHolderDate(v);
        } else {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event_empty, parent, false);
            vh = new ViewHolder(v);
        }

        return vh;
    }

    @Override
    public void onBindViewHolder(EventAdapter.ViewHolder viewHolder, 
                                 final int position) {
        int viewType = getItemViewType(position);
        if (viewType == VIEW_TYPE_EVENT) {
            //...
        } else if (viewType == VIEW_TYPE_DATE) {
            //...
        } else if (viewType == VIEW_TYPE_EMPTY) {
            //...
        }
    }

    public static class ViewHolder extends ParentViewHolder {
        public ViewHolder(View v) {
            super(v);
        }
    }

    public static class ViewHolderDate extends ViewHolder {

        public ViewHolderDate(View v) {
            super(v);
        }
    }

    public static class ViewHolderEvent extends ViewHolder {

        public ViewHolderEvent(View v) {
            super(v);
        }
    }

}
57
radu122

Я использую ViewSwitcher

<ViewSwitcher
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:id="@+id/switcher"
    >

    <Android.support.v7.widget.RecyclerView
        Android:id="@+id/list"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        />

    <TextView Android:id="@+id/text_empty"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:text="@string/list_empty"
        Android:gravity="center"
        />

</ViewSwitcher>

в коде вы будете проверять курсор/набор данных и переключаться между представлениями.

void showItems(Cursor items) {
    if (items.size() > 0) {

        mAdapter.switchCursor(items);

        if (R.id.list == mListSwitcher.getNextView().getId()) {
            mListSwitcher.showNext();
        }
    } else if (R.id.text_empty == mListSwitcher.getNextView().getId()) {
        mListSwitcher.showNext();
    }
}

Также вы можете установить анимацию, если хотите, с помощью пары строк кода.

mListSwitcher.setInAnimation(slide_in_left);
mListSwitcher.setOutAnimation(slide_out_right);
39
wnc_21

Так как ответ Кевина не полный.
Это более правильный ответ, если вы используете RecyclerAdapter's notifyItemInserted и notifyItemRemoved для обновления набора данных. Посмотрите версию Kotlin, добавленную другим пользователем ниже.

Джава:

mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {

    @Override
    public void onChanged() {
        super.onChanged();
        checkEmpty();
    }

    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        super.onItemRangeInserted(positionStart, itemCount);
        checkEmpty();
    }

    @Override
    public void onItemRangeRemoved(int positionStart, int itemCount) {
        super.onItemRangeRemoved(positionStart, itemCount);
        checkEmpty();
    }

    void checkEmpty() {
        mEmptyView.setVisibility(mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
    }
});

Котлин

adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
    override fun onChanged() {
        super.onChanged()
        checkEmpty()
    }

    override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
        super.onItemRangeInserted(positionStart, itemCount)
        checkEmpty()
    }

    override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
        super.onItemRangeRemoved(positionStart, itemCount)
        checkEmpty()
    }

    fun checkEmpty() {
        empty_view.visibility = (if (adapter.itemCount == 0) View.VISIBLE else View.GONE)
    }
})
17
wonsuc

RVEmptyObserver

Вместо использования настраиваемого RecyclerView расширение AdapterDataObserver является более простым решением, позволяющим установить настраиваемое View, которое отображается, когда в списке нет элементов:

Пример использования:

RVEmptyObserver observer = new RVEmptyObserver(recyclerView, emptyView)
rvAdapter.registerAdapterDataObserver(observer);

Класс:

public class RVEmptyObserver extends RecyclerView.AdapterDataObserver {
    private View emptyView;
    private RecyclerView recyclerView;

    public RVEmptyObserver(RecyclerView rv, View ev) {
        this.recyclerView = rv;
        this.emptyView    = ev;
        checkIfEmpty();
    }

    private void checkIfEmpty() {
        if (emptyView != null && recyclerView.getAdapter() != null) {
            boolean emptyViewVisible = recyclerView.getAdapter().getItemCount() == 0;
            emptyView.setVisibility(emptyViewVisible ? View.VISIBLE : View.GONE);
            recyclerView.setVisibility(emptyViewVisible ? View.GONE : View.VISIBLE);
        }
    }

    public void onChanged() { checkIfEmpty(); }
    public void onItemRangeInserted(int positionStart, int itemCount) { checkIfEmpty(); }
    public void onItemRangeRemoved(int positionStart, int itemCount) { checkIfEmpty(); }
}
13
Sheharyar

На getItemViewType вашего адаптера проверьте, имеет ли адаптер 0 элементов, и верните другой viewType, если это так.

Затем в вашем onCreateViewHolder проверьте, является ли viewType тем, который вы вернули ранее, и создайте другое представление. В этом случае файл макета с этим TextView

РЕДАКТИРОВАТЬ

Если это все еще не работает, то вы можете установить размер представления программно следующим образом:

Point size = new Point();
((WindowManager)itemView.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getSize(size);

И тогда, когда вы завышаете ваш вид звонка:

inflatedView.getLayoutParams().height = size.y;
inflatedView.getLayoutParams().width = size.x;
9
Pedro Oliveira

Вот мой класс для показа пустого представления, повторного просмотра (когда загрузка API потерпела неудачу) и прогресса загрузки для RecyclerView

public class RecyclerViewEmptyRetryGroup extends RelativeLayout {
    private RecyclerView mRecyclerView;
    private LinearLayout mEmptyView;
    private LinearLayout mRetryView;
    private ProgressBar mProgressBar;
    private OnRetryClick mOnRetryClick;

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

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

    public RecyclerViewEmptyRetryGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void onViewAdded(View child) {
        super.onViewAdded(child);
        if (child.getId() == R.id.recyclerView) {
            mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
            return;
        }
        if (child.getId() == R.id.layout_empty) {
            mEmptyView = (LinearLayout) findViewById(R.id.layout_empty);
            return;
        }
        if (child.getId() == R.id.layout_retry) {
            mRetryView = (LinearLayout) findViewById(R.id.layout_retry);
            mRetryView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mRetryView.setVisibility(View.GONE);
                    mOnRetryClick.onRetry();
                }
            });
            return;
        }
        if (child.getId() == R.id.progress_bar) {
            mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
        }
    }

    public void loading() {
        mRetryView.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.VISIBLE);
    }

    public void empty() {
        mEmptyView.setVisibility(View.VISIBLE);
        mRetryView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public void retry() {
        mRetryView.setVisibility(View.VISIBLE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public void success() {
        mRetryView.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public RecyclerView getRecyclerView() {
        return mRecyclerView;
    }

    public void setOnRetryClick(OnRetryClick onRetryClick) {
        mOnRetryClick = onRetryClick;
    }

    public interface OnRetryClick {
        void onRetry();
    }
}

activity_xml

<...RecyclerViewEmptyRetryGroup
        Android:id="@+id/recyclerViewEmptyRetryGroup">

        <Android.support.v7.widget.RecyclerView
            Android:id="@+id/recyclerView"/>

        <LinearLayout
            Android:id="@+id/layout_empty">
            ...
        </LinearLayout>

        <LinearLayout
            Android:id="@+id/layout_retry">
            ...
        </LinearLayout>

        <ProgressBar
            Android:id="@+id/progress_bar"/>

</...RecyclerViewEmptyRetryGroup>

enter image description here

Источник находится здесь https://github.com/PhanVanLinh/AndroidRecyclerViewWithLoadingEmptyAndRetry

7
Phan Van Linh

Используйте AdapterDataObserver в пользовательском RecyclerView

котлинский:

RecyclerViewEnum.kt

enum class RecyclerViewEnum {
    LOADING,
    NORMAL,
    EMPTY_STATE
}

RecyclerViewEmptyLoadingSupport.kt

class RecyclerViewEmptyLoadingSupport : RecyclerView {

    var stateView: RecyclerViewEnum? = RecyclerViewEnum.LOADING
        set(value) {
            field = value
            setState()
        }
    var emptyStateView: View? = null
    var loadingStateView: View? = null


    constructor(context: Context) : super(context) {}

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {}


    private val dataObserver = object : AdapterDataObserver() {
        override fun onChanged() {
            onChangeState()
        }

        override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
            super.onItemRangeRemoved(positionStart, itemCount)
            onChangeState()
        }

        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
            super.onItemRangeInserted(positionStart, itemCount)
            onChangeState()
        }
    }


    override fun setAdapter(adapter: RecyclerView.Adapter<*>?) {
        super.setAdapter(adapter)
        adapter?.registerAdapterDataObserver(dataObserver)
        dataObserver.onChanged()
    }


    fun onChangeState() {
        if (adapter?.itemCount == 0) {
            emptyStateView?.visibility = View.VISIBLE
            loadingStateView?.visibility = View.GONE
            [email protected] = View.GONE
        } else {
            emptyStateView?.visibility = View.GONE
            loadingStateView?.visibility = View.GONE
            [email protected] = View.VISIBLE
        }
    }

    private fun setState() {

        when (this.stateView) {
            RecyclerViewEnum.LOADING -> {
                loadingStateView?.visibility = View.VISIBLE
                [email protected] = View.GONE
                emptyStateView?.visibility = View.GONE
            }

            RecyclerViewEnum.NORMAL -> {
                loadingStateView?.visibility = View.GONE
                [email protected] = View.VISIBLE
                emptyStateView?.visibility = View.GONE
            }
            RecyclerViewEnum.EMPTY_STATE -> {
                loadingStateView?.visibility = View.GONE
                [email protected] = View.GONE
                emptyStateView?.visibility = View.VISIBLE
            }
        }
    }
}

layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:orientation="vertical">

    <LinearLayout
        Android:id="@+id/emptyView"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:background="@color/white"
        Android:gravity="center"
        Android:orientation="vertical">

        <TextView
            Android:id="@+id/emptyLabelTv"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:text="empty" />
    </LinearLayout>

    <LinearLayout
        Android:id="@+id/loadingView"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:background="@color/white"
        Android:gravity="center"
        Android:orientation="vertical">

        <ProgressBar
            Android:id="@+id/progressBar"
            Android:layout_width="45dp"
            Android:layout_height="45dp"
            Android:layout_gravity="center"
            Android:indeterminate="true"
            Android:theme="@style/progressBarBlue" />
    </LinearLayout>

    <com.peeyade.components.recyclerView.RecyclerViewEmptyLoadingSupport
        Android:id="@+id/recyclerView"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />
</LinearLayout>

в деятельности используйте этот способ:

recyclerView?.apply {
        layoutManager = GridLayoutManager(context, 2)
        emptyStateView = emptyView
        loadingStateView = loadingView
        adapter = adapterGrid
    }

    // you can set LoadingView or emptyView manual
    recyclerView.stateView = RecyclerViewEnum.EMPTY_STATE
    recyclerView.stateView = RecyclerViewEnum.LOADING
4
Rasoul Miri

Я добавил RecyclerView и альтернативный ImageView к RelativeLayout:

<RelativeLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <ImageView
        Android:id="@+id/no_active_jobs"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:src="@mipmap/ic_active_jobs" />

    <Android.support.v7.widget.RecyclerView xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:id="@+id/recyclerView"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />

</RelativeLayout>

а затем в Adapter:

@Override
public int getItemCount() {
    if (mOrders.size() == 0) {
        mRecyclerView.setVisibility(View.INVISIBLE);
    } else {
        mRecyclerView.setVisibility(View.VISIBLE);
    }
    return mOrders.size();
}
2
YTerle

Просто если вы работаете с FirebaseRecyclerAdapter, этот пост работает как очарование https://stackoverflow.com/a/39058636/6507009

1
knightcube