• 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);
        }
    
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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);
            }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    @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);
                }
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    点进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);
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

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

    public static interface Expandable {
            public View getExpandView();
        }
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3

    在MainAdapter中传入infos这个Textview

    @Override
            public View getExpandView() {
                return infos;
            }
    • 1
    • 2
    • 3
    • 4
    • 1
    • 2
    • 3
    • 4

    第三个是一个标记,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;
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    可以看出 具体展开的动画在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);
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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);
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    再深入一层看展开的动画

    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;
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

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

    http://blog.csdn.net/gulumi_mmga/article/details/46683437

  • 相关阅读:
    构造方法
    $this
    public/private/protected三种访问控制符的区别
    vsftpd的虚拟主机配置,错误记录
    高斯模糊css
    css绘制三角形
    禁止缩放的一行代码
    自适应的Javascript代码resize.js
    判断是pc端还是手机端js
    倒计时
  • 原文地址:https://www.cnblogs.com/ldq2016/p/5825015.html
Copyright © 2020-2023  润新知