继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

对RecyclerView Item做动画

波斯汪
关注TA
已关注
手记 502
粉丝 67
获赞 430

对RecyclerView Item做动画,刚刚开始研究的时候一些坑,在这里把一些设计思路分享出去

添加动态位移,静态位移,缩放等动画,保证了动画状态的平滑衔接

效果图:

5b8a8f9300010d5b03110226.jpg


Demo下载:http://www.apkbus.com/thread-586098-1-1.html

RecyclerView,ListView这些具有Item复用性的View,想要对其Item做动画,需要注意以下几点: 

1,如果要一点击,让所有Item做动画的效果。例如,上图的编辑和取消,这样的动态动画。可以对所有ViewHolder中的View直接做动画。 
但是需要在onBindViewHolder方法中对复用的item做静态动画,保证动画状态的平滑衔接。



2,每一个Item的特有属性,例如,上图checkbox的选中状态,都需要把状态字段放到对应的Java bean中, 并在onBindViewHolder方法从java bean取出状态值,设置到view里。


首先,对一些细节进行分析: 

如何设计一个自定义View,来让他可以自己移动,做动画起来?



1,首先,创建一个View,他是RecyclerView Item的根布局:

        public class SlideRelativeLayout extends RelativeLayout {
            public static final String TAG = SlideRelativeLayout.class.getSimpleName();            private CheckBox mCheckBox;            private RelativeLayout mContentSlide;            private int mOffset;        public SlideRelativeLayout(Context context) {            super(context);
        }        public SlideRelativeLayout(Context context, AttributeSet attrs) {            super(context, attrs);
        }        public SlideRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {            super(context, attrs, defStyleAttr);
        }        @Override
        protected void onFinishInflate() {            super.onFinishInflate();
            mCheckBox = (CheckBox) findViewById(R.id.item_checkbox);
            mContentSlide = (RelativeLayout) findViewById(R.id.item_content_rl);
            setOffset(35);
        }        public void setOffset(int offset) {
            mOffset = (int) (getContext().getResources().getDisplayMetrics().density * offset + 0.5f);
        }        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        public void openAnimation() {
            ValueAnimator valueAnimator = new ValueAnimator();
            valueAnimator.setIntValues(0, 1);
            valueAnimator.setDuration(300);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {                    float fraction = valueAnimator.getAnimatedFraction();                    int endX = (int) (-mOffset * fraction);
                    doAnimationSet(endX, fraction);
                }
            });
            valueAnimator.start();
        }        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        public void closeAnimation() {
            ValueAnimator valueAnimator = new ValueAnimator();
            valueAnimator.setIntValues(0, 1);
            valueAnimator.setDuration(150);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {                    float fraction = valueAnimator.getAnimatedFraction();                    int endX = (int) (-mOffset * (1 - fraction));
                    doAnimationSet(endX, (1 - fraction));
                }
            });
            valueAnimator.start();
        }        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        private void doAnimationSet(int dx, float fraction) {
            mContentSlide.scrollTo(dx, 0);
            mCheckBox.setScaleX(fraction);
            mCheckBox.setScaleY(fraction);
            mCheckBox.setAlpha(fraction * 255);
        }        public void open() {
            mContentSlide.scrollTo(-mOffset, 0);
        }        public void close() {
            mContentSlide.scrollTo(0, 0);
        }
    }



这里,在View树创建完毕之后找到我们需要做动画的子View:

        @Override
        protected void onFinishInflate() {            super.onFinishInflate();
            mCheckBox = (CheckBox) findViewById(R.id.item_checkbox);
            mContentSlide = (RelativeLayout) findViewById(R.id.item_content_rl);
            setOffset(35);
        }



然后,设计4个方法,分别为:动态的打开动画,动态的关闭动画,静态的打开动画,静态的关闭动画。


        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        public void openAnimation() {
            ValueAnimator valueAnimator = new ValueAnimator();
            valueAnimator.setIntValues(0, 1);
            valueAnimator.setDuration(300);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {                    float fraction = valueAnimator.getAnimatedFraction();                    int endX = (int) (-mOffset * fraction);
                    doAnimationSet(endX, fraction);
                }
            });
            valueAnimator.start();
        }        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        public void closeAnimation() {
            ValueAnimator valueAnimator = new ValueAnimator();
            valueAnimator.setIntValues(0, 1);
            valueAnimator.setDuration(150);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {                    float fraction = valueAnimator.getAnimatedFraction();                    int endX = (int) (-mOffset * (1 - fraction));
                    doAnimationSet(endX, (1 - fraction));
                }
            });
            valueAnimator.start();
        }        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        private void doAnimationSet(int dx, float fraction) {
            mContentSlide.scrollTo(dx, 0);
            mCheckBox.setScaleX(fraction);
            mCheckBox.setScaleY(fraction);
            mCheckBox.setAlpha(fraction * 255);
        }        public void open() {
            mContentSlide.scrollTo(-mOffset, 0);
        }        public void close() {
            mContentSlide.scrollTo(0, 0);
        }        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        private void doAnimationSet(int dx, float fraction) {
            mContentSlide.scrollTo(dx, 0);
            mCheckBox.setScaleX(fraction);
            mCheckBox.setScaleY(fraction);
            mCheckBox.setAlpha(fraction * 255);
        }



对子View做动画我采取的策略是:使用属性动画,在每一贞动画里获取到对应的值,对子View做相应的动画,例如:动态的打开动画。

onAnimationUpdate方法,显示每一贞动画都会回调一次


        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        public void openAnimation() {
            ValueAnimator valueAnimator = new ValueAnimator();
            valueAnimator.setIntValues(0, 1);
            valueAnimator.setDuration(300);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {                    float fraction = valueAnimator.getAnimatedFraction();                    int endX = (int) (-mOffset * fraction);
                    doAnimationSet(endX, fraction);
                }
            });
            valueAnimator.start();
        }        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        private void doAnimationSet(int dx, float fraction) {
            mContentSlide.scrollTo(dx, 0);
            mCheckBox.setScaleX(fraction);
            mCheckBox.setScaleY(fraction);
            mCheckBox.setAlpha(fraction * 255);
        }


这样RecylerView 带有动态动画和静态动画的View就设计好了。


2,在bind方法中使用静态动画,动态动画对外提供方法调用:

        private class SlideViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

            private SlideRelativeLayout mSlideRelativeLayout;            private CheckBox mCheckBox;            private ItemBean mItemBean;            public SlideViewHolder(View itemView) {                super(itemView);
                mSlideRelativeLayout = (SlideRelativeLayout) itemView.findViewById(R.id.item_root);
                mCheckBox = (CheckBox) itemView.findViewById(R.id.item_checkbox);
                itemView.setOnClickListener(this);
            }            public void bind(ItemBean itemBean) {
                mItemBean = itemBean;
                mCheckBox.setChecked(itemBean.isChecked());                switch (mState) {                    case NORMAL:
                        mSlideRelativeLayout.close();                        break;                    case SLIDE:
                        mSlideRelativeLayout.open();                        break;
                }
            }            public void openItemAnimation() {
                mSlideRelativeLayout.openAnimation();
            }            public void closeItemAnimation() {
                mSlideRelativeLayout.closeAnimation();
            }



可以看到静态动画在bind里调用,打开或者关闭是由mState变量决定的。而动态的滑动需要手动调用,那怎么来使用这些动画呢? 

动态动画的使用方法:存储所有创建出来的ViewHolder,统一调用动态动画方法。并设置mState变量值,防止滑动时动画不能平滑衔接.

        private List<SlideViewHolder> mSlideViewHolders = new ArrayList<>();        public void openItemAnimation() {
            mState = SLIDE;//
            for (SlideViewHolder holder : mSlideViewHolders) {
                holder.openItemAnimation();
            }
        }        public void closeItemAnimation() {
            mState = NORMAL;            for (SlideViewHolder holder : mSlideViewHolders) {
                holder.closeItemAnimation();
            }
        }


而外面又是这样调用的:

        private void editItems() {            if ("编辑".equals(mRightTV.getText().toString())) {
                mRightTV.setText("取消");
                mSlideAdapter.openItemAnimation();
            } else if ("取消".equals(mRightTV.getText().toString())) {
                mRightTV.setText("编辑");
                mSlideAdapter.closeItemAnimation();
            }
        }


总体就是:点击按钮 – 变量ViewHolder集合做动态动画,并设置mState变量 – 手机滑动屏幕走bind方法又是根据mState做静态动画

动画从而平滑的衔接起来

再来看一次效果图:


5b8a8f9300010d5b03110226.jpg

动态动画起先,设置状态值,引导处理正确的静态动画,RecyclerView item的动画处理是不是变简单了。


还有,对item的特殊数据需要在对应的java bean里设置值,在bind方法取值设置到item中去。 

原文链接:http://www.apkbus.com/blog-822717-76496.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP