Android RecyclerView 动画展开item显示详情

stackoverflow上看到这个问题,答主给了个demo
http://stackoverflow.com/questions/27446051/recyclerview-animate-item-resize
看懂了之后发个博,记录一下,刚开始看别人代码好难受,就这么3个文件看了一晚上。。

效果如下

res文件
main_activity文件就是一个recyclerview
main_item是两个textview 一个标题一个详细信息

MainActivity就是加载了一个RecyclerView

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);

        final RecyclerView rv = (RecyclerView) findViewById(R.id.rv);

        final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        rv.setLayoutManager(layoutManager);

        final MainAdapter adapter = new MainAdapter();
        rv.setAdapter(adapter);
    }

}

MainAdapter中new了一个keepOne对象,点进去看这个类,有两个方法:bind和toggle,其中的bind是在MainAdapter中的onBindViewHolder()方法中调用,而toggle是响应viewholder的点击事件

public static class KeepOneH<VH extends RecyclerView.ViewHolder & Expandable> {
//    opened为-1表示所有item是关闭状态,open为pos值的表示pos位置的item为展开的状态
        private int _opened = -1;
        public void bind(VH holder, int pos) {
            if (pos == _opened)
//                3
//            直接显示expandView 无动画
                ExpandableViewHoldersUtil.openH(holder, holder.getExpandView(), false);
            else
//            直接关闭expandView 无动画
                ExpandableViewHoldersUtil.closeH(holder, holder.getExpandView(), false);
        }

@SuppressWarnings("unchecked")
//        响应点击事件的方法
        public void toggle(VH holder) {
//            如果点击的就是开着的item,就关闭该item并把opened置-1
//            ???TODO
            if (_opened == holder.getPosition()) {
                _opened = -1;
//                关闭expandView 有动画
                ExpandableViewHoldersUtil.closeH(holder, holder.getExpandView(), true);
            }
//            如果点击其他本来关闭着的item,则把opened值换成当前pos,把之前开的item给关掉
            else {
                int previous = _opened;
                _opened = holder.getPosition();
//                展开expandView 有动画
                ExpandableViewHoldersUtil.openH(holder, holder.getExpandView(), true);

//                用动画关闭之前的item
                final VH oldHolder = (VH) ((RecyclerView) holder.itemView.getParent()).findViewHolderForPosition(previous);
                if (oldHolder != null)
                    ExpandableViewHoldersUtil.closeH(oldHolder, oldHolder.getExpandView(), true);
            }
        }
    }

点进openH和closeH方法进去看

//    4
    public static void openH(final RecyclerView.ViewHolder holder, final View expandView, final boolean animate) {
//        animate参数为true,则有动画效果
        if (animate) {
            expandView.setVisibility(View.VISIBLE);
//            5
//            改变高度的动画,具体操作点进去看
            final Animator animator = ViewHolderAnimator.ofItemViewHeight(holder);
//            扩展的动画结束后透明度动画开始
            animator.addListener(new AnimatorListenerAdapter() {
                @Override public void onAnimationEnd(Animator animation) {
                    final ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(expandView, View.ALPHA, 1);
                    alphaAnimator.addListener(new ViewHolderAnimator.ViewHolderAnimatorListener(holder));
                    alphaAnimator.start();
                }
            });
            animator.start();
        }
//        animate参数为false,则直接设置为可见
        else {
            expandView.setVisibility(View.VISIBLE);
            expandView.setAlpha(1);
        }
    }

openH方法接收3个参数,
第一个是viewholder.
第二个是展开部分的view,由holder.getExpandView()方法获取。这里定义了一个接口

public static interface Expandable {
        public View getExpandView();
    }

在MainAdapter中传入infos这个Textview

@Override
        public View getExpandView() {
            return infos;
        }

第三个是一个标记,true时有动画,false时直接设置其展开或者是关闭的状态。所以在bind()方法中调用的openH()都是false,而toggle()中调用的设置为true。

openH方法中 具体动画的操作为ViewHolderAnimator.ofItemViewHeight(holder)

public static Animator ofItemViewHeight(RecyclerView.ViewHolder holder) {
        View parent = (View) holder.itemView.getParent();
        if (parent == null)
            throw new IllegalStateException("Cannot animate the layout of a view that has no parent");

//        测量扩展动画的起始高度和结束高度
        int start = holder.itemView.getMeasuredHeight();
        holder.itemView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        int end = holder.itemView.getMeasuredHeight();
//  6
        final Animator animator = LayoutAnimator.ofHeight(holder.itemView, start, end);
//        设定该item在动画开始结束和取消时能否被recycle
        animator.addListener(new ViewHolderAnimatorListener(holder));
//        设定结束时这个item的宽高
        animator.addListener(new LayoutParamsAnimatorListener(holder.itemView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

        return animator;
    }

可以看出 具体展开的动画在LayoutAnimator.ofHeight(holder.itemView, start, end);中,ViewHolderAnimator只是测量参数,设定监听两个监听事件
1设定在动画开始结束和取消状态下是否可以被回收

public ViewHolderAnimatorListener(RecyclerView.ViewHolder holder) {
            _holder = holder;
        }

        @Override
        public void onAnimationStart(Animator animation) {
            _holder.setIsRecyclable(false);
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            _holder.setIsRecyclable(true);
        }

        @Override
        public void onAnimationCancel(Animator animation) {
            _holder.setIsRecyclable(true);
        }
    }

2.设定在动画结束后view的高和宽分别为warp_content,match_parent.

public static class LayoutParamsAnimatorListener extends AnimatorListenerAdapter {
        private final View _view;
        private final int _paramsWidth;
        private final int _paramsHeight;

        public LayoutParamsAnimatorListener(View view, int paramsWidth, int paramsHeight) {
            _view = view;
            _paramsWidth = paramsWidth;
            _paramsHeight = paramsHeight;
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            final ViewGroup.LayoutParams params = _view.getLayoutParams();
            params.width = _paramsWidth;
            params.height = _paramsHeight;
            _view.setLayoutParams(params);
        }
    }

再深入一层看展开的动画

public class LayoutAnimator {

    public static class LayoutHeightUpdateListener implements ValueAnimator.AnimatorUpdateListener {

        private final View _view;

        public LayoutHeightUpdateListener(View view) {
            _view = view;
        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            final ViewGroup.LayoutParams lp = _view.getLayoutParams();
            lp.height = (int) animation.getAnimatedValue();
            _view.setLayoutParams(lp);
        }

    }

    public static Animator ofHeight(View view, int start, int end) {
        final ValueAnimator animator = ValueAnimator.ofInt(start, end);
        animator.addUpdateListener(new LayoutHeightUpdateListener(view));
        return animator;
    }
}

是用ValueAnimator.ofInt生成一系列高度值,然后监听动画的变化,不断设定view的高度值

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
RecyclerViewAndroid中常用的列表控件,用于展示大量数据。要实现RecyclerViewitem动画展开折叠效果,可以通过自定义动画和布局参数的方式来实现。 首先,需要为RecyclerViewitem布局添加动画效果。可以使用属性动画或者动画资源文件来实现。例如可以使用属性动画item的宽度从0变为原始宽度,实现展开效果;或者使用动画资源文件定义透明度的变化,实现淡入淡出效果。这样,在item被添加到RecyclerView中时,会播放动画效果,实现展开折叠的效果。 其次,为了在item展开时使其占用更多的空间,需要设置item的布局参数。可以通过修改item的宽度、高度、权重等属性,来实现展开折叠的效果。例如,在展开时,可以将item的宽度设置为match_parent,高度设为固定值或者根据内容自适应的值,从而让item占据更多的空间,显示更多的内容;在折叠时,可以将item的宽度设置为wrap_content,高度设置为固定值或者较小的值,从而让item占据较少的空间,显示较少的内容。 最后,需要在RecyclerView的适配器中编写代码来控制item展开折叠。可以使用标志位或者数据集合的状态来记录item展开折叠状态,在适配器的ViewHolder中根据状态来设置item动画和布局参数。例如,在item展开时,可以将展开状态设置为true,并为item添加展开动画和修改布局参数的代码;在item折叠时,将展开状态设置为false,并为item添加折叠动画和恢复原始布局参数的代码。 总之,要实现RecyclerViewitem动画展开折叠效果,需要为item添加动画效果,修改item的布局参数,并在适配器中控制item展开折叠状态以及动画和布局参数的设置。这样就可以实现一个具有动画效果的可展开折叠的RecyclerView列表。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值