带有RecyclerView的可扩展列表?


95

可以在新的RecyclerView中使用可扩展列表项吗?像ExpandableListView吗?


1
你可以看到在谷歌源Android的时钟在Android 6.0
ZYS

@zys在哪里可以找到此android clock示例源代码?
AppiDevo

1
单击扩展按钮时,可以使用不同的viewType加载不同的布局。AndroidClock使用此解决方案:android.googlesource.com/platform/packages/apps/DeskClock
zys


Answers:


121

使用库存LayoutManagers可以很容易地做到这一点,这完全取决于您如何管理适配器。

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

更新:Ryan Brooks现在写了一篇有关如何执行此操作的文章


很棒的建议。不知道为什么没有人赞成这个答案!
x-treme 2015年

我将在下一发行版中看到将其作为示例添加到SuperSLiM的过程。
Tonic Artos

瑞安·布鲁克斯(Ryan Brooks)现在写了一篇有关如何做到这一点的文章
Tonic Artos

很棒的建议。为什么这个答案在页面下方如此之远,我必须向下滚动才能找到它?它应该显示在最顶部,以便更多的人可以更轻松地找到这个漂亮的答案。
RestInPeace

1
瑞安·布鲁克斯(Ryan Brooks)将他的图书馆标记为已弃用。我不知道是因为他不再支持它,还是证明这种方法破坏了某些东西或造成了内存泄漏或其他问题……
Varvara Kalinina

6

此处获取示例代码实现

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

    }
}

11
这不能“像ExpandableListView” 那样工作,因为在这种情况下,扩展内容本身就是一个列表,其中包含来自适配器的项。这是一个退化的解决方案,只有1个项目作为组中的子级被允许。
TWiStErRob 2015年

它也无法与所有可循环使用的视图一起正常使用
注意2015年

由于RecyclerList中的视图可能会重复多次,因此它无法正常工作,因此当您展开一项时,您会看到列表中的多项已展开
Hossein Shahdoost

3

https://github.com/gabrielemariotti/cardslib

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


2
此扩展卡列表不是RecyclerView的子级(它没有扩展RecyclerView,它仅扩展了ExpandableListVIew)
whizzzkey

0

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

懒人的解决方案:如果您不想将代码更改太多,则有一个简单的解决方案。只需手动使用适配器创建视图并将其添加到中即可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)的邮差已经为布局设置动画了……但是,如果您想使用我的班级,请随时这样做,我发布了一个要点:https : //gist.github.com/MichaelFlisar/738dfa03a1579cc7338a


9
“惰性解决方案”是一个非常糟糕的主意。将视图添加到可滚动视图内部的linearlayout效率很低。
马修

我认为这至少超出了可用范围。在我测试过的所有设备上都能快速流畅地工作。顺便说一句,当列表视图不可见时添加视图...之后将仅显示已经填充的列表视图...
prom85

这很整齐!感谢吨
Pawan Kumar 2015年

1
正如@Matthew所提到的,这确实不是一个好主意。带有LinearLayouts的单个大型可滚动视图无法很好地缩放。编写RecyclerView / ListView和其他类似视图的最大原因之一是要优化具有未知大小的大型数据支持列表。使用添加的一堆视图构建单个视图会抛出所有提供的优化。回收视图是一项巨大的好处,它可以提高应用程序的内存效率。除非项目数量很少,否则使用列表处理视图绑定将节省大量工作。
munkay

您完全正确,当然,这不是完美的,也无法优化任何东西。对于我的用例,我总是只有几项...所以这没问题...顺便说一句,我发现了一种方法对于嵌套的回收站视图...只需使用固定高度的水平回收站视图(并非对每个用例来说都是完美的,但仍然)作为嵌套recyclerview,您可以扩展/隐藏此嵌套的recyclerview
视图


0

这是@TonicArtos提到的用于添加和删除Items并在执行过程中对其进行动画处理的示例代码,该代码来自RecyclerView AnimationsGitHub示例

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与Recycler View不在同一活动中,则在创建时将recyclerView实例传递给适配器

5) itemList中是类型的ArrayList的mObject这有助于基于值维持(打开/关闭))项的状态,名称,类型项目的(子项/ mainItem和集主题

public class mObject{
    private String label;
    private int type;
    private boolean checked;
} 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.