如何像“ recyclerview字母表索引在android中”那样颤动地获得手指移动事件,请检查示例图像。
我已经创建了位置字母索引列表视图,但是在DragUpdate中找不到当前索引。
var alphabet = ["#","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
new Positioned(
top: .0,
left: 1,
bottom: 10,
width: 55,
child: Material(
borderRadius: BorderRadius.circular(15.0),
elevation: 10.0,
child: ListView.builder(
itemCount: alphabet.length,
itemBuilder: (BuildContext context, int index) {
return new GestureDetector(
onVerticalDragUpdate:
(DragUpdateDetails detail) {
setState(() {
_barOffset += detail.delta.dy;
});
print("$detail");
print("Update ${alphabet[index]}");
},
onVerticalDragStart: (DragStartDetails detail) {
print("onVerticalDragStart");
print("Start ${alphabet[index]}");
},
onVerticalDragEnd: (DragEndDetails detail) {
print("onVerticalDragEnd");
print("End ${alphabet[index]}");
},
onTap: () => print(alphabet[index]),
child: new Container(
margin: EdgeInsets.only(
left: 20.0, right: 10.0, top: 6.5),
height: 15.0,
child: new Text('${alphabet[index]}'),
));
}),
),
答案 0 :(得分:3)
执行以下步骤以实现此目的。
第一步:用于A到Z滑块的手势检测器
onVerticalDragUpdate:_onVerticalDragUpdate
onVerticalDragStart:_onVerticalDragStart
第2步::为滚动字母添加语音提示。
步骤3:计算垂直滚动位置的索引
if ((_offsetContainer + details.delta.dy) >= 0 &&
(_offsetContainer + details.delta.dy) <=
(_sizeheightcontainer - _heightscroller)) {
_offsetContainer += details.delta.dy;
posSelected =
((_offsetContainer / _heightscroller) % _alphabet.length).round();
_text = _alphabet[posSelected];
if (_text != _oldtext) {
for (var i = 0; i < exampleList.length; i++) {
if (_text
.toString()
.compareTo(exampleList[i].toString().toUpperCase()[0]) == 0) {
_controller.jumpTo(i * _itemsizeheight);
break;
}
}
_oldtext = _text;
}
}
有关完整实施的信息,请检查以下内容:
答案 1 :(得分:1)
要拥有这样的东西:
要解决此问题,您需要创建一个自定义窗口小部件,该窗口小部件将返回包含LayoutBuilder
的{{1}},其中Stack
的第一层包含ListView
,而Container
包含第二层(作为滚动条)在右边(将滚动条放在右边,将alignment: Alignment.topRight
添加到Stack
中。)
要移动滚动条,请添加GestureDetector
并捕获onVerticalDragUpdate
,使用自定义属性_offsetContainer
到您要添加的details.delta.dy
(来自事件)中让您的滚动条上下滚动。
此后,您需要向ScrollController
中添加ListView
来控制它的垂直移动(作为controller
属性)。
好吧,现在您需要知道多个内容才能继续:
ListView
ListView
中一项(我们称为_itemsizeheight
)的高度尺寸现在要达到的目标是使ListView
从列表中的第一字母开始移动。
为此,您需要一些数学和一些技巧。
要计算您的ListView
身高,您需要获取身体身高并删除其他小部件的高度。但是为了简化起见,我们将假设您的ListView
占据了整个身体的高度。然后它对应于contrainsts.biggest.height
(感谢LayoutBuilder
)。
要计算滚动条的高度,您可以有效地完成,并将正文高度除以字母数组中的字母数。
_heightscroller = contrainsts.biggest.height / _alphabet.length;
我们将首先使用Text
小部件使滚动条显示字母索引。然后将Text
小部件添加到控制器,并在自定义小部件中添加_text
属性,以允许您使用setState
函数对其进行更改。
在您的onVerticalDragUpdate
中,您的_offset
值与滚动条的位置相对应。然后,您需要进行一个“简单”计算,以找到与之对应的字母数组的女巫索引。
_text = _alphabet[((_offsetContainer / _heightscroller) % _alphabet.length).round()];
现在您知道字母了,那么移动您的ListView
很容易。
首先在您的第一个元素列表中获得与所选字母相对应的索引。
示例:
var index = 0;
for (var i = 0; i < widget.items.length; i++) {
if (widget.items[i].toString().trim().length > 0 &&
widget.items[i].toString().trim().toUpperCase()[0] ==
_text.toString().toUpperCase()[0]) {
index = i;
break;
}
}
现在您有了索引,只需使用ScrollController
移动ListView
。
_scrollController.animateTo(index * _itemsizeheight,
duration: new Duration(milliseconds: 500),
curve: Curves.ease);
好,现在可以了,但是我们也需要移动滚动条,不是吗?我的意思是当V
向我显示ListView
个字母项目时,我不希望它停留在A
字母上。
如何执行此操作?
在您设置自己的ScrollController
之后,请在init函数中添加一个
_scrollController.addListener(onscrolllistview);
来了解用户何时直接在ListView
中滚动。
在您的onscrolllistview
函数中,您需要进行一些计算才能显示第一个项目和最后一个项目。
var indexFirst = ((_scrollController.offset / _itemsizeheight) % widget.items.length).floor();
var indexLast = ((((_heightscroller * _alphabet.length) / _itemsizeheight).floor() + indexFirst) - 1) % widget.items.length;
完美的现在,您可以轻松地知道需要在滚动条上显示的女巫字母,然后知道它的位置(更改其偏移量)。
var i = _alphabet.indexOf(fletter);
if (i != -1) {
setState(() {
_text = _alphabet[i];
_offsetContainer = i * _heightscroller;
});
fletter
var将取决于滚动方向。
如果您要向下走,它将是:
var fletter = widget.items[indexLast].toString().toUpperCase()[0];
如果您要向上走,将会是:
var fletter = widget.items[indexFirst].toString().toUpperCase()[0];
现在确定,如果您放置debugPrint
,即使我们正在使用滚动条,也将看到onscrolllistview
函数一直被调用。这是个问题。那怎么解决呢?
所有动画制作完成后,您需要知道何时完成移动。
然后只需在您的班级中添加一个_animationcounter
属性。
当您移动滚动条时,可以增加它,而在完成一个动画时,可以减少它。
像这样:
for (var i = 0; i < widget.items.length; i++) {
if (widget.items[i].toString().trim().length > 0 &&
widget.items[i].toString().trim().toUpperCase()[0] ==
_text.toString().toUpperCase()[0]) {
_animationcounter++;
_scrollController.animateTo(i * _itemsizeheight,
duration: new Duration(milliseconds: 500),
curve: Curves.ease) .then((x) => {_animationcounter--});
break;
}
}
现在只需将滚动条的代码围拢在onscrolllistview
中,
if (_animationcounter == 0) {/*...your code...*/}
您需要_offsetContainer
照顾自己,以免越界。
例如,您可以这样做:
if ((_offsetContainer + details.delta.dy) >= 0 &&
(_offsetContainer + details.delta.dy) <=
(context.size.height - _heightscroller)) {
_offsetContainer += details.delta.dy;
}
此解决方案可能已经过优化(不发送多个动画,添加var可以减少计算量)。您可以在这里找到完整的实现: AtoZscrollflutter
答案 2 :(得分:0)
您可以尝试执行以下操作:
Material(
child: ListView.builder(
itemCount: alphabet.length,
itemBuilder: (BuildContext context, int index) {
var letterHeight = (SizeConfig.safeBlockVertical * 100) / 33;
return GestureDetector(
onVerticalDragUpdate: (DragUpdateDetails detail) {
setState(() {
currentPosition += detail.delta.dy;
});
},
onVerticalDragEnd: (DragEndDetails detail) {
setState(() {
int letters = currentPosition > 0
? ((currentPosition + letterHeight) / letterHeight)
.round()
: ((currentPosition - letterHeight) / letterHeight)
.round();
if (index + letters < alphabet.length) {
index += letters;
} else {
index = alphabet.length;
}
currentPosition = 0;
currentLetter = alphabet[index];
});
},
child: InkWell(
onTap: () {
setState(() {
currentLetter = alphabet[index];
});
},
child: Container(
height: letterHeight,
child: Center(
child: Text('${alphabet[index]}'),
),
),
),
);
},
),
)