【问题标题】:Expandable list with RecyclerView?RecyclerView 的可扩展列表?
【发布时间】:2014-10-17 06:30:56
【问题描述】:

新的 RecyclerView 可以使用可扩展的列表项吗?喜欢 ExpandableListView?

【问题讨论】:

标签: android android-recyclerview


【解决方案1】:

使用现有的 LayoutManager 很容易做到这一点,这完全取决于您如何管理适配器。

当您想要扩展一个部分时,您只需在标题之后将新项目添加到您的适配器。请记住在执行此操作时调用 notifyItemRangeInserted。要折叠一个部分,您只需删除相关项目,然后调用 notifyItemRangeRemoved()。对于适当通知的任何数据更改,回收器视图将为视图设置动画。添加项目时,会创建一个要填充新项目的区域,新项目会淡入。删除则相反。除了适配器之外,您需要做的就是设置视图样式以向用户传达逻辑结构。

更新:Ryan Brooks 现在写了一封 article 来说明如何做到这一点。

【讨论】:

  • 很好的建议。想知道为什么没有其他人赞成这个答案!
  • 我会看到将这个作为示例添加到 SuperSLiM 作为下一个版本的一部分。
  • Ryan Brooks 现在写了一封 article 来说明如何做到这一点。
  • 很好的建议。为什么这个答案在我必须向下滚动才能找到它的页面下方这么远?它应该显示在最顶部,以便更多人可以更轻松地找到这个漂亮的答案。
  • Ryan Brooks 将他的库标记为已弃用。我想知道是否只是他已经停止支持它,或者事实证明这种方法会破坏某些东西或造成内存泄漏或......
【解决方案2】:

从here获取示例代码实现

在ViewHolder的onClick里面设置ValueAnimator

@Override
public void onClick(final View view) {
    if (mOriginalHeight == 0) {
        mOriginalHeight = view.getHeight();
    }
    ValueAnimator valueAnimator;
    if (!mIsViewExpanded) {
        mIsViewExpanded = true;
        valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
    } else {
        mIsViewExpanded = false;
        valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
    }
    valueAnimator.setDuration(300);
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer value = (Integer) animation.getAnimatedValue();
            view.getLayoutParams().height = value.intValue();
            view.requestLayout();
        }
    });
    valueAnimator.start();

}

这是最终代码

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    private TextView mFriendName;
    private int mOriginalHeight = 0;
    private boolean mIsViewExpanded = false;


    public ViewHolder(RelativeLayout v) {
        super(v);
        mFriendName = (TextView) v.findViewById(R.id.friendName);
        v.setOnClickListener(this);
    }

    @Override
    public void onClick(final View view) {
        if (mOriginalHeight == 0) {
            mOriginalHeight = view.getHeight();
        }
        ValueAnimator valueAnimator;
        if (!mIsViewExpanded) {
            mIsViewExpanded = true;
            valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
        } else {
            mIsViewExpanded = false;
            valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
        }
        valueAnimator.setDuration(300);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer value = (Integer) animation.getAnimatedValue();
                view.getLayoutParams().height = value.intValue();
                view.requestLayout();
            }
        });
        valueAnimator.start();

    }
}

【讨论】:

  • 这不起作用“像ExpandableListView”,因为在这种情况下扩展的内容是一个列表本身,其中包含来自适配器的项目。这是一个退化的解决方案,组内只允许有一项作为子项。
  • 对于完全被回收的视图也不能正常工作
  • 它无法正常工作,因为 RecyclerList 中的视图可能会重复多次,因此当您展开一项时,您会看到列表中的多个项被展开
【解决方案3】:

https://github.com/gabrielemariotti/cardslib

这个库实现了一个带有 recyclerview 的可扩展列表(请参阅“CardViewNative”-->“List、Grid 和 RecyclerView”->“可扩展卡”下的演示应用程序)。它还有许多其他很酷的卡片/列表组合。

【讨论】:

  • 这个 Expandable Cards 列表不是 RecyclerView 的子项(它不扩展 RecyclerView,它只是扩展 ExpandableListVIew)
【解决方案4】:

有人抱怨上述解决方案不适用于列表视图作为可扩展内容。但有一个简单的解决方案:创建一个列表视图并用您的行手动填充此列表视图。

懒惰者的解决方案:如果您不想过多地更改代码,有一个简单的解决方案。 只需手动使用您的适配器创建视图并将它们添加到LinearLayout。

示例如下:

if (mIsExpanded)
{
    // llExpandable... is the expandable nested LinearLayout
    llExpandable.removeAllViews();
    final ArrayAdapter<?> adapter = ... // create your adapter as if you would use it for a ListView
    for (int i = 0; i < adapter.getCount(); i++)
    {
        View item = adapter.getView(i, null, null);
        // if you want the item to be selectable as if it would be in a default ListView, then you can add following code as well:
        item.setBackgroundResource(Functions.getThemeReference(context, android.R.attr.selectableItemBackground));
        item.setTag(i);
        item.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // item would be retrieved with: 
                // adapter.getItem((Integer)v.getTag())
            }
        });
        llExpandable.addView(item);
    }
    ExpandUtils.expand(llExpandable, null, 500);
}
else
{
    ExpandUtils.collapse(llExpandable, null, 500);
}

辅助函数:getThemeReference

public static int getThemeReference(Context context, int attribute)
{
    TypedValue typeValue = new TypedValue();
    context.getTheme().resolveAttribute(attribute, typeValue, false);
    if (typeValue.type == TypedValue.TYPE_REFERENCE)
    {
        int ref = typeValue.data;
        return ref;
    }
    else
    {
        return -1;
    }
}

帮助类:ExpandUtils

Kavin Varnan postet 已经知道如何为布局设置动画... 但是如果你想使用我的课程,请随意,我发布了一个要点:https://gist.github.com/MichaelFlisar/738dfa03a1579cc7338a

【讨论】:

  • “懒惰的解决方案”是一个非常糟糕的主意。将视图添加到可滚动视图内的线性布局是如此低效。
  • 我认为它至少不仅仅是可用的。在我测试过的所有设备上都能快速流畅地运行。顺便说一句,当列表视图不可见时添加视图...只有已经填充的列表视图会在之后显示...
  • 这太棒了!非常感谢
  • 正如@Matthew 提到的,这真的不是一个好主意。拥有一个带有 LinearLayouts 的大型可滚动视图不能很好地扩展。编写 RecyclerView / ListView 和其他类似视图的最大原因之一是优化具有未知大小的大型数据支持列表。构建一个添加了一堆视图的单个视图会抛出所有提供的优化。回收视图是一个巨大的好处,可以让你的应用程序内存高效。除非项目数量很少,否则使用列表处理视图绑定可以节省大量工作。
  • 你完全正确,当然这并不完美,也没有优化任何东西......对于我的用例,我总是只有几个项目......所以这没问题......顺便说一句,与此同时,我找到了一种嵌套回收器视图的方法......只需使用固定高度的水平回收器视图(并不适合每个用例,但仍然)作为嵌套的recyclerview,您可以展开/隐藏这个嵌套的并使用 recyclerview 的所有优化
【解决方案5】:

您可以使用像平滑展开/折叠动画CheckBox一样的ExpandableLayout,因此您可以将其用作ListView和RecyclerView中的CheckBox。

https://github.com/KyoSherlock/ExpandableLayout

【讨论】:

    【解决方案6】:

    这是 @TonicArtos 提到的添加和删除项目并在执行时为其设置动画的示例代码,取自 RecyclerView Animations 和 GitHub sample

    1) 在 onCreateViewHolder() 中添加监听器以注册 onClick

    2) 在您的适配器中创建您的自定义 OnClickListener

    private View.OnClickListener mItemListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            TextView tv = (TextView) v.findViewById(R.id.tvItems);
            String selected = tv.getText().toString();
            boolean checked = itemsList.get(recyclerView.getChildAdapterPosition(v)).isChecked();
    
            switch (selected){
                case "Item1":
                    if(checked){
                        deleteItem(v);
                        itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(false);
                    }else {
                        addItem(v);
                        itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(true);
                    }
                    break;
                case "Item2":
                    if(checked){
                        deleteItem(v);
                        itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(false);
                    }else {
                        addItem(v);
                        itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(true);
                    }
                    break;                 
                default:
                    //In my case I have checkList in subItems,
                    //checkItem(v);
                    break;
            }
        }
    };
    

    3) 添加您的 addItem() 和 deleteItem()

    private void addItem(View view){
        int position = recyclerView.getChildLayoutPosition(view);
        if (position != RecyclerView.NO_POSITION){
            navDrawItems.add(position+1,new mObject());
            navDrawItems.add(position+2,new mObject());
            notifyItemRangeInserted(position+1,2);
        }
    }
    
    
    private void deleteItem(View view) {
        int position = recyclerView.getChildLayoutPosition(view);
        if (position != RecyclerView.NO_POSITION) {
            navDrawItems.remove(position+2);
            navDrawItems.remove(position+1);
            notifyItemRangeRemoved(position+1,2);
        }
    }
    

    4) 如果您的 RecyclerViewAdapter 与 RecyclerView 不在同一个 Activity 中,则在创建时将 recyclerView 的实例传递给 Adapter p>

    5) itemList 是一个 mObject 类型的 ArrayList,它有助于维护项目状态(打开/关闭)、名称、项目类型(子项目/主项目)和设置基于价值观的主题

    public class mObject{
        private String label;
        private int type;
        private boolean checked;
    } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-01
      • 1970-01-01
      相关资源
      最近更新 更多