首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >RecyclerView问答

RecyclerView问答
EN

Stack Overflow用户
提问于 2016-02-04 17:16:04
回答 5查看 1.3K关注 0票数 19

我正在创建一个问答,每一个问题都是一张卡片。答案开始显示第一行,但当点击它时,它应该展开以显示完整的答案。

当一个答案被展开/折叠时,RecyclerView的其余部分应该是动画的,以便为展开或折叠腾出空间,以避免显示空白。

我在RecyclerView动画上观看了演讲,并且相信我想要一个定制的ItemAnimator,在那里我重写了animateChange。此时,我应该创建一个ObjectAnimator来动画视图的LayoutParams的高度。不幸的是,我很难把这一切绑在一起。在重写canReuseUpdatedViewHolder时,我还返回true,因此我们重用相同的viewholder。

代码语言:javascript
运行
复制
@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);
}

现在我只是想弄点动画,但什么都没发生.有什么想法吗?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2016-02-11 06:05:24

我认为您的动画没有工作,因为您不能这样动画LayoutParams,虽然它将是整洁的,如果你可以。我试过了你的代码,它所做的只是让我的观点跳到新的高度。我发现让它工作的唯一方法是使用ValueAnimator,如您在下面的示例中所看到的。

在使用DefaultItemAnimator通过更新视图的可见性来显示/隐藏视图时,我注意到了一些缺点。虽然它确实为新视图腾出了空间,并根据可扩展视图的可见性将其余的项目上下动画化,但我注意到它并没有显示可扩展视图的高度。它只是简单地消失在适当的位置和不合适的地方,只使用alpha值。

下面是一个定制的ItemAnimator,它具有基于隐藏/显示ViewHolder布局中的LinearLayout的大小和alpha动画。它还允许重用相同的ViewHolder,并在用户快速点击头部时尝试正确处理部分动画:

代码语言:javascript
运行
复制
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;
        }
    }
}

这是使用稍微修改过的RecyclerView演示的结果。

更新:

刚刚注意到,在重新阅读这个问题之后,您的用例实际上有点不同。您有一个文本视图,只想显示其中的一行,然后展开它以显示所有行。幸运的是,这简化了自定义动画器:

代码语言:javascript
运行
复制
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;
    }
}

新的演示:

票数 10
EN

Stack Overflow用户

发布于 2016-02-08 20:24:06

您不必实现自定义ItemAnimator,默认的DefaultItemAnimator已经支持所需的内容。但是,您需要告诉这个动画家,哪些视图发生了变化。我想您是在适配器中调用notifyDataSetChanged()。这将防止RecyclerView中单个已更改项的动画(在您的示例中是项的展开/折叠)。

对于已更改的项,应使用notifyItemChanged(int position)。下面是一个简短的itemClicked(int position)方法,它扩展/折叠RecyclerView中的视图。字段expandedPosition跟踪当前展开的项:

代码语言:javascript
运行
复制
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);
    }
}

其结果是:

票数 9
EN

Stack Overflow用户

发布于 2016-02-04 17:45:04

根据文档,您需要在animateChange中返回false或稍后调用runPendingAnimations。试试看还假。

http://developer.android.com/reference/android/support/v7/widget/RecyclerView.ItemAnimator.html

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35207407

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档