Android 自定义横向列表开启动画

公司产品和UI重新设计了APP首页,顺便加上了一些奇怪的动画。设计了开启横向RecyclerView,共搞了两个版本。此文用于展示并发布这个动画的开源代码。
先贴上代码开源地址:https://gitee.com/rookieci/shop-anim
开源的代码是从项目中剥离出来的,不包含项目业务逻辑,可以直接编译运行。
做了两个gif的展示图,然后csdn表示文章只能传5M以内的图片。泪奔。。。。
两张展示图地址:展示图1 展示图2

动画说明
动画一,主要上下平移RecyclerView,同时定义每一个item的位置、速度和加速度。这样就能实现item去追赶RecyclerView。

VerticalDrawerLayout中监听触摸确定位置,用于平移RecyclerView

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //if (!BuildConfig.DEBUG) return super.dispatchTouchEvent(ev);
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (buttonView != null) {
                    startX = ev.getX();
                    startY = ev.getY();
                    oldY = startY;
                    final int left = buttonView.getLeft();
                    final int right = buttonView.getRight();
                    final int top = buttonView.getTop();
                    final int bottom = buttonView.getBottom();
                    if (startX > left && startX < right && startY > top && startY < bottom) {
                        onTouchButton = true;
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (onTouchButton) {
                    //float x = ev.getX();
                    float y = ev.getY();
                    float len = y - oldY;
                    oldY = y;
                    int paddingTop = getPaddingTop();
                    setPadding(getPaddingLeft(), (int) (paddingTop + len), getPaddingRight(), getPaddingBottom());
                }
                break;
            case MotionEvent.ACTION_UP:
                if (onTouchButton) {
                    //float x = ev.getX();
                    //float y = ev.getY();
                    oldY = ev.getY();
                    final boolean down = oldY > startY;//向下滑动
                    final int paddingTop = getPaddingTop();
                    if (currOpen) {//展开状态
                        final int openHeight = getOpenHeight();//最大展开
                        if (down) {//恢复原位
                            startAnim(paddingTop, openHeight);
                        } else {//向上
                            int contentHeight = getContentHeight();
                            int i = (openHeight - paddingTop) << 1;
                            if (i > contentHeight) {//开始关闭
                                startAnim(paddingTop, -contentHeight);
                                currOpen = false;
                                if (onVerticalDrawerListener != null) {
                                    onVerticalDrawerListener.onChangeOpen(currOpen);
                                }
                            } else startAnim(paddingTop, openHeight);
                        }
                    } else {//关闭状态
                        int contentHeight = getContentHeight();
                        if (down) {//向下
                            int i = (paddingTop + contentHeight) << 1;
                            if (i > contentHeight) {//展开
                                startAnim(paddingTop, getOpenHeight());
                                currOpen = true;
                                if (onVerticalDrawerListener != null) {
                                    onVerticalDrawerListener.onChangeOpen(currOpen);
                                }
                            } else startAnim(paddingTop, -contentHeight);
                        } else startAnim(paddingTop, -contentHeight);//恢复原位
                    }
                }
                onTouchButton = false;
                break;
        }
        if (onTouchButton) {
            getParent().requestDisallowInterceptTouchEvent(true);
            return true;
        } else return super.dispatchTouchEvent(ev);
    }

    private void startAnim(float startY, float endY) {
        if (startY == endY) return;
        ValueAnimator animator = ValueAnimator.ofFloat(startY, endY);
        animator.setDuration(300);
        animator.setInterpolator(new DecelerateInterpolator());
        animator.addUpdateListener(animation -> {
            Object animatedValue = animation.getAnimatedValue();
            if (animatedValue instanceof Float) {
                Float f = (Float) animatedValue;
                setPadding(getPaddingLeft(), f.intValue(), getPaddingRight(), getPaddingBottom());
            }
        });
        animator.start();
    }

olda.LayoutDrawerViewHolder.java
item的位置是相对于RecyclerView。这部分代码用于计算item相对位置,并执行动画

    private void startChaseAnim(int paddingTop) {
        ViewHolderAnim holderAnimTmp;
        if (animList == null) {
            final int animCurrY = -drawerLayout.getContentHeight();
            holderAnimTmp = new ViewHolderAnim(animCurrY, paddingTop);
            animList = new ArrayList<>(5);
            animList.add(holderAnimTmp);//先创建一个
        } else {
            holderAnimTmp = animList.get(0);
            for (int i = 0; i < animList.size(); i++) {
                ViewHolderAnim holderAnim = animList.get(i);
                holderAnim.setAnimEndY(paddingTop);
            }
        }
        final int firstPosition = layoutManager.findFirstVisibleItemPosition();
        final int lastPosition = layoutManager.findLastVisibleItemPosition();
        List<View> viewList = new ArrayList<>();
        for (int i = firstPosition; i <= lastPosition; i++) {
            View view = layoutManager.findViewByPosition(i);
            if (view == null) continue;
            viewList.add(view);
        }

        for (int i = 0; i < viewList.size(); i++) {
            View view = viewList.get(i);
            ViewHolderAnim holderAnim;
            if (i < animList.size()) {
                holderAnim = animList.get(i);
            } else {
                holderAnim = holderAnimTmp.cloneAnim();
                animList.add(holderAnim);
            }
            int top = holderAnim.animCurrY - holderAnim.animEndY;
            setViewTop(view, top);
        }
        startViewAnim();
    }

==ViewHolderAnim ==计算并模拟动画

        private static final float Rub = 0.45f;//摩擦
        public int animCurrY;//动画当前位置或开始位置 -ContentHeight
        public int animEndY;//动画要追逐的位置 当前的paddingTop
        public float animV;//速度
        public float animG;//动态加速度
        public float animGB;//这个决定最大的加速度
        
		public void move() {
            int len = animCurrY - animEndY;//距离
            animG = -len / animGB;
            if (animG > maxG) animG = maxG;
            else if (animG < -2.0f) animG = -2.0f;
            animV += animG;
            animCurrY = (int) (animCurrY + animV);
            if (len > 0) {
                if (animCurrY - animEndY < 0) animV = Rub * animV;//减速
            } else if (animCurrY - animEndY > 0) animV = Rub * animV;//减速
        }

动画二,主要就是对RecyclerView平移和对ViewPage缩放。这儿就不说了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值