系列文章

造轮子:滚轮挑选器完成及原理解析(一)

造轮子:滚轮挑选器完成及原理解析(二)

造轮子:滚轮挑选器完成及原理解析(三)

造轮子:滚轮挑选器完成及原理解析(源码)

完成效果

上方非循环翻滚,下方循环翻滚 翻滚下面的挑选器使上方挑选器联动

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

需求拆解

滚轮挑选器的运用场景最常见于时间挑选,手机上操作出生年月时常常能看到年月日挑选器。 其最典型特征:

  1. 数据在同一列展示
  2. 中心部分突出显现
  3. 有3D环绕感
  4. 支持循环翻滚
  5. 拥有惯性翻滚与就近吸附动画

抛开动画和展示效果不谈,首要任务在于如何把内容排列在屏幕上。

模仿顺序排列

造轮子:滚轮选择器实现及原理解析(一)
视觉上典型的LinearLayout布局,从上到下按顺序排列,乃至翻滚都是类似的,不过咱们这里不运用LinearLayout,当需要循环时运用LinearLayout并不便利

// onDraw()
for (int i = 0; i < size; i++) {
    drawItem(canvas, i);
}
// drawItem()
// 丈量文字,制作时居中显现
float textWidth = paint.measureText(text);
// 核算偏移量,在每行固定高度时,偏移量很好核算
float totalOffset = i* itemHeight;
canvas.save();
canvas.translate(0, -totalOffset);
// 此处用于适配文字baseline,否则会导致文字无法制作在绝对居中方位
Paint.FontMetrics metrics = paint.getFontMetrics();
canvas.drawText(text, width / 2f - textWidth / 2f,  - (metrics.top + metrics.bottom) / 2f, paint);
canvas.restore();

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

发现问题

  1. 在翻滚时翻滚距离会实时改变,没办法只是运用i*itemHeight核算偏移
  2. 翻滚默许方位应当从正中心开端的,而非顶部
  3. 高度就那么高,我的数据过多时也看不到

改造

  1. 运用暂时变量curY模仿当时翻滚距离,运用该变量核算
  2. 因为是从中心开端翻滚,咱们制作方式调整为从中心发散制作(关于3D视图来说,中心处于缩放点,从中心开端核算更便利,后续会用到)
  3. 约束可见个数与制作个数,全部制作糟蹋性能不说实际也看不到

1.根据当时翻滚距离得到中心方位的下标

protected int getCenterShowPosition(float y) {
    //这里进行y纠正,避免小于0或超出float上限,便于后续核算
    float newY = adjustingY(y);
    return (int) (newY / itemHeight);
}

现在咱们将模型图转换: 因为咱们制作文本时,总是居中制作,所以理论上咱们只关怀制作点的中心线段在哪,后续制作时根据itemHeight进行平移纠正一下,后续咱们只关怀线条的方位即可

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

2. 改变整体制作逻辑

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    float y = curY;
    // 获取中心方位item的下标
    int centerPosition = getCenterShowPosition(y);
    // item不可能刚好在中线,核算出它的偏移距离
    float offsetY = adjustingY(y) - itemHeight * centerPosition;
    // 约束制作个数,为了确保上下对称,当向上翻滚一段距离时,应当在下方多弥补一个item制作
    int max = centerPosition + showCount;
    // 处于正中心时使两边显现相同个数item,非中心时下方添加1个
    if (offsetY > 0f) {
        max += 1;
    }
    // 只遍历可显现出来的部分
    for (int i = centerPosition - showCount + 1; i < max; i++) {
        drawItem(canvas, i, centerPosition, offsetY);
    }
}

3. 改变item制作逻辑

// 核算和中心item的差距
int count = centerPosition - position;
float totalOffset = offsetY + count * itemHeight;
canvas.save();
canvas.translate(0, -totalOffset);
Paint.FontMetrics metrics = paint.getFontMetrics();
// 制作在中心点的方位
canvas.drawText(text, width / 2f - textWidth / 2f, height / 2f - (metrics.top + metrics.bottom) / 2f, paint);
canvas.restore();

至此,简略的滚轮挑选器的根底制作已完成,调整curY可使其方位发生改变

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