RecyclerView添加EmptyView

Ski*_*ᴉʞS 6 android android-recyclerview

我正试图在我身上实施EmptyView,RecyclerView Adapter但我没有得到任何结果.

我已经按照本教程和这个提示,但没有人为我工作.

我实施了:

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;
Run Code Online (Sandbox Code Playgroud)

但它不允许我编译,因为它们是不同的ViewHolder,因为我创建了两个ViewHolder类,但他们extends Recycler.ViewHolder所以我没有得到它...

我正在尝试这样做,因为我有一个SearchView,我想要当列表为空它显示一个EmptyView,我已经得到它以编程方式做它但我更喜欢添加像一个layout因为我不知道那么多怎么样放置TextViews并以Buttons编程方式.

如果我放

return dataList.size() > 0 ? dataList.size() : 1;
Run Code Online (Sandbox Code Playgroud)

它给了我错误,因为索引是0.

我已经调试了viewType并且始终是1,那么它将不会加入if条件......

深入Android我发现了这个:

/**
 * Return the view type of the item at <code>position</code> for the purposes
 * of view recycling.
 *
 * <p>The default implementation of this method returns 0, making the assumption of
 * a single view type for the adapter. Unlike ListView adapters, types need not
 * be contiguous. Consider using id resources to uniquely identify item view types.
 *
 * @param position position to query
 * @return integer value identifying the type of the view needed to represent the item at
 *                 <code>position</code>. Type codes need not be contiguous.
 */
 public int getItemViewType(int position) {
    return 0;
 }
Run Code Online (Sandbox Code Playgroud)

但问题是没有改变价值.

编辑

我几乎完成了,我这样做了:

 @Override
public int getItemViewType(int position) {

    return list.size() > 0 ? list.size() : 1;
}
Run Code Online (Sandbox Code Playgroud)

但有时当size()为0时它返回0 ...我没有得到它,我正在使用这个SearchView,有时当我输入一个与列表中的任何项目不匹配的字母时,它不会显示,有时它...

另外发生的事情是当我把它放在屏幕上时layout显示的弹出窗口,但是我认为这是问题,因为放入它内部.leftcenterRecyclerViewlayout

RecyclerView布局:

<?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:id="@+id/rtpew"
    android:layout_centerInParent="true"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    >
         <LinearLayout android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:id="@+id/linearpew">
            <android.support.v7.widget.RecyclerView
             android:id="@+id/rv"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
            />
         </LinearLayout>
 </RelativeLayout>
Run Code Online (Sandbox Code Playgroud)

这是我的emptylayout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true">
<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/ImageViewSearchFail"
    android:src="@drawable/sadface"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true" />
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:layout_gravity="center"
    android:textSize="@dimen/15dp"
    android:layout_marginTop="4dp"
    android:text="foo"
    android:layout_below="@+id/ImageViewSearchFail"
    android:layout_centerHorizontal="true" />
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/ButtonAddEntity"
    android:text="foo"
    android:background="?android:selectableItemBackground"
    android:layout_centerVertical="true"
    android:layout_centerHorizontal="true" />
</RelativeLayout>
Run Code Online (Sandbox Code Playgroud)

我想的另一种方式是以编程方式实现它如下:

   @Override
public boolean onQueryTextChange(String query) {
    final ArrayList<List> filteredModelList = filter(mModel, query);
        mAdapter.animateTo(filteredModelList);
        rv.scrollToPosition(0);
    if(query.isEmpty()){
            //Here
    }
  return true;
}
Run Code Online (Sandbox Code Playgroud)

而且:

private ArrayList<List> filter(ArrayList<List> models, String query) {
    query = query.toLowerCase();
    final ArrayList<List> filteredModelList = new ArrayList<List>();
    for (List model : models) {
        final String text = model.getRedName().toLowerCase();
        if (text.contains(query)) {
            filteredModelList.add(model);
        }
    }
    if (filteredModelList.size()<0) {
           //HERE
    }
    else{
           //Delete the views added
    }
    return filteredModelList;
}
Run Code Online (Sandbox Code Playgroud)

问题

- 我只使用@Jimeux答案添加视图,但是我想这样做Adapter,我得到了它,但并不总是显示view即使它list是空的.

- 是时候把emptyview.xml它放在RecyclerView当时,因为我把所有这些xml放在中间,它显示在右边.我试图以xml编程方式添加,但它就像一个混乱....

the*_*les 3

由于您需要处理两种不同类型的视图,因此使用业务对象的中间列表来更轻松地将它们与视图绑定会更容易。想法是在列表中使用一种占位符来表示空状态。从这个意义上说,定义中间层非常有用,它允许您考虑将来应用到列表的最终更改(例如添加元素类型)。此外,通过这种方式,您可以更清楚地将业务模型与 ui 表示分开(例如,您可以实现基于模型对象的内部状态返回 ui 设置的方法)。

您可以按如下方式进行:

  1. 为列表项(例如ListItem)定义专用抽象类型来包装您的业务对象。它的实现可能是这样的:

    public abstract class ListItem {
    
        public static final int TYPE_EMPTY = 0;
        public static final int TYPE_MY_OBJ = 1;
    
        abstract public int getType();
    
    }  
    
    Run Code Online (Sandbox Code Playgroud)
  2. 为每个列表元素类型定义一个类:

    public class EmptyItem extends ListItem {
    
        @Override
        public int getType() {
            return TYPE_EMPTY;
        }
    
    }
    
    public class MyObjItem extends ListItem {
    
        private MyObj obj;
    
        public ContactItem(MyObj obj) {
            this.obj = obj;
        }
    
        public MyObj getMyObj() {
            return obj;
        }
    
        // here you can also add methods for simplify
        // objects rendering (e.g. get background color
        // based on your object internal status)
    
        @Override
        public int getType() {
            return TYPE_MY_OBJ;
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 创建您的清单。

    List<ListItem> mItems = new ArrayList<>();
    if (dataList != null && dataList.size() > 0) {
        for (MyObj obj : dataList) {
            mItems.add(new MyObjItem(obj));
        }
    } else {
        mItems.add(new EmptyItem());
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这是代码中最重要的部分。创建此列表有多种选择。您可以在 RecyclerView 适配器内部或外部执行此操作,但正确处理对其的最终修改非常重要。这对于利用适配器通知方法至关重要。例如,如果您在适配器中创建列表,它可能还应该提供用于添加或删除模型项的方法。例如:

    public void addObj(MyObj obj) {
        if (mItems.size() == 1 && mItems.get(0).getType() == ListItem.EMPTY_TYPE) {
            mItems.clear();
        }
        mItems.add(new MyObjItem(obj));
        notifyDataSetChanged();
    } 
    
    Run Code Online (Sandbox Code Playgroud)
  4. 为您的 RecyclerView 定义一个适配器,处理第 3 点定义的 List。这里重要的是重写 getItemViewType 方法,如下所示:

    @Override
    public int getItemViewType(int position) {
        return mItems.get(position).getType();
    }
    
    Run Code Online (Sandbox Code Playgroud)

此外,ViewHolder 的类型应该是 RecyclerView.ViewHolder (除非您决定在这种情况下创建一个中间类)。

  1. 然后,您需要有两个布局和 ViewHolder 用于空和业务 obj 项目。适配器方法应该相应地处理这个问题:

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == ListItem.TYPE_EMPTY) {
            View itemView = mLayoutInflater.inflate(R.layout.empty_layout, parent, false);
            return new EmptyViewHolder(itemView);
        } else {
            View itemView = mLayoutInflater.inflate(R.layout.myobj_layout, parent, false);
            return new MyObjViewHolder(itemView);
        }
    }
    
    
    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
        int type = getItemViewType(position);
        if (type == ListItem.TYPE_EMPTY) {
            EmptyItem header = (EmptyItem) mItems.get(position);
            EmptyViewHolder holder = (EmptyViewHolder) viewHolder;
            // your logic here... probably nothing to do since it's empty
        } else {            
            MyObjItem event = (MyObjItem) mItems.get(position);
            MyObjViewHolder holder = (MyObjViewHolder) viewHolder;
            // your logic here
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

当然,正如我在开头所写的,您不需要严格定义 ui 表示的中间类型(EmptyItem 和 MyObjItem)。您甚至可以仅使用 MyObj 类型并为其创建代表空占位符的特定配置。如果将来您需要通过包含新的列表项类型来使逻辑更加复杂,这种方法可能不是最好的。