Trong RecyclerView
, tôi muốn đặt chế độ xem trống sẽ được hiển thị khi bộ điều hợp trống. Có tương đương với ListView.setEmptyView()
?
Trong RecyclerView
, tôi muốn đặt chế độ xem trống sẽ được hiển thị khi bộ điều hợp trống. Có tương đương với ListView.setEmptyView()
?
Câu trả lời:
Với tính năng liên kết dữ liệu mới, bạn cũng có thể đạt được điều này trực tiếp trong bố cục của mình:
<TextView
android:text="No data to display."
android:visibility="@{dataset.size() > 0 ? View.GONE : View.VISIBLE}" />
Trong trường hợp đó, bạn chỉ cần thêm một biến và một nhập vào phần dữ liệu trong XML của mình:
<data>
<import type="android.view.View"/>
<variable
name="dataset"
type="java.util.List<java.lang.String>"
/>
</data>
Adapter
tập dữ liệu thay vì tập dữ liệu và sử dụng tập dữ liệu đó getItemCount()
hoặc bọc mọi thứ trong một ViewModel
và đặt android:visibility
thành viewModel.getEmptyViewVisibility()
.
Đây là một lớp tương tự như của @dragon sinh ra, nhưng hoàn thiện hơn. Dựa trên ý chính này .
public class EmptyRecyclerView extends RecyclerView {
private View emptyView;
final private AdapterDataObserver observer = new AdapterDataObserver() {
@Override
public void onChanged() {
checkIfEmpty();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
checkIfEmpty();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
checkIfEmpty();
}
};
public EmptyRecyclerView(Context context) {
super(context);
}
public EmptyRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EmptyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
void checkIfEmpty() {
if (emptyView != null && getAdapter() != null) {
final boolean emptyViewVisible = getAdapter().getItemCount() == 0;
emptyView.setVisibility(emptyViewVisible ? VISIBLE : GONE);
setVisibility(emptyViewVisible ? GONE : VISIBLE);
}
}
@Override
public void setAdapter(Adapter adapter) {
final Adapter oldAdapter = getAdapter();
if (oldAdapter != null) {
oldAdapter.unregisterAdapterDataObserver(observer);
}
super.setAdapter(adapter);
if (adapter != null) {
adapter.registerAdapterDataObserver(observer);
}
checkIfEmpty();
}
public void setEmptyView(View emptyView) {
this.emptyView = emptyView;
checkIfEmpty();
}
}
setEmptyView
phương thức mà bạn có thể gọi bất cứ khi nào bạn muốn xác định khung nhìn trống. Xem ListView.setEmptyView
tài liệu nếu nó không rõ ràng, đó là cùng một ý tưởng.
Giải pháp được cung cấp trong liên kết này có vẻ hoàn hảo. Nó sử dụng viewType để xác định thời điểm hiển thị blankView. Không cần tạo RecyclerView tùy chỉnh
Thêm mã từ liên kết trên:
package com.example.androidsampleproject;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class RecyclerViewActivity extends Activity {
RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_view);
recyclerView = (RecyclerView) findViewById(R.id.myList);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new MyAdapter());
}
private class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<String> dataList = new ArrayList<String>();
public class EmptyViewHolder extends RecyclerView.ViewHolder {
public EmptyViewHolder(View itemView) {
super(itemView);
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView data;
public ViewHolder(View v) {
super(v);
data = (TextView) v.findViewById(R.id.data_view);
}
}
@Override
public int getItemCount() {
return dataList.size() > 0 ? dataList.size() : 1;
}
@Override
public int getItemViewType(int position) {
if (dataList.size() == 0) {
return EMPTY_VIEW;
}
return super.getItemViewType(position);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder vho, final int pos) {
if (vho instanceof ViewHolder) {
ViewHolder vh = (ViewHolder) vho;
String pi = dataList.get(pos);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
if (viewType == EMPTY_VIEW) {
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.empty_view, parent, false);
EmptyViewHolder evh = new EmptyViewHolder(v);
return evh;
}
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.data_row, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
private static final int EMPTY_VIEW = 10;
}
}
Tôi chỉ muốn một giải pháp đơn giản như,
đặt RecyclerView của bạn bên trong FrameLayout hoặc RelativeLayout với TextView hoặc chế độ xem khác có hiển thị thông báo dữ liệu trống với khả năng hiển thị GONE theo mặc định và sau đó trong lớp bộ điều hợp, hãy áp dụng logic
Ở đây, tôi có một TextView với thông báo không có dữ liệu
@Override
public int getItemCount() {
textViewNoData.setVisibility(data.size() > 0 ? View.GONE : View.VISIBLE);
return data.size();
}
RVEmptyObserver
:Đó là một triển khai của một AdapterDataObserver
cho phép bạn chỉ cần đặt một View
làm bố cục trống mặc định cho của bạn RecylerView
. Bằng cách này, thay vì sử dụng một tùy chỉnh RecyclerView
và khiến cuộc sống của bạn khó khăn hơn, bạn có thể dễ dàng sử dụng nó với mã hiện có của mình:
Cách sử dụng ví dụ:
RVEmptyObserver observer = new RVEmptyObserver(recyclerView, emptyView)
rvAdapter.registerAdapterDataObserver(observer);
Bạn có thể xem mã và ví dụ sử dụng trong một ứng dụng thực tế tại đây.
Lớp học:
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(); }
}
Phiên bản của tôi, dựa trên https://gist.github.com/adelnizamutdinov/31c8f054d1af4588dc5c
public class EmptyRecyclerView extends RecyclerView {
@Nullable
private View emptyView;
public EmptyRecyclerView(Context context) { super(context); }
public EmptyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); }
public EmptyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private void checkIfEmpty() {
if (emptyView != null && getAdapter() != null) {
emptyView.setVisibility(getAdapter().getItemCount() > 0 ? GONE : VISIBLE);
}
}
private final AdapterDataObserver observer = new AdapterDataObserver() {
@Override
public void onChanged() {
checkIfEmpty();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
checkIfEmpty();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
checkIfEmpty();
}
};
@Override
public void setAdapter(@Nullable Adapter adapter) {
final Adapter oldAdapter = getAdapter();
if (oldAdapter != null) {
oldAdapter.unregisterAdapterDataObserver(observer);
}
super.setAdapter(adapter);
if (adapter != null) {
adapter.registerAdapterDataObserver(observer);
}
checkIfEmpty();
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
if (null != emptyView && (visibility == GONE || visibility == INVISIBLE)) {
emptyView.setVisibility(GONE);
} else {
checkIfEmpty();
}
}
public void setEmptyView(@Nullable View emptyView) {
this.emptyView = emptyView;
checkIfEmpty();
}
}
setVisibility
quá.
Tôi muốn triển khai chức năng này trong Recycler.Adapter
Trên phương thức getItemCount bị ghi đè của bạn, hãy nhập mã kiểm tra trống vào đó:
@Override
public int getItemCount() {
if(data.size() == 0) listIsEmtpy();
return data.size();
}
setVisibility()
sẽ được gọi. Chắc chắn bạn có thể thêm một số cờ để bù đắp nhưng đó là khi nó trở nên phức tạp hơn.
Nếu bạn muốn hỗ trợ nhiều trạng thái hơn như trạng thái tải, trạng thái lỗi thì bạn có thể kiểm tra https://github.com/rockerhieu/rv-adapter-states . Nếu không, hỗ trợ chế độ xem trống có thể được triển khai dễ dàng bằng cách sử dụng RecyclerViewAdapterWrapper
từ ( https://github.com/rockerhieu/rv-adapter ). Ưu điểm chính của cách tiếp cận này là bạn có thể dễ dàng hỗ trợ chế độ xem trống mà không cần thay đổi logic của bộ điều hợp hiện có:
public class StatesRecyclerViewAdapter extends RecyclerViewAdapterWrapper {
private final View vEmptyView;
@IntDef({STATE_NORMAL, STATE_EMPTY})
@Retention(RetentionPolicy.SOURCE)
public @interface State {
}
public static final int STATE_NORMAL = 0;
public static final int STATE_EMPTY = 2;
public static final int TYPE_EMPTY = 1001;
@State
private int state = STATE_NORMAL;
public StatesRecyclerViewAdapter(@NonNull RecyclerView.Adapter wrapped, @Nullable View emptyView) {
super(wrapped);
this.vEmptyView = emptyView;
}
@State
public int getState() {
return state;
}
public void setState(@State int state) {
this.state = state;
getWrappedAdapter().notifyDataSetChanged();
notifyDataSetChanged();
}
@Override
public int getItemCount() {
switch (state) {
case STATE_EMPTY:
return 1;
}
return super.getItemCount();
}
@Override
public int getItemViewType(int position) {
switch (state) {
case STATE_EMPTY:
return TYPE_EMPTY;
}
return super.getItemViewType(position);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_EMPTY:
return new SimpleViewHolder(vEmptyView);
}
return super.onCreateViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (state) {
case STATE_EMPTY:
onBindEmptyViewHolder(holder, position);
break;
default:
super.onBindViewHolder(holder, position);
break;
}
}
public void onBindEmptyViewHolder(RecyclerView.ViewHolder holder, int position) {
}
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
public SimpleViewHolder(View itemView) {
super(itemView);
}
}
}
Sử dụng:
Adapter adapter = originalAdapter();
StatesRecyclerViewAdapter statesRecyclerViewAdapter = new StatesRecyclerViewAdapter(adapter, emptyView);
rv.setAdapter(endlessRecyclerViewAdapter);
// Change the states of the adapter
statesRecyclerViewAdapter.setState(StatesRecyclerViewAdapter.STATE_EMPTY);
statesRecyclerViewAdapter.setState(StatesRecyclerViewAdapter.STATE_NORMAL);
Tôi đã sửa lỗi này:
Đã tạo tệp layout layout_recyclerview_with_emptytext.xml.
Đã tạo EmptyViewRecyclerView.java
---------
EmptyViewRecyclerView blankRecyclerView = (EmptyViewRecyclerView) findViewById (R.id.emptyRecyclerViewLayout);
voidRecyclerView.addAdapter (mPrayerCollectionRecyclerViewAdapter, "Không có lời cầu nguyện nào cho danh mục đã chọn.");
tệp layout_recyclerview_with_emptytext.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/switcher"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<com.ninestars.views.CustomFontTextView android:id="@+id/recyclerViewEmptyTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Empty Text"
android:layout_gravity="center"
android:gravity="center"
android:textStyle="bold"
/>
</merge>
EmptyViewRecyclerView.java
public class EmptyViewRecyclerView extends ViewSwitcher {
private RecyclerView mRecyclerView;
private CustomFontTextView mRecyclerViewExptyTextView;
public EmptyViewRecyclerView(Context context) {
super(context);
initView(context);
}
public EmptyViewRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.layout_recyclerview_with_emptytext, this, true);
mRecyclerViewExptyTextView = (CustomFontTextView) findViewById(R.id.recyclerViewEmptyTextView);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(context));
}
public void addAdapter(final RecyclerView.Adapter<?> adapter) {
mRecyclerView.setAdapter(adapter);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
if(adapter.getItemCount() > 0) {
if (R.id.recyclerView == getNextView().getId()) {
showNext();
}
} else {
if (R.id.recyclerViewEmptyTextView == getNextView().getId()) {
showNext();
}
}
}
});
}
public void addAdapter(final RecyclerView.Adapter<?> adapter, String emptyTextMsg) {
addAdapter(adapter);
setEmptyText(emptyTextMsg);
}
public RecyclerView getRecyclerView() {
return mRecyclerView;
}
public void setEmptyText(String emptyTextMsg) {
mRecyclerViewExptyTextView.setText(emptyTextMsg);
}
}
public class EmptyRecyclerView extends RecyclerView {
@Nullable View emptyView;
public EmptyRecyclerView(Context context) { super(context); }
public EmptyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); }
public EmptyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
void checkIfEmpty() {
if (emptyView != null) {
emptyView.setVisibility(getAdapter().getItemCount() > 0 ? GONE : VISIBLE);
}
}
final @NotNull AdapterDataObserver observer = new AdapterDataObserver() {
@Override public void onChanged() {
super.onChanged();
checkIfEmpty();
}
};
@Override public void setAdapter(@Nullable Adapter adapter) {
final Adapter oldAdapter = getAdapter();
if (oldAdapter != null) {
oldAdapter.unregisterAdapterDataObserver(observer);
}
super.setAdapter(adapter);
if (adapter != null) {
adapter.registerAdapterDataObserver(observer);
}
}
public void setEmptyView(@Nullable View emptyView) {
this.emptyView = emptyView;
checkIfEmpty();
}
}
một cái gì đó như thế này có thể giúp
RecyclerView
khi nào emptyView
hiển thị (và ngược lại). Bạn cũng sẽ cần phải gọi checkIfEmpty()
vào onItemRangeInserted()
và onItemRangeRemoved()
. Ồ, và bạn có thể đã trích dẫn nguồn của mình: gist.github.com/adelnizamutdinov/31c8f054d1af4588dc5c
Tôi nghĩ rằng điều này hoàn thiện hơn với cả ErrorView & EmptyView https://gist.github.com/henrytao-me/2f7f113fb5f2a59987e7
Bạn chỉ có thể sơn văn bản trên RecyclerView
khi nó trống. Các tùy chỉnh sau lớp con hỗ trợ empty
, failed
, loading
, và offline
chế độ. Để biên dịch thành công, hãy thêm recyclerView_stateText
màu sắc vào tài nguyên của bạn.
/**
* {@code RecyclerView} that supports loading and empty states.
*/
public final class SupportRecyclerView extends RecyclerView
{
public enum State
{
NORMAL,
LOADING,
EMPTY,
FAILED,
OFFLINE
}
public SupportRecyclerView(@NonNull Context context)
{
super(context);
setUp(context);
}
public SupportRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs)
{
super(context, attrs);
setUp(context);
}
public SupportRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
setUp(context);
}
private Paint textPaint;
private Rect textBounds;
private PointF textOrigin;
private void setUp(Context c)
{
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setColor(ContextCompat.getColor(c, R.color.recyclerView_stateText));
textBounds = new Rect();
textOrigin = new PointF();
}
private State state;
public State state()
{
return state;
}
public void setState(State newState)
{
state = newState;
calculateLayout(getWidth(), getHeight());
invalidate();
}
private String loadingText = "Loading...";
public void setLoadingText(@StringRes int resId)
{
loadingText = getResources().getString(resId);
}
private String emptyText = "Empty";
public void setEmptyText(@StringRes int resId)
{
emptyText = getResources().getString(resId);
}
private String failedText = "Failed";
public void setFailedText(@StringRes int resId)
{
failedText = getResources().getString(resId);
}
private String offlineText = "Offline";
public void setOfflineText(@StringRes int resId)
{
offlineText = getResources().getString(resId);
}
@Override
public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
String s = stringForCurrentState();
if (s == null)
return;
canvas.drawText(s, textOrigin.x, textOrigin.y, textPaint);
}
private void calculateLayout(int w, int h)
{
String s = stringForCurrentState();
if (s == null)
return;
textPaint.setTextSize(.1f * w);
textPaint.getTextBounds(s, 0, s.length(), textBounds);
textOrigin.set(
w / 2f - textBounds.width() / 2f - textBounds.left,
h / 2f - textBounds.height() / 2f - textBounds.top);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
calculateLayout(w, h);
}
private String stringForCurrentState()
{
if (state == State.EMPTY)
return emptyText;
else if (state == State.LOADING)
return loadingText;
else if (state == State.FAILED)
return failedText;
else if (state == State.OFFLINE)
return offlineText;
else
return null;
}
}
Theo quan điểm của tôi, cách đơn giản nhất để thực hiện một View trống là tạo RecyclerView trống mới với bố cục bạn muốn tăng lên làm nền. Và Bộ điều hợp trống này được đặt khi bạn kiểm tra kích thước tập dữ liệu của mình.