RecyclerView展开/折叠项目

时间:2014-11-29 15:23:25

标签: android android-layout android-recyclerview recycler-adapter expand

我想展开/折叠recyclerView的项目以显示更多信息。我希望实现SlideExpandableListView的相同效果。

基本上在我的viewHolder中我有一个不可见的视图,我想做一个平滑的展开/折叠动画,而不是仅将可见性设置为VISIBLE / GONE。我只需要一次扩展一个项目,如果有一个高程来显示该项目已被选中,那将会很酷。

这与新Android最近通话记录列表的效果相同。选项“CALL BACK”和“DETAILS”仅在选择项目时可见。

RecyclerView expandable items

12 个答案:

答案 0 :(得分:173)

请不要使用任何库来实现此效果,而是根据Google I / O使用推荐的方式。在recyclerView的 onBindViewHolder 方法中执行以下操作:

final boolean isExpanded = position==mExpandedPosition;
holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mExpandedPosition = isExpanded ? -1:position;
        TransitionManager.beginDelayedTransition(recyclerView);
        notifyDataSetChanged();
    }
});
  • 详细信息是我的视图,将在触摸时显示(在您的案例中为通话详细信息。默认Visibility.GONE)。
  • mExpandedPosition 是一个初始化为-1的int变量

对于您想要的炫酷效果,请将它们用作list_item属性:

android:background="@drawable/comment_background"
android:stateListAnimator="@animator/comment_selection"

其中comment_background是:

<selector
xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true"
android:enterFadeDuration="@android:integer/config_shortAnimTime"
android:exitFadeDuration="@android:integer/config_shortAnimTime">

<item android:state_activated="true" android:drawable="@color/selected_comment_background" />
<item android:drawable="@color/comment_background" />
</selector>

和comment_selection是:

<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:state_activated="true">
    <objectAnimator
        android:propertyName="translationZ"
        android:valueTo="@dimen/z_card"
        android:duration="2000"
        android:interpolator="@android:interpolator/fast_out_slow_in" />
</item>

<item>
    <objectAnimator
        android:propertyName="translationZ"
        android:valueTo="0dp"
        android:duration="2000"
        android:interpolator="@android:interpolator/fast_out_slow_in" />
</item>
</selector>

答案 1 :(得分:67)

不是说这是最好的方法,但似乎对我有用。

完整代码可在以下位置找到: 示例代码:https://github.com/dbleicher/recyclerview-grid-quickreturn

首先,将展开的区域添加到单元格/项目布局中,并使封闭的单元格布局为animateLayoutChanges =“true”。这将确保展开/折叠动画:

<LinearLayout
    android:id="@+id/llCardBack"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:animateLayoutChanges="true"
    android:padding="4dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center|fill_horizontal"
        android:padding="10dp"
        android:gravity="center"
        android:background="@android:color/holo_green_dark"
        android:text="This is a long title to show wrapping of text in the view."
        android:textColor="@android:color/white"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/tvSubTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center|fill_horizontal"
        android:background="@android:color/holo_purple"
        android:padding="6dp"
        android:text="My subtitle..."
        android:textColor="@android:color/white"
        android:textSize="12sp" />

    <LinearLayout
        android:id="@+id/llExpandArea"
        android:visibility="gone"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="6dp"
            android:text="Item One" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="6dp"
            android:text="Item Two" />

    </LinearLayout>
</LinearLayout>

然后,使您的RV Adapter类实现View.OnClickListener,以便您可以对所单击的项目执行操作。添加一个int字段以保存一个展开视图的位置,并将其初始化为负值:

private int expandedPosition = -1;

最后,实现ViewHolder,onBindViewHolder()方法并覆盖onClick()方法。如果它的位置等于“expandedPosition”,你将在onBindViewHolder中展开视图,如果不是,则隐藏它。您可以在onClick侦听器中设置expandedPosition的值:

@Override
public void onBindViewHolder(RVAdapter.ViewHolder holder, int position) {

    int colorIndex = randy.nextInt(bgColors.length);
    holder.tvTitle.setText(mDataset.get(position));
    holder.tvTitle.setBackgroundColor(bgColors[colorIndex]);
    holder.tvSubTitle.setBackgroundColor(sbgColors[colorIndex]);

    if (position == expandedPosition) {
        holder.llExpandArea.setVisibility(View.VISIBLE);
    } else {
        holder.llExpandArea.setVisibility(View.GONE);
    }
}

@Override
public void onClick(View view) {
    ViewHolder holder = (ViewHolder) view.getTag();
    String theString = mDataset.get(holder.getPosition());

    // Check for an expanded view, collapse if you find one
    if (expandedPosition >= 0) {
        int prev = expandedPosition;
        notifyItemChanged(prev);
    }
    // Set the current position to "expanded"
    expandedPosition = holder.getPosition();
    notifyItemChanged(expandedPosition);

    Toast.makeText(mContext, "Clicked: "+theString, Toast.LENGTH_SHORT).show();
}

/**
 * Create a ViewHolder to represent your cell layout
 * and data element structure
 */
public static class ViewHolder extends RecyclerView.ViewHolder {
    TextView tvTitle;
    TextView tvSubTitle;
    LinearLayout llExpandArea;

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

        tvTitle = (TextView) itemView.findViewById(R.id.tvTitle);
        tvSubTitle = (TextView) itemView.findViewById(R.id.tvSubTitle);
        llExpandArea = (LinearLayout) itemView.findViewById(R.id.llExpandArea);
    }
}

这应该一次只扩展一个项目,使用系统默认动画进行布局更改。至少它对我有用。希望它有所帮助。

答案 2 :(得分:62)

为此,只需要简单的线条就不复杂了

在你的onBindViewHolder方法中添加以下代码

final boolean isExpanded = position==mExpandedPosition;
holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mExpandedPosition = isExpanded ? -1:position;
        notifyItemChanged(position);
    }
});
  

mExpandedPosition 是一个初始化为-1的int全局变量

对于那些只想要扩展一个项目而其他项目被折叠的人。使用此

首先使用previousExpandedPosition = -1

声明一个全局变量

然后

    final boolean isExpanded = position==mExpandedPosition;
    holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);
    holder.itemView.setActivated(isExpanded);

    if (isExpanded)
       previousExpandedPosition = position;

    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mExpandedPosition = isExpanded ? -1:position;
            notifyItemChanged(previousExpandedPosition);
            notifyItemChanged(position);
        }
    });

完成!!!。简单而谦逊...... :)

答案 3 :(得分:14)

有一个非常简单易用的库,支持gradle:https://github.com/cachapa/ExpandableLayout

直接来自图书馆文档:

<net.cachapa.expandablelayout.ExpandableLinearLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:el_duration="1000"
    app:el_expanded="true">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Click here to toggle expansion" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="Fixed height"
        app:layout_expandable="true" />

 </net.cachapa.expandablelayout.ExpandableLinearLayout>

在您标记可展开视图后,只需在容器上调用以下任何方法:expand()collapse()toggle()

答案 4 :(得分:8)

根本不需要使用第三方库。 Google I / O 2016 和Heisenberg在此主题中演示的方法中的小调整可以解决问题。

由于notifyDataSetChanged() 重新绘制完整的RecyclerView notifyDataItemChanged()是更好的选择(不是最佳选择),因为我们有位置和{{1}我们可以使用ViewHolder重绘特定位置的特定notifyDataItemChanged()

但问题是即使使用了ViewHolderViewHolder在点击及其出现时的过早消失也不会消除。

以下代码不会诉诸notifyDataItemChanged()notifyDataSetChanged()并在API 23上进行测试,并且在RecyclerView上使用时就像魅力一样,其中每个ViewHolder都有{ {1}}作为它的根元素:

notifyDataItemChanged()

CardView是一个初始化为-1的全局整数。 holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final boolean visibility = holder.details.getVisibility()==View.VISIBLE; if (!visibility) { holder.itemView.setActivated(true); holder.details.setVisibility(View.VISIBLE); if (prev_expanded!=-1 && prev_expanded!=position) { recycler.findViewHolderForLayoutPosition(prev_expanded).itemView.setActivated(false); recycler.findViewHolderForLayoutPosition(prev_expanded).itemView.findViewById(R.id.cpl_details).setVisibility(View.GONE); } prev_expanded = position; } else { holder.itemView.setActivated(false); holder.details.setVisibility(View.GONE); } TransitionManager.beginDelayedTransition(recycler); } }); 是展开时展示的完整视图,折叠后会隐藏。

如上所述,prev_position的根元素是detailsViewHolderCardView属性的定义与海森堡就此主题完全相同。

更新:如果其中一个已展开,则上述演示将折叠优先扩展的项目。要修改此行为并保持扩展项目,即使展开了其他项目,您也需要以下代码。

foreground

更新:展开列表中的最后一项时,可能无法使其完全可见,因为展开的部分位于屏幕下方。要获取屏幕中的完整项目,请使用以下代码。

stateListAnimator

重要事项:要使上述演示工作,必须在其代码中保留RecyclerView&amp;的实例。它的LayoutManager(灵活性较晚)

答案 5 :(得分:6)

我知道自原始问题发布以来已经很长时间了。但我认为对于像我这样缓慢的人来说@Hisenberg的回答会有所帮助。

在适配器类中声明两个变量

private int mExpandedPosition= -1;
private RecyclerView recyclerView = null;

然后在原来的答案中给出 onBindViewHolder

      // This line checks if the item displayed on screen 
      // was expanded or not (Remembering the fact that Recycler View )
      // reuses views so onBindViewHolder will be called for all
      // items visible on screen.
    final boolean isExpanded = position==mExpandedPosition;

        //This line hides or shows the layout in question
        holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);

        // I do not know what the heck this is :)
        holder.itemView.setActivated(isExpanded);

        // Click event for each item (itemView is an in-built variable of holder class)
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

 // if the clicked item is already expaned then return -1 
//else return the position (this works with notifyDatasetchanged )
                mExpandedPosition = isExpanded ? -1:position;
    // fancy animations can skip if like
                TransitionManager.beginDelayedTransition(recyclerView);
    //This will call the onBindViewHolder for all the itemViews on Screen
                notifyDataSetChanged();
            }
        });

最后,在适配器覆盖

中获取recyclerView对象
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);

    this.recyclerView = recyclerView;
}

希望这有助于。

答案 6 :(得分:5)

使用推荐的方法实现RecyclerView expand/collapse items HeisenBerg {{3}}所应用的RecyclerView实施的可展开/可折叠项目,RecyclerView时我看到了一些明显的文物通过调用TransitionManager.beginDelayedTransition(ViewGroup)和随后notifyDatasetChanged()来刷新。

他最初的回答:

final boolean isExpanded = position==mExpandedPosition;
holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mExpandedPosition = isExpanded ? -1 : position;
        TransitionManager.beginDelayedTransition(recyclerView);
        notifyDataSetChanged();
    }
});

修改:

final boolean isExpanded = position == mExpandedPosition;
holder.details.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mExpandedHolder != null) {
            mExpandedHolder.details.setVisibility(View.GONE);
            notifyItemChanged(mExpandedPosition);
        }
        mExpandedPosition = isExpanded ? -1 : holder.getAdapterPosition();
        mExpandedHolder = isExpanded ? null : holder;
        notifyItemChanged(holder.getAdapterPosition());
    }
}
  • 详细信息是您要在项展开/折叠期间显示/隐藏的视图
  • mExpandedPosition 是跟踪展开项目的int
  • mExpandedHolder 是项目崩溃期间使用的ViewHolder

请注意,方法TransitionManager.beginDelayedTransition(ViewGroup)notifyDataSetChanged()已替换为notifyItemChanged(int),以定位特定项目和一些小调整。

修改后,之前不需要的效果应该消失。但是,这可能不是完美的解决方案。它只做我想要的,消除了眼睛。

<强> :: EDIT ::

为了澄清,mExpandedPositionmExpandedHolder都是全局的。

答案 7 :(得分:3)

将onClick侦听器设置为ViewHolder类后执行以下操作:

@Override
    public void onClick(View v) {
        final int originalHeight = yourLinearLayout.getHeight();
        animationDown(YourLinearLayout, originalHeight);//here put the name of you layout that have the options to expand.
    }

    //Animation for devices with kitkat and below
    public void animationDown(LinearLayout billChoices, int originalHeight){

        // Declare a ValueAnimator object
        ValueAnimator valueAnimator;
        if (!billChoices.isShown()) {
            billChoices.setVisibility(View.VISIBLE);
            billChoices.setEnabled(true);
            valueAnimator = ValueAnimator.ofInt(0, originalHeight+originalHeight); // These values in this method can be changed to expand however much you like
        } else {
            valueAnimator = ValueAnimator.ofInt(originalHeight+originalHeight, 0);

            Animation a = new AlphaAnimation(1.00f, 0.00f); // Fade out

            a.setDuration(200);
            // Set a listener to the animation and configure onAnimationEnd
            a.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    billChoices.setVisibility(View.INVISIBLE);
                    billChoices.setEnabled(false);
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
            // Set the animation on the custom view
            billChoices.startAnimation(a);
        }
        valueAnimator.setDuration(200);
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer value = (Integer) animation.getAnimatedValue();
                billChoices.getLayoutParams().height = value.intValue();
                billChoices.requestLayout();
            }
        });


        valueAnimator.start();
    }
}

我认为这应该会有所帮助,这就是我实施的方式,并且在最近的通话视图中也是如此。

答案 8 :(得分:3)

您可以使用ExpandableLayout。 https://github.com/KyoSherlock/ExpandableLayout

这个ExpandableLayout就像一个平滑的展开/折叠动画CheckBox,所以它可以在任何地方使用(ListView或RecyclerView)。

答案 9 :(得分:3)

令我惊讶的是,还没有一个简明的答案,尽管仅用两行代码就可以很容易地实现这种展开/折叠动画:

(recycler.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false // called once

一起
notifyItemChanged(position) // in adapter, whenever a child view in item's recycler gets hidden/shown

因此,对我来说,以下链接中的说明非常有用:https://medium.com/@nikola.jakshic/how-to-expand-collapse-items-in-recyclerview-49a648a403a6

答案 10 :(得分:1)

//Global Variable

private int selectedPosition = -1;

 @Override
    public void onBindViewHolder(final CustomViewHolder customViewHolder, final int i) {

        final int position = i;
        final GetProductCatalouge.details feedItem = this.postBeanses.get(i);
        customViewHolder.lly_main.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                selectedPosition = i;
                notifyDataSetChanged();
            }
        });
        if (selectedPosition == i) {

          if (customViewHolder.lly_hsn_code.getVisibility() == View.VISIBLE) {

            customViewHolder.lly_hsn_code.setVisibility(View.GONE);
            customViewHolder.lly_sole.setVisibility(View.GONE);
            customViewHolder.lly_sole_material.setVisibility(View.GONE);

        } else {

            customViewHolder.lly_hsn_code.setVisibility(View.VISIBLE);
            customViewHolder.lly_sole.setVisibility(View.VISIBLE);
            customViewHolder.lly_sole_material.setVisibility(View.VISIBLE);
        }


        } else {
            customViewHolder.lly_hsn_code.setVisibility(View.GONE);
            customViewHolder.lly_sole.setVisibility(View.GONE);
            customViewHolder.lly_sole_material.setVisibility(View.GONE);
        }
}

enter image description here

答案 11 :(得分:0)

RVAdapter中使用两种视图类型。一个用于扩展布局,另一个用于折叠。 为android:animateLayoutChanges="true"设置RecyclerView就会发生魔力 在this video

的0:42查看使用此效果