我正在嘗試制作一個與 Google 日歷周視圖完全相同的小部件。這意味著
- 捏縮放
- 垂直滾動
這是一個例子。
這是該示例的GitHub 存盤庫。
為了簡單起見,我想只關注垂直滾動是可能的,我可以自己添加其余部分。
問題是 PinchToZoom 非常不可靠,即使我在捏縮放,串列也會開始滾動。為什么會這樣?我做了一些研究,發現了這篇文章。
它基本上描述了我的問題的簡化版本,即兩個 GestureDetector 競爭。解決方案是 RawGestureDetector。我自己寫了:
class PinchToZoomGestureRecognizer extends OneSequenceGestureRecognizer {
final void Function() onScaleStart;
final void Function() onScaleUpdate;
final void Function() onScaleEnd;
PinchToZoomGestureRecognizer({
required this.onScaleStart,
required this.onScaleUpdate,
required this.onScaleEnd,
});
@override
String get debugDescription => '$runtimeType';
Map<int, Offset> pointerPositionMap = {};
@override
void addAllowedPointer(PointerEvent event) {
startTrackingPointer(event.pointer);
pointerPositionMap[event.pointer] = event.position;
if (pointerPositionMap.length >= 2) {
resolve(GestureDisposition.accepted);
}
}
@override
void handleEvent(PointerEvent event) {
if (event is PointerMoveEvent) {
pointerPositionMap[event.pointer] = event.position;
return;
} else if (event is PointerDownEvent) {
pointerPositionMap[event.pointer] = event.position;
} else if (event is PointerUpEvent || event is PointerCancelEvent) {
stopTrackingPointer(event.pointer);
pointerPositionMap.remove(event.pointer);
}
if (pointerPositionMap.length >= 2) {
resolve(GestureDisposition.accepted);
}
}
@override
void didStopTrackingLastPointer(int pointer) {
resolve(GestureDisposition.rejected);
}
}
所有這一切都應該做的是,如果有兩個或更多指標,則在 GestureArena 中宣告 Victory。如果兩個指標同時進入競技場,這很好用。但是,如果它們不是,并且 ListView 接受了第一個指標,那么對于第二個指標,我的 GestureDetector 甚至都不會添加到 Arena:
I/flutter (13717): Gesture arena 8 ? ★ Opening new gesture arena.
I/flutter (13717): Gesture arena 8 ? Adding: PinchToZoomGestureRecognizer#f9b77
I/flutter (13717): Gesture arena 8 ? Adding: VerticalDragGestureRecognizer#07525(start behavior: start)
I/flutter (13717): Gesture arena 8 ? Closing with 2 members.
I/flutter (13717): Gesture arena 8 ? Accepting: VerticalDragGestureRecognizer#07525(start behavior: start)
I/flutter (13717): Gesture arena 8 ? Self-declared winner: VerticalDragGestureRecognizer#07525(start behavior: start)
D/EGL_emulation(13717): app_time_stats: avg=781.66ms min=5.34ms max=12373.45ms count=16
I/flutter (13717): Gesture arena 9 ? ★ Opening new gesture arena.
I/flutter (13717): Gesture arena 9 ? Adding: VerticalDragGestureRecognizer#07525(start behavior: start)
I/flutter (13717): Gesture arena 9 ? Accepting: VerticalDragGestureRecognizer#07525(start behavior: start)
I/flutter (13717): Gesture arena 9 ? Closing with 1 member.
I/flutter (13717): Gesture arena 9 ? Default winner: VerticalDragGestureRecognizer#07525(start behavior: start)
這意味著,如果用戶沒有完美地調整他們的兩根手指捏縮放的時間,串列將簡單地滾動。
這是我用來測驗的小部件代碼:
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() {
debugPrintGestureArenaDiagnostics = true;
runApp(const MaterialApp(home: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: RawGestureDetector(
gestures: <Type, GestureRecognizerFactory>{
PinchToZoomGestureRecognizer: GestureRecognizerFactoryWithHandlers<
PinchToZoomGestureRecognizer>(
() => PinchToZoomGestureRecognizer(
onScaleStart: () {},
onScaleUpdate: () {},
onScaleEnd: () {},
),
(instance) {},
),
},
child: Container(
width: MediaQuery.of(context).size.width,
decoration: const BoxDecoration(
color: Colors.blueGrey,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(
100,
(index) => Text("Item $index"),
),
),
),
),
),
);
}
}
請注意,我實際上并沒有實作捏縮放,因為這很簡單,它只是讓 GestureDetector 首先識別手勢。
如果有人可以幫助我使用完全不同的方法來縮放 ListView,或者知道 RawGestureDetector 是如何作業的,以及為什么在這種情況下它不起作用,我將不勝感激。
知道如何制作可靠的縮放小部件嗎?
uj5u.com熱心網友回復:
我在 GoogleMap 中使用了帶有 EagerGestureRecognizer 的手勢識別器,當我在串列旁邊有一張地圖并且地圖支持的捏合手勢時,看看這是否有幫助
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>[
Factory<OneSequenceGestureRecognizer>(
() => EagerGestureRecognizer(),
),
].toSet(),
uj5u.com熱心網友回復:
對于捏到縮放功能,您可以使用InteractiveViewer小部件,它允許您識別縮放事件、設定最小/最大縮放因子等
簡單用法示例:
InteractiveViewer(
minScale: 0.1,
maxScale: 2.0,
onInteractionStart: (details) {},
onInteractionUpdate: (details) {},
onInteractionEnd: (details) {},
child: widget,
)
uj5u.com熱心網友回復:
編輯:使用下面的舊答案后,我遇到了一些不同的問題,例如 Scrollable 中的一些失敗的斷言。我的新方法如下:
- 復制并粘貼代碼
DragGestureRecognizer并重命名為SinglePointerDragRecognizer. 將添加允許的指標方法更改為以下內容:
@override
void addAllowedPointer(PointerDownEvent event) {
super.addAllowedPointer(event);
_velocityTrackers[event.pointer] = velocityTrackerBuilder(event);
// ADD THIS
if (_velocityTrackers.length > 1) {
resolve(GestureDisposition.rejected);
return;
}
// END OF ADD THIS
if (_state == _DragState.ready) {
_state = _DragState.possible;
_initialPosition =
OffsetPair(global: event.position, local: event.localPosition);
_initialButtons = event.buttons;
_pendingDragOffset = OffsetPair.zero;
_globalDistanceMoved = 0.0;
_lastPendingEventTimestamp = event.timeStamp;
_lastTransform = event.transform;
_checkDown();
} else if (_state == _DragState.accepted) {
resolve(GestureDisposition.accepted);
}
}
復制和粘貼代碼
VerticalDragGestureRecognizer并重命名為SinglePointerVerticalDragGestureRecognizer并使其擴展SinglePointerDragRecognizer執行與
Scrollable前 2 步相同的操作,并將所有出現的 替換VerticalDragGestureRecognizer為SinglePointerVerticalDragGestureRecognizer在您選擇的滾動視圖中(例如
SingleChildScrollView,ListView等)做同樣的事情并將任何出現的 Scrollable 替換為SinglePointerScrollable使用該滾動視圖代替內置視圖
舊答案
我遇到了一個非常相似的問題。我在 Github 問題上發現了這條評論,它達到了令人滿意的水平,但仍然不完美:
https://github.com/flutter/flutter/issues/72996#issuecomment-918180838
這是關于在哪里可以找到代碼的后續評論:
https://github.com/flutter/flutter/issues/72996#issuecomment-981446721
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/516458.html
標籤:安卓扑镖用户界面列表显示
