在RecyclerView中隐藏视图

44

我有这样的代码

public static class MyViewHolder extends RecyclerView.ViewHolder {
    @InjectView(R.id.text)
    TextView label;

    public MyViewHolder(View itemView) {
        super(itemView);
        ButterKnife.inject(this, itemView);
    }

    public void hide(boolean hide) {
        label.setVisibility(hide ? View.GONE : View.VISIBLE);
    }
}

它映射到RecyclerView中的单个行。实际上,R.id.text是被充气并在此传递给构造函数的布局的根视图。

我正在使用LinearLayoutManager的默认实现。

bindViewHolder中,我对MyViewHolder的实例调用hide(true),但是该行不像预期的那样折叠,而是变得不可见,保持其高度和在RecyclerView中的位置。有其他人遇到过这个问题吗?

你如何在RecyclerView中隐藏项?


你尝试过使用 RecyclerView.getLayoutManager().removeViewAt() 吗?http://developer.android.com/reference/android/support/v7/widget/RecyclerView.LayoutManager.html#removeViewAt(int) - hoomi
你不应该直接调用那些方法在LM中。它们是为了让LM管理子元素而存在的。 - yigit
你好。请查看我的回答 这里 - Taslim Oseni
6个回答

44

在RV中,没有内置的方法来隐藏子项,但是如果其高度变为0,则它将不可见:)。我假设您的根布局具有一些最小高度(或确切高度),即使它是GONE,它仍然会占用空间。

此外,如果您想要删除视图,请从适配器中删除它,而不是隐藏它。您要隐藏而不是删除的原因是什么?


1
不,我实际上尝试将某些行的高度设置为0。LayoutManager不会考虑行高度的更改。但你说得对,解决方案是从适配器中“删除”有问题的对象。请查看我的答案。 - Tony J Huang
1
LayoutManager确实会考虑项目高度。我想知道您是如何更改它们的。我猜您正在调用view.setLayoutParams,这将触发LayoutManager中的重新布局,并且它将获得新的大小。那里应该有其他问题。 - yigit
哦,你说得对,我刚刚意识到我设置高度的方式是错误的。我会将你的答案标记为正确的。 - Tony J Huang
1
请注意,如果您正在使用装饰器(例如添加项目之间的分隔符),则您的“不可见”项目仍将被装饰。 - Marc Plano-Lesay
@yigit 我的情况有一个原因,我将前两个项目作为常量项目。因此,它们始终是列表中的第1个和第2个项目。列表中的第一项是控制器,第二项是“添加卡片”。当我在控制器中按下按钮时,“添加卡片”会出现。其余的项目是模型列表的标准表示形式。我正在尝试模拟Facebook中的发布功能,在列表顶部始终有一个“发布你的想法”卡片,因此我认为可以通过RecyclerView创建复合视图。但也许这只是错误的做法。 - Neon Warge

42

ViewHolder中新增了setVisibility(boolean isVisible)方法。

你可以更改LayoutManageritemView的参数(宽度和高度):

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    ...

    public void setVisibility(boolean isVisible){
        RecyclerView.LayoutParams param = (RecyclerView.LayoutParams)itemView.getLayoutParams();
        if (isVisible){
            param.height = LinearLayout.LayoutParams.WRAP_CONTENT;
            param.width = LinearLayout.LayoutParams.MATCH_PARENT;
            itemView.setVisibility(View.VISIBLE);
        }else{
            itemView.setVisibility(View.GONE);
            param.height = 0;
            param.width = 0;
        }
        itemView.setLayoutParams(param);
    }

    public ViewHolder(View itemView) {
        super(itemView);
        ...
    }
}

并更改 ItemDecoration(分隔线)的可见性:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    ...

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        ...

        for (int i = 0; i < parent.getChildCount(); i++) {

            if (parent.getChildAt(i).getVisibility() == View.GONE) 
                continue;

            /* draw dividers */    

        }
    }
}

2
虽然这样做可以实现功能,但会导致大量的回收测量,并使滚动非常卡顿。 - MLProgrammer-CiM
@MLProgrammer-CiM 如果列表中有很多隐藏的项目,你应该考虑一些其他的算法来显示它们。例如更改布局管理器。 - Dmitry Velychko
我们需要操作LayoutParams吗?仅使用View.GONE不就可以了吗? - mtsahakis

12

你可以做到!

首先,您需要检测要隐藏的项目的位置。您可以自定义getItemViewType来执行此操作。

接下来,在onCreateViewHolder上,根据视图类型进行操作。您可以像这样做:

if(viewType == TYPE_HIDE) {
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.empty_item, parent, false);
                vHolder = new ViewHolder(context, v, viewType, this);
                break;
        }
        return vHolder; 

空项目是一个没有任何内容的布局(换句话说,它是每次创建时的默认布局)。或者用代码表示:

<?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="match_parent"
    android:orientation="vertical">

</LinearLayout>

希望它有帮助!


3
如果你将 layout_height 设置为 0dpwrap_content,我认为这是一个好主意。如果你将其设置为 match_parent,则每个空单元格都将占据父视图的大小... - Sakiboy

11

好的,最终我做法是这样的:我有一个完整的数据集 myObjects,但有时我只想显示其中的一部分。

由于在 RecyclerView 中设置行的可见性不会导致高度折叠,而设置行高似乎也没有效果,所以我所做的就是保留一个名为myObjectsShown的辅助数据集,它实际上是一个 List<Integer>,用于索引到 myObjects 来确定要显示哪些对象。

然后,我会间歇性地更新 myObjectsShown 以包含正确的索引。

因此,

public int getItemCount() {
    return myObjectsShown.size();
}

public void onBindViewHolder(MyViewHolder holder, int position) {
    Object myObject = myObjects.get(myObjectsShown.get(position));
    // bind object to viewholder here...
} 

2
这很聪明而简单。这应该是最好的答案,因为你真的不需要干涉getItemViewType或其他任何东西。但它可以很好地动态隐藏您不想显示的子项,并且非常好地考虑了高度变化。 - SergeantPeauts
真的很聪明的回答 - Mosa

3

为了在RecyclerView中隐藏视图,我在OnBindViewHolder中隐藏/显示视图:

if (item.isShown) {
    vh.FooterLayout.setVisibility(View.Visible);
} else {
    vh.FooterLayout.setVisibility(View.Gone);
}

例如,从活动中我只需重新绘制所需的项:_postListAdapter.notifyItemChanged(position)// 如果您想显示/隐藏页脚 - 位置是amountOfPosts.size()并更改布尔变量 - amountOfPosts[amountOfPosts.size()].isShown


2
为了完整起见,请注意将视图可见性设置为GONE不会隐藏边距。您需要执行以下操作:
       if(itemView.getVisibility() != GONE) itemView.setVisibility(GONE);
       RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) itemView.getLayoutParams();
       layoutParams.setMargins(0, 0, 0, 0);
       itemView.setLayoutParams(layoutParams);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接