实现一个可定制化的TabFlowLayout(二) -- 实现滚动和平滑过渡

效果图

在这里插入图片描述

FlowHelper工程源码

这次,我们来实现第二个模块,即view 的滚动和使用 Scroller 平滑滚动,在这篇文章中,您将看到:

  1. View 的事件传递简析
  2. ScrollerBy 和 ScrollerTo 的区别,以及使用 Scroller 实现平滑过渡

前面中,我们已经通过 FlowLayout 实现测量和布局,这次新建一个类 ScrollFlowLayout 是专门实现滚动逻辑

一、View 的事件传递

当点击一个控件的时候,它的向下传递过程大致如下: activity --> window – > viewGroud --> view 。当然第一次走的是 disPatchTouchEvent 方法;通过源码知道,如果我们对 onInterceptTouchEvent 返回true,则父控件接管当前触摸事件,不再往下传递,而是回调自己的 onTouchEvent 方法。

View 的事件传递是基操,大家自行查阅啦

那么,我们就可以在 onInterceptTouchEvent 去这样写:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = ev.getX();
                //拿到上次的down坐标
                mMoveX = ev.getX();
                break;

            case MotionEvent.ACTION_MOVE:
                float dx = ev.getX() - mLastX;
                if (Math.abs(dx) >= mTouchSlop) {
                    //由父控件接管触摸事件
                    return true;
                }
                mLastX = ev.getX();
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

二、View 的滚动和 Scroller

在 onTouchEvent 中,拿到了移动的偏移量,那怎么实现 View 自身的移动呢?
没错,就是使用 ScrollerBy 和 ScrollerTo,它们只改变 View 的内容而不会改变 View 的坐标 ,这正是我们需要的,需要注意的是,向左滑为正,向右为负。

  • ScrollerTo(int x,int y) 绝对坐标移动,以原点为参考点
  • ScrollerBy(int x,int y) 相对坐标移动,以上一次坐标为参考点

那么,onTouchEvent 就可以这样写了:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                //scroller 向右为负,向左为正
                int dx = (int) (mMoveX - event.getX());
                scrollBy(dx, 0);
                mMoveX = event.getX();
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;

        }
        return super.onTouchEvent(event);
    }

效果如下:
在这里插入图片描述
嗯,还差边界判断,首先,拿到右边边界:

mRightBound = child.getRight() + getPaddingRight();

接着,在move中去判断边界:

 case MotionEvent.ACTION_MOVE:
     //scroller 向右为负,向左为正
     int dx = (int) (mMoveX - event.getX());
     /**
      * 判断左右边界
      */
     int scrollX = getScrollX();
     if (scrollX + dx <= 0) {
         scrollTo(0, 0);
         return true;
     }
     if (scrollX + dx >= mRightBound - mScreenWidth) {
         scrollTo(mRightBound - mScreenWidth, 0);
         return true;
     }
     scrollBy(dx, 0);
     mMoveX = event.getX();
     break;

接着,再运行一下:
在这里插入图片描述
边界加上了,但是总感觉有点卡顿,不够流畅,我们接着优化一下:

三、使用 Scroller 优化滑动卡顿

上面看到,当通过手指按住滑动之后,应该要有个滚动速度;从 Scroller 的 API 中,我们发现可以使用 Scroller 中的 Fling 方法;
先初始化 Scroller

mScroller = new Scroller(context);

而这个横向的滚动速度怎么来呢?
可以有两种,第一个是通过手势 Gestrue 的 Fling 拿到,还有一种则是 VelocityTracker ;这里,我们直接用 VelocityTracker 来拿到横向速度;
首先 VelocityTracker 通过 obtain 来拿到实例,并通过 addMoveMent 拿到 MotionEvent,这样才能正确的速度:

 if (mVelocityTracker == null) {
    mVelocityTracker = VelocityTracker.obtain();
    }
mVelocityTracker.addMovement(event);

在 up 的时候,拿到横向速度:

 case MotionEvent.ACTION_UP:
 case MotionEvent.ACTION_CANCEL:

     mVelocityTracker.computeCurrentVelocity(1000,mMaximumVelocity);
     int velocityX = (int) mVelocityTracker.getXVelocity();
     if (Math.abs(velocityX) >= mMinimumVelocity) {
         mCurScrollX = getScrollX();
         mScroller.fling(mCurScrollX, 0, velocityX, 0, 0, getWidth(), 0, 0);
         if (mVelocityTracker != null) {
             mVelocityTracker.recycle();
             mVelocityTracker = null;
         }
     }
    break;

其中 mVelocityTracker.getXVelocity() 表示的是 1s 内偏移的像素点;接着再把它赋值给 mScroller.fling();

当调用了 fling 之后,Scroller 就会调用 computeScroll 方法了。在 computeScroll 方法,拿到当前偏移的像素,与上次的对比,即可让它平滑滚动,如下:

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()){
            int dx = mCurScrollX - mScroller.getCurrX();
            // 超出右边界,进行修正
            if (getScrollX() + dx >= mRightBound - mScreenWidth) {
                dx = mRightBound - mScreenWidth - getScrollX();
            }

            // 超出左边界,进行修正
            if (getScrollX() + dx <= 0) {
                dx = -getScrollX();
            }
            scrollBy(dx,0);
            postInvalidate();
        }
    }

最终效果如下 (gif 看起来一般般,建议运行看效果):
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个基于Vue.js实现的el-tabs平滑页面切换的代码示例: HTML模板: ``` <template> <el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick"> <el-tab-pane label="Tab 1" name="tab1"> <transition :name="transitionName" mode="out-in"> <div v-show="activeTab === 'tab1'" class="tab-content"> <p>Tab 1 Content</p> </div> </transition> </el-tab-pane> <el-tab-pane label="Tab 2" name="tab2"> <transition :name="transitionName" mode="out-in"> <div v-show="activeTab === 'tab2'" class="tab-content"> <p>Tab 2 Content</p> </div> </transition> </el-tab-pane> <el-tab-pane label="Tab 3" name="tab3"> <transition :name="transitionName" mode="out-in"> <div v-show="activeTab === 'tab3'" class="tab-content"> <p>Tab 3 Content</p> </div> </transition> </el-tab-pane> </el-tabs> </template> ``` JavaScript代码: ``` <script> export default { data() { return { activeTab: 'tab1', transitionName: 'slide-left' } }, methods: { handleTabClick(tab) { if (tab.index < this.activeTab) { this.transitionName = 'slide-right' } else { this.transitionName = 'slide-left' } this.activeTab = tab.index } } } </script> ``` CSS样式: ``` <style> .tab-content { height: 200px; padding: 20px; background-color: #f2f2f2; border-radius: 5px; } .slide-left-enter, .slide-right-leave-to { opacity: 0; transform: translateX(-100%); } .slide-left-leave-to, .slide-right-enter { opacity: 0; transform: translateX(100%); } .slide-left-enter-active, .slide-right-leave-active, .slide-left-leave-active, .slide-right-enter-active { transition: all .5s ease; } </style> ``` 基本原理是利用Vue的过渡动画效果实现平滑的页面切换。当点击Tab的时候,根据当前Tab和点击的Tab的位置关系,设置不同的过渡动画效果。在CSS中定义不同的过渡效果,包括初始状态、结束状态和过渡过程中的样式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值