我正在尝试实现一个水平可滚动的值选择器,类似于这个:
用户滚动“胶带”左右以选择值(显示在中间框中)。胶带具有最大和最小值,到达时会显示Typical Overscroll Animation(Android上的发光;iOS上的弹跳)。
Hixie在Gitter上建议我可以使用
编辑:经过进一步调查,我改变了原来的方法,使用低级别的小部件,例如
我已经通过扩展
这样做达到了我想要的效果:现在我可以左右滚动磁带,并且免费获得过度滚动效果。
问题在于当前代码效率低下:整个磁带只绘制一次,而滚动条只是通过移动缓冲位图实现。这对于非常大的“磁带”会造成问题。
相反,我正在寻找的是在每帧重新绘制小部件,以便只需计算和绘制可见部分。这也将使我能够实现其他滚动相关效果,例如随着数字靠近中心动态淡入。
![Scrollable tape value selector](https://istack.dev59.com/PbZVw.webp)
Hixie在Gitter上建议我可以使用
GestureDetector
+CustomPaint
,但我感觉我需要自己实现滚动逻辑,并且无法利用Flutter的fling和overscroll实现。编辑:经过进一步调查,我改变了原来的方法,使用低级别的小部件,例如
Scrollable
和Viewport
。我已经通过扩展
CustomPaint
并将其宽度设置为胶带的全长来创建胶带:
_width = (_maxValue - _minValue) * _spacing;
然后,我将我的自定义小部件放在CustomScrollView中:import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(new MaterialApp(home: new Scaffold(
appBar: new AppBar(title: new Text("Test"),),
body: new CustomScrollView(
scrollDirection: Axis.horizontal,
slivers: <Widget>[
new SliverToBoxAdapter(
child: new Tape(),
)
],
)
)));
}
const _width = (_maxValue - _minValue) * spacing;
const spacing = 20.0;
const _minValue = 0;
const _maxValue = 100;
class Tape extends CustomPaint {
Tape() : super(
size: new Size(_width, 60.0),
painter: new _TapePainter(),
);
}
class _TapePainter extends CustomPainter {
Paint _tickPaint;
_TapePainter() {
_tickPaint = new Paint();
_tickPaint.color = Colors.black;
_tickPaint.strokeWidth = 1.0;
}
@override
void paint(Canvas canvas, Size size) {
var rect = Offset.zero & size;
var o1 = new Offset(0.0, 0.0);
var o2 = new Offset(0.0, rect.height);
while (o1.dx < size.width) {
canvas.drawLine(o1, o2, _tickPaint);
o1 = o1.translate(spacing, 0.0);
o2 = o2.translate(spacing, 0.0);
}
}
@override
bool shouldRepaint(_TapePainter oldDelegate) {
return true;
}
}
这样做达到了我想要的效果:现在我可以左右滚动磁带,并且免费获得过度滚动效果。
问题在于当前代码效率低下:整个磁带只绘制一次,而滚动条只是通过移动缓冲位图实现。这对于非常大的“磁带”会造成问题。
相反,我正在寻找的是在每帧重新绘制小部件,以便只需计算和绘制可见部分。这也将使我能够实现其他滚动相关效果,例如随着数字靠近中心动态淡入。