eim*_*mer 19 animation android android-recyclerview
我正在创建一个问答,每个问题都是一张卡片.答案开始显示第一行,但是当点击它时,它应该展开以显示完整的答案.
当答案扩展/折叠时,RecyclerView的其余部分应设置动画以为扩展或折叠腾出空间,以避免显示空白区域.
我观看了有关RecyclerView动画的演讲,并且相信我想要一个自定义的ItemAnimator,我在其中覆盖animateChange.那时我应该创建一个ObjectAnimator来动画View的LayoutParams的高度.不幸的是,我很难将它们捆绑在一起.当覆盖canReuseUpdatedViewHolder时我也返回true,因此我们重用相同的viewholder.
@Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
return true;
}
@Override
public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder,
@NonNull final RecyclerView.ViewHolder newHolder,
@NonNull ItemHolderInfo preInfo,
@NonNull ItemHolderInfo postInfo) {
Log.d("test", "Run custom animation.");
final ColorsAdapter.ColorViewHolder holder = (ColorsAdapter.ColorViewHolder) newHolder;
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) holder.tvColor.getLayoutParams();
ObjectAnimator halfSize = ObjectAnimator.ofInt(holder.tvColor.getLayoutParams(), "height", params.height, 0);
halfSize.start();
return super.animateChange(oldHolder, newHolder, preInfo, postInfo);
}
Run Code Online (Sandbox Code Playgroud)
现在我只想尝试动画,但没有任何反应......任何想法?
Geo*_*gan 10
我认为你的动画不起作用,因为你不能以LayoutParams
这种方式制作动画,尽管如果可以的话它会很好.我尝试了你所拥有的代码,它所做的就是让我的视图跳到新的高度.我发现让它工作的唯一方法是使用a ValueAnimator
,如下面的示例所示.
当我使用DefaultItemAnimator
通过更新其可见性来显示/隐藏视图时,我注意到了一些缺点.虽然它确实为新视图腾出了空间,并根据可扩展视图的可见性来上下动画其余项目,但我注意到它没有为可扩展视图的高度设置动画.它只是使用alpha值褪色到位并且不合适.
下面是一个定制的ItemAnimator
基于隐藏/显示具有大小和α动画LinearLayout
在ViewHolder
布局.ViewHolder
如果用户快速点击标题,它还允许重用它并尝试正确处理部分动画:
public static class MyAnimator extends DefaultItemAnimator {
@Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
return true;
}
private HashMap<RecyclerView.ViewHolder, AnimatorState> animatorMap = new HashMap<>();
@Override
public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder, @NonNull final RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
final ValueAnimator heightAnim;
final ObjectAnimator alphaAnim;
final CustomAdapter.ViewHolder vh = (CustomAdapter.ViewHolder) newHolder;
final View expandableView = vh.getExpandableView();
final int toHeight; // save height for later in case reversing animation
if(vh.isExpanded()) {
expandableView.setVisibility(View.VISIBLE);
// measure expandable view to get correct height
expandableView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
toHeight = expandableView.getMeasuredHeight();
alphaAnim = ObjectAnimator.ofFloat(expandableView, "alpha", 1f);
} else {
toHeight = 0;
alphaAnim = ObjectAnimator.ofFloat(expandableView, "alpha", 0f);
}
heightAnim = ValueAnimator.ofInt(expandableView.getHeight(), toHeight);
heightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
expandableView.getLayoutParams().height = (Integer) heightAnim.getAnimatedValue();
expandableView.requestLayout();
}
});
AnimatorSet animSet = new AnimatorSet()
.setDuration(getChangeDuration());
animSet.playTogether(heightAnim, alphaAnim);
animSet.addListener(new Animator.AnimatorListener() {
private boolean isCanceled;
@Override
public void onAnimationStart(Animator animation) { }
@Override
public void onAnimationEnd(Animator animation) {
if(!vh.isExpanded() && !isCanceled) {
expandableView.setVisibility(View.GONE);
}
dispatchChangeFinished(vh, false);
animatorMap.remove(newHolder);
}
@Override
public void onAnimationCancel(Animator animation) {
isCanceled = true;
}
@Override
public void onAnimationRepeat(Animator animation) { }
});
AnimatorState animatorState = animatorMap.get(newHolder);
if(animatorState != null) {
animatorState.animSet.cancel();
// animation already running. Set start current play time of
// new animations to keep them smooth for reverse animation
alphaAnim.setCurrentPlayTime(animatorState.alphaAnim.getCurrentPlayTime());
heightAnim.setCurrentPlayTime(animatorState.heightAnim.getCurrentPlayTime());
animatorMap.remove(newHolder);
}
animatorMap.put(newHolder, new AnimatorState(alphaAnim, heightAnim, animSet));
dispatchChangeStarting(newHolder, false);
animSet.start();
return false;
}
public static class AnimatorState {
final ValueAnimator alphaAnim, heightAnim;
final AnimatorSet animSet;
public AnimatorState(ValueAnimator alphaAnim, ValueAnimator heightAnim, AnimatorSet animSet) {
this.alphaAnim = alphaAnim;
this.heightAnim = heightAnim;
this.animSet = animSet;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是使用略微修改的RecyclerView
演示的结果.
更新:
刚刚注意到你的用例在重新阅读问题后实际上有点不同.你有一个文本视图,只想显示它的一行,然后再展开它以显示所有行.幸运的是,这简化了自定义动画师:
public static class MyAnimator extends DefaultItemAnimator {
@Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
return true;
}
private HashMap<RecyclerView.ViewHolder, ValueAnimator> animatorMap = new HashMap<>();
@Override
public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder, @NonNull final RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
ValueAnimator prevAnim = animatorMap.get(newHolder);
if(prevAnim != null) {
prevAnim.reverse();
return false;
}
final ValueAnimator heightAnim;
final CustomAdapter.ViewHolder vh = (CustomAdapter.ViewHolder) newHolder;
final TextView tv = vh.getExpandableTextView();
if(vh.isExpanded()) {
tv.measure(View.MeasureSpec.makeMeasureSpec(((View) tv.getParent()).getWidth(), View.MeasureSpec.AT_MOST), View.MeasureSpec.UNSPECIFIED);
heightAnim = ValueAnimator.ofInt(tv.getHeight(), tv.getMeasuredHeight());
} else {
Paint.FontMetrics fm = tv.getPaint().getFontMetrics();
heightAnim = ValueAnimator.ofInt(tv.getHeight(), (int)(Math.abs(fm.top) + Math.abs(fm.bottom)));
}
heightAnim.setDuration(getChangeDuration());
heightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
tv.getLayoutParams().height = (Integer) heightAnim.getAnimatedValue();
tv.requestLayout();
}
});
heightAnim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchChangeFinished(vh, false);
animatorMap.remove(newHolder);
}
@Override
public void onAnimationCancel(Animator animation) { }
@Override
public void onAnimationStart(Animator animation) { }
@Override
public void onAnimationRepeat(Animator animation) { }
});
animatorMap.put(newHolder, heightAnim);
dispatchChangeStarting(newHolder, false);
heightAnim.start();
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
而新的演示:
您不必实现自定义ItemAnimator
,默认DefaultItemAnimator
已经支持您需要的内容.但是你需要告诉这个Animator哪些观点发生了变化.我猜你在调用notifyDataSetChanged()
你的适配器.这可以防止RecyclerView中单个更改项目的动画(在您的情况下是项目的展开/折叠).
您应该使用notifyItemChanged(int position)
已更改的项目.这是一个itemClicked(int position)
在RecyclerView中扩展/折叠视图的简短方法.该字段expandedPosition
跟踪当前展开的项目:
private void itemClicked(int position) {
if (expandedPosition == -1) {
// selected first item
expandedPosition = position;
notifyItemChanged(position);
} else if (expandedPosition == position) {
// collapse currently expanded item
expandedPosition = -1;
notifyItemChanged(position);
} else {
// collapse previously expanded item and expand new item
int oldExpanded = expandedPosition;
expandedPosition = position;
notifyItemChanged(oldExpanded);
notifyItemChanged(position);
}
}
Run Code Online (Sandbox Code Playgroud)
这是结果:
归档时间: |
|
查看次数: |
999 次 |
最近记录: |