回收视图展开/折叠项

我想展开/折叠我的回收视图的项目,以显示更多的信息。我想达到同样的效果的 SlideExpandableListView

基本上在我的视图持有者,我有一个视图,是不可见的,我想做一个平滑的展开/折叠动画,而不是设置可见性为可见/消失只。我只需要一个项目被展开的时间,它会有一些凉爽的海拔显示该项目是选定的。

新的 Android 近期通话历史列表也有同样的效果。选项“ CALL BACK”和“ DETAILS”只有在选中某个项目时才可见。

RecyclerView expandable items

195418 次浏览

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

完整代码可在以下网址找到: 示例代码位于: < a href = “ https://github.com/dbleicher/迴收视图-网格-快速返回”> 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 ()方法。如果 onBindViewHolder 中的视图的位置等于“  展開位置”,您可以展開它,如果不等于,您可以隐藏它。您可以在 onClick 侦听器中设置 expandposition 的值:

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

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

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

我认为这应该有所帮助,这就是我如何实现和做同样的谷歌在最近的呼叫视图。

有一个非常简单的使用图书馆与梯度支持: 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()

请不要使用任何库来达到这个效果,而是按照 Google I/O 推荐的方法来做。在回收站的 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();
}
});
  • 其中 详情是我的视图,将显示在触摸(呼叫细节在您的情况下。默认可见性 GONE)。
  • 是一个初始化为 -1的 int 变量

要获得想要的炫酷效果,可以使用这些属性作为 list _ item 属性:

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

其中的 comments _ back 是:

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

以及评论 _ 选择是:

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

RVAdapter中使用两种视图类型。一种用于展开布局,另一种用于折叠。 神奇的事情发生在为 RecyclerView设置 android:animateLayoutChanges="true"的时候 检查在0:42使用这个效果在 < a href = “ https://vimeo.com/201271481”rel = “ nofollow noReferrer”> 这个视频

为此,只需要简单的行不复杂

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

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

对于那些只想要一个项目展开和其他崩溃。使用这个

首先声明一个全局变量,其值为 -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);
}
});

在使用 海森堡所回答的 回收视图展开/折叠项上的 RecyclerView中实现可展开/可折叠项的推荐方法之后,每当通过调用 TransitionManager.beginDelayedTransition(ViewGroup)和随后的 notifyDatasetChanged()来刷新 RecyclerView时,我都会看到一些明显的构件。

他最初的回答是:

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());
}
}
  • Details 是在项展开/折叠期间要显示/隐藏的视图
  • 是一个跟踪展开项的 int
  • 是在项折叠过程中使用的 ViewHolder

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

修改后,以前不需要的影响应该消失。然而,这可能不是完美的解决方案。它只是做了我想做的,消除了碍眼的东西。

歌曲: EDIT:

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

我知道自从最初的问题发布以来已经过去很长时间了。但我认为,对于像我这样反应迟钝的人来说,解释一下@Heisenberg 的回答会有所帮助。

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

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

最后,在适配器覆盖中获取回收视图对象

@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);


this.recyclerView = recyclerView;
}

希望这个能帮上忙。

根本不需要使用第三方库。稍微调整一下中的方法在 谷歌 I/O 2016和海森堡中展示了这个主题,做到了这一点。

由于 notifyDataSetChanged() 重新绘制完整的 RecyclerViewnotifyDataItemChanged()是一个更好的选择(不是最好的) ,因为我们有位置和 ViewHolder在我们的处置,和 notifyDataItemChanged()只有 在给定的位置重新绘制特定的 ViewHolder

但问题是,点击后过早消失的 ViewHolder和它的出现是不能消除,即使使用 notifyDataItemChanged()

下面的代码 没有使用 notifyDataSetChanged()notifyDataItemChanged(),并在 API 23上进行测试,当用在回收视图上时,效果非常好,每个 ViewHolder 都有一个 CardView作为它的根元素:

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是一个初始化为 -1的全局整数。 details是展开时显示的完整视图,折叠时显示为隐藏视图。

如前所述,ViewHolder的根元素是 CardView,其 foregroundstateListAnimator属性的定义与海森堡在这个主题上所说的完全一致。

更新: 上面的演示如果其中一个在扩展项目中,将会崩溃之前的扩展项目。要修改此行为并保持展开项的原样,即使在展开另一个项时也是如此,您将需要以下代码。

if (row.details.getVisibility()!=View.VISIBLE)
{
row.details.setVisibility(View.VISIBLE);
row.root.setActivated(true);
row.details.animate().alpha(1).setStartDelay(500);
}
else
{
row.root.setActivated(false);
row.details.setVisibility(View.GONE);
row.details.setAlpha(0);
}
TransitionManager.beginDelayedTransition(recycler);

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

LinearLayoutManager manager = (LinearLayoutManager) recycler.getLayoutManager();
int distance;
View first = recycler.getChildAt(0);
int height = first.getHeight();
int current = recycler.getChildAdapterPosition(first);
int p = Math.abs(position - current);
if (p > 5) distance = (p - (p - 5)) * height;
else       distance = p * height;
manager.scrollToPositionWithOffset(position, distance);

重要提示: 为了使上面的演示工作,必须在代码中保留回收视图及其 LayoutManager 的一个实例(后者是为了灵活性)

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

我很惊讶还没有简明的答案,尽管这样的展开/折叠动画很容易实现,只需要两行代码:

(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

这对我来说很简单,也很好用,希望对你有所帮助:

首先: 将其添加到项目回收视图的视图组中

android:animateLayoutChanges="true"

第二: 将这个添加到您的 onclick 方法中,该方法可以展开或折叠当前项:

val changeBounds = ChangeBounds()
// binder.contraintLayout is viewGroup that have animateLayoutChange = true (iam using databinding)
changeBounds.duration = binder.contrainLayout.layoutTransition.getDuration(LayoutTransition.CHANGING)
changeBounds.interpolator = binder.contrainLayout.layoutTransition.getInterpolator(LayoutTransition.CHANGING)
binder.expandView.Visibility = binder.expandView.Visibility != View.Visible
TransitionManager.beginDelayedTransition(binder.root.parent as ViewGroup, changeBounds)

创建自定义视图

  • 如果愿意,可以创建自己的可重用自定义视图。给你是我写的一篇关于这个主题的博客文章。参考文献会在博客文章中注明