RecyclerView添加EmptyView

时间:2016-01-20 00:07:41

标签: android android-recyclerview

我正在尝试在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; ,但我没有得到任何结果。

我已经关注了这个tutorialtip,但没有人为我工作。

我已实施:

ViewHolder

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

我正在尝试这样做,因为我有一个EmptyView,我希望当列表为空时显示layout,我已经让它以编程方式进行,但我更喜欢像TextViews一样添加,因为我不太了解如何以编程方式放置Buttonsreturn dataList.size() > 0 ? dataList.size() : 1;

如果我把

viewType

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

我调试了if并且始终为1,然后它不会加入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; } 我发现了这个:

 @Override
public int getItemViewType(int position) {

    return list.size() > 0 ? list.size() : 1;
}

但问题是没有改变价值。

修改

我差不多完成了,我这样做了:

layout

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

另外发生的事情是当我将left显示在屏幕的center上时,我将其RecyclerView放在layout上,但我认为这是<?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> 的问题1}}因为emptylayout放在其中。

RecyclerView布局:

<?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>

这是我的 @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; }

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

我认为另一种方式是以编程方式实现它,如下所示:

Adapter

并且:

view

问题

- 我只使用@Jimeux answer添加视图,但我想在list上执行此操作,我得到了它,但即使emptyview.xml也未显示RecyclerView {1}}为空。

- 是时候将xml置于xml之内,然后我将所有这些<details>放在右侧显示的中心。我试图以编程方式添加<server name="A"> <details>id=5 gid=10</details> </server> <server name="B"> <details>id=5 gid=10</details> </server> ,但它就像一个混乱....

5 个答案:

答案 0 :(得分:3)

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

您可以按以下步骤操作:

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

    public abstract class ListItem {
    
        public static final int TYPE_EMPTY = 0;
        public static final int TYPE_MY_OBJ = 1;
    
        abstract public int getType();
    
    }  
    
  2. 为每个List元素类型定义一个类:

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

    这是代码中最重要的部分。您可以使用许多选项来创建此列表。您可以在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();
    } 
    
  4. 为您的RecyclerView定义一个适配器,处理在第3点定义的List。重要的是重写getItemViewType方法,如下所示:

    @Override
    public int getItemViewType(int position) {
        return mItems.get(position).getType();
    }
    
  5. 此外,ViewHolder的类型应该是RecyclerView.ViewHolder(除非您决定在这种情况下创建一个中间类)。

    1. 然后,您需要为空和商业对象项目提供两个布局和ViewHolder。适配器方法应该相应地处理这个问题:

      @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
          }
      }
      
    2. 当然,正如我在开头所写的那样,你不需要为ui表示(EmptyItem和MyObjItem)严格定义中间类型。您甚至可以使用MyObj类型并为其创建表示空占位符的特定配置。这种方法可能不是最好的,以防您将来需要通过包含新的列表项类型来使您的逻辑更复杂。

答案 1 :(得分:2)

编译错误可能是因为您使用主RecyclerView.Adapter扩展ViewHolder作为通用参数。

你应该像

一样
YourAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>

然后适当地投射你的ViewHolders(你可以在这里重复使用getViewType(position))。一定要在方法中切换ViewHolder类型。

答案 2 :(得分:2)

如果我是你,我根本不会把空视图放在适配器中。将其置于您持有RecyclerView的linearpew布局下,并在数据更改时隐藏/显示它。您也可以使用此设置轻松添加加载视图,错误视图等。

这里有一些来自我的应用程序的简化代码,可以为您提供一些想法。 @Bind来自Butter Knife,如果您不熟悉它。您可能还想查看Jake Wharton's u2020 project以了解更多RecyclerView的想法。

//fragment_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:id="@+id/content">
    </FrameLayout>

    <include layout="@layout/status_views" />

</RelativeLayout>

//status_views.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <LinearLayout style="@style/ListStatusView"
        android:id="@+id/empty_view"/>

    <LinearLayout style="@style/ListStatusView"
        android:id="@+id/error_view"/>

    <LinearLayout style="@style/ListStatusView"
        android:id="@+id/loading_view"
        android:padding="30dp"/>

</LinearLayout>

//MyFragment.java
@Bind(R.id.content)      protected ViewGroup contentView;
@Bind(R.id.loading_view) protected ViewGroup loadingView;
@Bind(R.id.empty_view)   protected ViewGroup emptyView;
@Bind(R.id.error_view)   protected ViewGroup errorView;

@Bind({R.id.loading_view, R.id.error_view, R.id.empty_view, R.id.content})
protected List<ViewGroup> stateViews;

protected void activateView(View view) {
    for (ViewGroup vg : stateViews)
        vg.setVisibility(View.GONE);

    view.setVisibility(VISIBLE);
}

@Override
public void onActivityCreated(@Nullable Bundle state) {
    super.onActivityCreated(state);
    if (state == null) {
        activateView(loadingView);
        loadData();
    } else if (data.isEmpty())
        activateView(emptyView);
    else
        activateView(contentView);
}

编辑:这是一个没有Butter Knife的简化版本。

private ViewGroup contentView;
private ViewGroup emptyView;

@Override
protected void onCreate(@Nullable Bundle state) {
    super.onCreate(state);
    setContentView(R.layout.activity_main);
    contentView = (ViewGroup) findViewById(R.id.content_view);
    emptyView = (ViewGroup) findViewById(R.id.empty_view);
}

@Override
public boolean onQueryTextChange(String query) {
    final ArrayList<List> filteredModelList = filter(mModel, query);
    mAdapter.animateTo(filteredModelList);
    rv.scrollToPosition(0);

    if(query.isEmpty()){
        contentView.setVisibility(View.GONE);
        emptyView.setVisibility(View.VISIBLE);
    } else {
        contentView.setVisibility(View.VISIBLE);
        emptyView.setVisibility(View.GONE);
    }
    return true;
}

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rtpew"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true">

    <LinearLayout
        android:id="@+id/content_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>

    <RelativeLayout android:id="@+id/empty_view">
        <ImageView android:src="@drawable/sadface"/>
        <TextView android:text="foo"/>
        <Button android:id="@+id/ButtonAddEntity"/>
    </RelativeLayout>
</RelativeLayout>

答案 3 :(得分:2)

逐一执行以下步骤

1)。由于您的RecyclerView项目有两种类型的视图,因此您的适配器声明应该看起来像普通的

public class YourAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> 

并且listview项和空视图的ViewHolders应该像这样扩展RecyclerView.ViewHolder

 static class ListItemViewHolder extends RecyclerView.ViewHolder {

        public ListItemViewHolder(View itemView) {
            super(itemView);
            // initialize your views here for list items
        }
    }

static class EmptyViewViewHolder extends RecyclerView.ViewHolder {

        public EmptyViewViewHolder(View itemView) {
            super(itemView);
            // initialize your views here for empty list
        }
    }

2)。您必须Override getItemCount()getItemViewType()

@Override
    public int getItemCount() {
        return yourList.size() > 0 ? yourList.size() : 1;// if size of your list is greater than 0, you will return your size of list otherwise 1 for the empty view.
    }

    @Override
    public int getItemViewType(int position) {
        if (yourList.size() == 0) {
            return VIEW_TYPE_EMPTY;
        }
        return position;
    }

3)。您的onCreateViewHolder()现在看起来很相似

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == VIEW_TYPE_EMPTY) {
        return new EmptyViewViewHolder(mLayoutInflater
                .inflate(R.layout.empty_view_layout, parent, false));
    } else {
        return new ListItemViewHolder(mLayoutInflater
                .inflate(R.layout.row_list_item, parent, false));
    }
}

4)。同样检查您是否必须在onBindViewHolder()中使用

@Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (getItemViewType(position) == VIEW_TYPE_EMPTY) {
            EmptyViewViewHolder emptyViewViewHolder = (EmptyViewViewHolder) holder;
            // set values for your empty views
        } else {
            ListItemViewHolder listItemViewHolder = (ListItemViewHolder) holder;
            // set values for your list items
        }
    }

5)。最后Override您的SearcView.setOnQueryTextListener()

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                currentSearchKeyword = newText.trim();
                if(currentSearchKeyword.iseEmpty()){
                    yourList.clear();
                    yourAdapter.notifyDataSetChanged();
                }else{
                   // there will two cases again 1). If your currentSearchKeyword matchces with list results, add that items to your list and notify your adapter. 2) If the currentSearchKeyword doesn't matched with list results, clear your list and notify your adapter;
                }
                return false;
            }
        });

希望它能帮到你,让我知道是否有任何问题。

答案 4 :(得分:1)

以下是您可以尝试的内容:

1。替换

EmptyViewHolder evh = new EmptyViewHolder(v);

RecyclerView.ViewHolder evh = new EmptyViewHolder(v);

这可能是编译失败的原因。

2。替换

@Override
public int getItemViewType(int position) {
    return list.size() > 0 ? list.size() : 1;
}

@Override
public int getItemViewType(int position) {
    return list.get(position) != null ? 1 : 0;
}

要使其生效,您必须在要显示null时插入EmptyView对象:

int progressPosition = list.size();
list.add(null);
adapter.notifyItemInserted(progressPosition);

并在要隐藏null时删除EmptyView对象:

int progressPosition = existingList.size() - 1;
existingList.remove(progressPosition);
adapter.notifyItemRemoved(progressPosition);

此外,您必须按如下方式修改onCreateViewHolder()方法:

 @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == 1) {
            // inflate your default ViewHolder here ...
        } else {
            // inflate the EmptyViewHolder here
        }
    }

我相信我们之前已经讨论过这个问题......请参阅this问题,详细讨论此事。

3。请考虑使用AutoCompleteTextViewFilter,而不是使用SearchView。这可能更容易与RecyclerView的{​​{1}}集成。有关此示例,请参阅this答案。

我会更新这个答案,因为我更了解你的问题...尝试这个并更新我。