Flutter- 可以缩放拖拽的图片,安卓内存优化管理器
return Rect.fromLTWH(center.dx - width / 2.0, center.dy - height / 2.0, width, height);}
拖拽边界的计算
1.计算是否需要计算限制边界 2.如果需要将区域限制在边界内部
if (_computeHorizontalBoundary) {//move rightif (result.left >= layoutRect.left) {result = Rect.fromLTWH(0.0, result.top, result.width, result.height);_boundary.left = true;}
///move leftif (result.right <= layoutRect.right) {result = Rect.fromLTWH(layoutRect.right - result.width, result.top,result.width, result.height);_boundary.right = true;}}
if (_computeVerticalBoundary) {//move downif (result.bottom <= layoutRect.bottom) {result = Rect.fromLTWH(result.left, layoutRect.bottom - result.height,result.width, result.height);_boundary.bottom = true;}
//move upif (result.top >= layoutRect.top) {result = Rect.fromLTWH(result.left, layoutRect.top, result.width, result.height);_boundary.top = true;}}
_computeHorizontalBoundary =result.left <= layoutRect.left && result.right >= layoutRect.right;
_computeVerticalBoundary =result.top <= layoutRect.top && result.bottom >= layoutRect.bottom;
缩放回弹效果以及拖拽惯性效果
void _handleScaleEnd(ScaleEndDetails details) {//animate back to maxScale if gesture exceeded the maxScale specifiedif (_gestureDetails.totalScale > _gestureConfig.maxScale) {final double velocity =(_gestureDetails.totalScale - _gestureConfig.maxScale) /_gestureConfig.maxScale;
_gestureAnimation.animationScale(_gestureDetails.totalScale, _gestureConfig.maxScale, velocity);return;}
//animate back to minScale if gesture fell smaller than the minScale specifiedif (_gestureDetails.totalScale < _gestureConfig.minScale) {final double velocity =(_gestureConfig.minScale - _gestureDetails.totalScale) /_gestureConfig.minScale;
_gestureAnimation.animationScale(_gestureDetails.totalScale, _gestureConfig.minScale, velocity);return;}
if (_gestureDetails.gestureState == GestureState.pan) {// get magnitude from gesture velocityfinal double magnitude = details.velocity.pixelsPerSecond.distance;
// do a significant magnitudeif (magnitude >= minMagnitude) {final Offset direction = details.velocity.pixelsPerSecond /magnitude *_gestureConfig.inertialSpeed;
_gestureAnimation.animationOffset(_gestureDetails.offset, _gestureDetails.offset + direction);}}}
唯一注意的是 Scale 的回弹动画将以最后的缩放中心点为中心进行缩放,这样缩放动画才看起来舒服一些
//true: user zoom/pan//false: animationfinal bool userOffset;Offset _getCenter(Rect destinationRect) {if (!userOffset && _center != null) {return _center;}
在 PageView 里面缩放拖拽
用法
1.使用ExtendedImageGesturePageView
展示图片
2.设置 GestureConfig 的 inPageView 为 Ture
GestureConfig 参数说明
实现过程
手势冲突
这个场景需要关注的是手势的冲突问题,PageView 里面是有水平或者垂直的手势的,会跟 onScaleStart/onScaleUpdate/onScaleEnd 有冲突。
最开始想的是手势应该有冒泡,是不是可以我监听到了之后,不像上冒泡,这样可以阻止 PageView 里面的滑动行为,最后结论是没有方法能阻止冒泡。
关于手势,大家可以看看拉面小姐姐关于手势的文章,神奇的竞技场概念。。
既然不能阻止手势冒泡,那么我就直接不让你能滚动了,然后全部的手势都交给我,我来处理。
首先我看了下 PageView 关于滚动的源码,直接指向最终 ScrollableState 里面的代码,在 setCanDrag 方法里面根据是否可以 Drag,准备了水平/垂直的手势。
把 ScrollableState 里面关于水平垂直滚动处理的代码拿出来,我创建了一个属于 extended_image 专门的 extended_image_gesture_page_view,属性跟 PageView 一样只是没法设置 physics,因为强制设置为了 NeverScrollableScrollPhysics
Widget result = PageView.custom(scrollDirection: widget.scrollDirection,reverse: widget.reverse,controller: widget.controller,childrenDelegate: widget.childrenDelegate,pageSnapping: widget.pageSnapping,physics: widget.physics,onPageChanged: widget.onPageChanged,key: widget.key,);
result = RawGestureDetector(gestures: _gestureRecognizers,behavior: HitTestBehavior.opaque,child: result,);
然后我们通过 RawGestureDetector 来注册_gestureRecognizers(水平/垂直的手势)。
关于_gestureRecognizers,我之前一直好奇 PageView 里面有个手 hold 的操作是怎么做到了,直到看到源码才知道这么个东西,源码真是个好东西。
void _handleDragDown(DragDownDetails details) {//print(details);_gestureAnimation.stop();assert(_drag == null);assert(_hold == null);_hold = position.hold(_disposeHold);}
到达边界滚动上下一个图片
有了之前缩放拖拽的基础,这部分就比较简单了。如果到达边界就是用默认代码去操作 PageView,否则就控制 Image 进行拖拽操作
void _handleDragUpdate(DragUpdateDetails details) {// _drag might be null if the drag activity ended and called _disposeDrag.assert(_hold == null || _drag == null);var delta = details.delta;
if (extendedImageGestureState != null) {var gestureDetails = extendedImageGestureState.gestureDetails;if (gestureDetails != null) {if (gestureDetails.movePage(delta)) {_drag?.update(details);} else {extendedImageGestureState.gestureDetails = GestureDetails(offset: gestureDetails.offset +delta * extendedImageGestureState.imageGestureConfig.speed,totalScale: gestureDetails.totalScale,gestureDetails: gestureDetails);}} else {_drag?.update(details);}} else {_drag?.update(details);}}
拖拽惯性效果
在 DragEnd 的时候,我们需要注意下处理下惯性。当图片是放大状态而且水平或者垂直能够滑动的时候,我们需要_drag 停止下来,以防止直接滑动到上一个或者下一个图片DragEndDetails(primaryVelocity: 0.0)
,并且根据惯性让图片在范围内继续惯性滑动。
void _handleDragEnd(DragEndDetails details) {// _drag might be null if the drag activity ended and called _disposeDrag.assert(_hold == null || _drag == null);
var temp = details;
if (extendedImageGestureState != null) {var gestureDetails = extendedImageGestureState.gestureDetails;
if (gestureDetails != null && gestureDetails.computeHorizontalBoundary ||gestureDetails.computeVerticalBoundary) {//stoptemp = DragEndDetails(primaryVelocity: 0.0);
// get magnitude from gesture velocityfinal double magnitude = details.velocity.pixelsPerSecond.distance;
// do a significant magnitudeif (magnitude >= minMagnitude) {Offset direction = details.velocity.pixelsPerSecond /magnitude *(extendedImageGestureState.imageGestureConfig.inertialSpeed);
if (widget.scrollDirection == Axis.horizontal) {direction = Offset(direction.dx, 0.0);
评论