造轮子:滚轮选择器实现及原理解析(二)

Android · 2023-07-21 · 624 人浏览
造轮子:滚轮选择器实现及原理解析(二)

系列文章
造轮子:滚轮选择器实现及原理解析(一)
造轮子:滚轮选择器实现及原理解析(二)
造轮子:滚轮选择器实现及原理解析(三)
造轮子:滚轮选择器实现及原理解析(源码)

回顾

造轮子:滚轮选择器实现及原理解析(一)
上一篇文章我们简单绘制出了基本框图,此篇我们开始尝试使其动起来

拆解动画

体验过滚轮选择器的同学应该能感受到,滚轮存在着多种动画,这次我们只处理和交互相关的动画,其他动画放到下一节分析

  1. 跟手动画 - 手拖动到哪就跟随滚动到哪
  2. 惯性动画 - 松手后一段时间内仍可滚动,速度慢慢降低
  3. 吸附动画 - 速度降低到一定数值时,使其吸附到就近的一个item上

跟手动画

说到跟手动画,那一定是放到touch事件里处理,我们的处理方式也和常规拖动监听一样简单,拖动时实时修改curY的位置,并刷新页面。这里新增了一个scrollState存储当前状态。

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 重置状态
            scrollState = SCROLL_STATE_NORMAL;
            lastY = event.getY();
            startY = event.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            curY -= event.getY() - lastY;
            // 调整y使其不超出边界
            curY = adjustingY(curY);
            lastY = event.getY();
            invalidate();
            break;
        default:
            break;
    }

    return true;
}

device-2023-07-21-144210.gif

惯性动画

惯性动画当然离不开Scroller,使用Scroller传入速度很容易使其实时帮助计算出当前惯性距离
调整一下onTouch事件

public boolean onTouchEvent(MotionEvent event) {
    // 速度监听
    velocityTracker.addMovement(event);
    switch (event.getAction()) {  
        ...    
        case MotionEvent.ACTION_UP:
            curY -= event.getY() - lastY;
            // 调整y使其不超出边界
            curY = adjustingY(curY);
            lastY = event.getY();
            // 计算速度,根据速度决定惯性滚动还是吸附
            velocityTracker.computeCurrentVelocity(1000, 2000);
            checkTouch((int) velocityTracker.getXVelocity(), (int) velocityTracker.getYVelocity());
            break;
        default:
            break;
    }

    return true;
}

在调用了scroller.fling方法后,会基于Y方向的速度进行实时计算,我们在computeScroll方法里对当前y值进行计算赋值,逻辑类似于拖拽时的计算,代码就不放了,感兴趣的可以直接看源码。

吸附动画

在开始滚动但是速度不足的场景,或滚动时速度慢慢变慢的场景下,需要使其吸附到最近的item上,逻辑很简单,直接上代码

public void startAdsorbAnim() {
    float y = curY;
    int centerPosition = getCenterShowPosition(y);
    float offsetY = adjustingY(y) - itemHeight * centerPosition;
    int newPosition;
    // 超出一半时吸附到下一个item
    if (offsetY >= itemHeight / 2f) {
        // 封装的动画,本质是ValueAnimator
        manager.playAnim(KEY_PICKER_ADSORB_ANIM, curY, curY + itemHeight - offsetY);
        newPosition = adjustingPosition(centerPosition + 1);
    } else {
       // 封装的动画,本质是ValueAnimator
        manager.playAnim(KEY_PICKER_ADSORB_ANIM, curY, curY - offsetY);
        newPosition = adjustingPosition(centerPosition);
    }
    if (pickerChangeListener != null && curPosition != newPosition) {
        curPosition = newPosition;
        pickerChangeListener.onPickerSelectedChanged(this, curPosition, data[curPosition], true);
    }
}

至此,能够完成基本的动画,使其看起来起码像一个滚轮了,下一篇我们开始优化细节,使其易用,美观

android picker 自定义View
Theme Jasmine by Kent Liao