背景
學習一個新的框架谱净,我還是更喜歡在實踐中去學習,最近在學習 Flutter 時擅威,我就從一個圖片瀏覽器來做起壕探,在實現(xiàn)過程中,我發(fā)現(xiàn)用 Flutter 來實現(xiàn) PictureView 非常簡單郊丛,因為框架已經(jīng)給我們做了大部分的事情李请,不像在原生 Android 上做的這么復雜,實現(xiàn)的效果大概如下:
代碼
因為內(nèi)容很簡單厉熟,直接先把源碼貼出來导盅,然后大概說一下里面的關(guān)鍵點就行了。
import 'package:flutter/material.dart';
import 'dart:io';
class PictureView extends StatefulWidget {
final String _path;
PictureView(this._path);
@override
State<StatefulWidget> createState() => _PictureViewState();
}
class _PictureViewState extends State<PictureView> with TickerProviderStateMixin {
double _scale = 1.0;
double _tmpScale = 1.0;
double _moveX = 0.0;
double _tmpMoveX = 0.0;
double _moveY = 0.0;
double _tmpMoveY = 0.0;
double _rotation = 0.0;
double _tmpRotation = 0.0;
Offset _tmpFocal = Offset.zero;
AnimationController _animationController;
Animation<double> _values;
@override
void initState() {
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 120));
// Tween 將動畫的 0 - 1 的值映射到我們設(shè)置的范圍內(nèi)
_values = Tween(begin: 1.0, end: 0.0).animate(_animationController);
_animationController.addListener(() {
setState(() {
// 通過動畫逐幀還原位置
_moveX = _tmpMoveX * _values.value;
_moveY = _tmpMoveY * _values.value;
_scale = (_tmpScale - 1) * _values.value + 1;
_rotation = _tmpRotation * _values.value;
});
});
super.initState();
}
@override
void dispose() {
_animationController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// 配置 Matrix
Matrix4 matrix4 = Matrix4.identity()
..scale(_scale, _scale)
..translate(_moveX, _moveY)
..rotateZ(_rotation);
return Scaffold(
body: GestureDetector(
onDoubleTap: () {
if (!_animationController.isAnimating) {
_tmpMoveX = _moveX;
_tmpMoveY = _moveY;
_tmpScale = _scale;
_tmpRotation = _rotation;
_animationController.reset();
_animationController.forward();
}
},
onScaleStart: (details) {
if (!_animationController.isAnimating) {
_tmpFocal = details.focalPoint;
_tmpMoveX = _moveX;
_tmpMoveY = _moveY;
_tmpScale = _scale;
_tmpRotation = _rotation;
}
},
onScaleUpdate: (details) {
if (!_animationController.isAnimating) {
setState(() {
_moveX = _tmpMoveX + (details.focalPoint.dx - _tmpFocal.dx) / _tmpScale;
_moveY = _tmpMoveY + (details.focalPoint.dy - _tmpFocal.dy) / _tmpScale;
_scale = _tmpScale * details.scale;
_rotation = _tmpRotation + details.rotation;
print(_rotation);
});
}
},
child: Container(
color: Colors.black,
child: Center(
child: Hero(
tag: widget._path,
child: Transform(
alignment: FractionalOffset.center,
transform: matrix4,
child: Image.file(
File(widget._path),
),
),
),
),
),
),
);
}
}
這里面比較有意思的就是 GestureDetector 這個手勢監(jiān)控控件揍瑟,因為我們要實現(xiàn)拖拽和縮放白翻,所以理所當然的我就想要實現(xiàn)了 onPan*** 和 onScale*** 兩種接口來實現(xiàn)這個效果,結(jié)果發(fā)現(xiàn)绢片,這兩種接口不能同時實現(xiàn)滤馍,他們存在沖突岛琼,同時實現(xiàn)會直接拋異常,于是我想到了用 GestureDetector 嵌套 GestureDetector 來實現(xiàn)巢株,外部的 GestureDetector 來實現(xiàn)縮放邏輯槐瑞,內(nèi)部的 GestureDetector 來實現(xiàn) 拖動邏輯,這樣最終實現(xiàn)了我想要的效果阁苞,但是在通過 onScale*** 接口來實現(xiàn)旋轉(zhuǎn)的時候随珠,發(fā)現(xiàn)旋轉(zhuǎn)的邏輯又與拖動的邏輯沖突了,非常的惡心猬错,這里更加神奇的是窗看,旋轉(zhuǎn)手勢的參數(shù) rotation 竟然通過 onScale*** 接口提供,給人一種東拼西湊的感覺倦炒。
既然多個手勢無法合并來用显沈,而 onScale*** 接口已經(jīng)可以幫我們實現(xiàn)縮放和旋轉(zhuǎn)了,那我們就想辦法去獲得拖動量就可以了逢唤,這里我想到了可以通過縮放焦點的位置變化來間接的去計算拖動距離拉讯,具體的實現(xiàn)就像上方代碼一樣。
總結(jié)
其實我認為學習一個新技術(shù)最好的方法就是在了解了它的基本規(guī)則之后就嘗試去寫一些 DEMO鳖藕,然后在 DEMO 中發(fā)現(xiàn)為題魔慷,逐步的去細化,了解原理著恩,這樣學習的過程就不會枯燥乏味了院尔。
吐槽
這里很想吐槽一下 Dart 中的 “;” ,相比較于其它語法來說真的是很奇怪喉誊,語法都已經(jīng)這么簡潔了為什么還非要寫一個 “;”邀摆,而且編輯器還不會自動幫你補上 ,特別是在寫 Flutter 這種多嵌套的結(jié)構(gòu)的時候伍茄,真的很麻煩栋盹,真的希望 Dart 可以多借鑒一下 Kotlin 的語法,個人還是蠻喜歡 Kotlin 的敷矫,哈哈例获。