add listview reader type
This commit is contained in:
parent
046a9e8f50
commit
6328e7d83b
|
@ -7,12 +7,14 @@ enum ReaderType {
|
||||||
WEB_TOON,
|
WEB_TOON,
|
||||||
WEB_TOON_ZOOM,
|
WEB_TOON_ZOOM,
|
||||||
GALLERY,
|
GALLERY,
|
||||||
|
WEB_TOON_FREE_ZOOM,
|
||||||
}
|
}
|
||||||
|
|
||||||
const _types = {
|
const _types = {
|
||||||
'WebToon (默认)': ReaderType.WEB_TOON,
|
'WebToon (默认)': ReaderType.WEB_TOON,
|
||||||
'WebToon + 双击放大': ReaderType.WEB_TOON_ZOOM,
|
'WebToon (双击放大)': ReaderType.WEB_TOON_ZOOM,
|
||||||
'相册': ReaderType.GALLERY,
|
'相册': ReaderType.GALLERY,
|
||||||
|
'WebToon (ListView双击放大)\n(此模式进度条无效)': ReaderType.WEB_TOON_FREE_ZOOM
|
||||||
};
|
};
|
||||||
|
|
||||||
const _propertyName = "readerType";
|
const _propertyName = "readerType";
|
||||||
|
|
|
@ -202,6 +202,8 @@ class _ImageReaderContent extends StatefulWidget {
|
||||||
return _WebToonZoomReaderState();
|
return _WebToonZoomReaderState();
|
||||||
case ReaderType.GALLERY:
|
case ReaderType.GALLERY:
|
||||||
return _GalleryReaderState();
|
return _GalleryReaderState();
|
||||||
|
case ReaderType.WEB_TOON_FREE_ZOOM:
|
||||||
|
return _ListViewReaderState();
|
||||||
default:
|
default:
|
||||||
throw Exception("ERROR READER TYPE");
|
throw Exception("ERROR READER TYPE");
|
||||||
}
|
}
|
||||||
|
@ -352,7 +354,26 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> {
|
||||||
),
|
),
|
||||||
Container(width: 10),
|
Container(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: widget.pagerType != ReaderType.WEB_TOON_FREE_ZOOM
|
||||||
|
? _buildSlider()
|
||||||
|
: Container(),
|
||||||
|
),
|
||||||
|
Container(width: 10),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.skip_next_outlined),
|
||||||
|
color: Colors.white,
|
||||||
|
onPressed: _onNextAction,
|
||||||
|
),
|
||||||
|
Container(width: 15),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSlider() {
|
||||||
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Container()),
|
Expanded(child: Container()),
|
||||||
Container(
|
Container(
|
||||||
|
@ -365,8 +386,7 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> {
|
||||||
onDragging: (handlerIndex, lowerValue, upperValue) {
|
onDragging: (handlerIndex, lowerValue, upperValue) {
|
||||||
_slider = (lowerValue.toInt());
|
_slider = (lowerValue.toInt());
|
||||||
},
|
},
|
||||||
onDragCompleted:
|
onDragCompleted: (handlerIndex, lowerValue, upperValue) {
|
||||||
(handlerIndex, lowerValue, upperValue) {
|
|
||||||
_slider = (lowerValue.toInt());
|
_slider = (lowerValue.toInt());
|
||||||
if (_slider != _current) {
|
if (_slider != _current) {
|
||||||
_needJumpTo(_slider, false);
|
_needJumpTo(_slider, false);
|
||||||
|
@ -393,8 +413,7 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> {
|
||||||
decoration: ShapeDecoration(
|
decoration: ShapeDecoration(
|
||||||
color: Colors.black.withAlpha(0xCC),
|
color: Colors.black.withAlpha(0xCC),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius:
|
borderRadius: BorderRadiusDirectional.circular(3)),
|
||||||
BorderRadiusDirectional.circular(3)),
|
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'${a.toInt()}',
|
'${a.toInt()}',
|
||||||
|
@ -409,19 +428,6 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> {
|
||||||
),
|
),
|
||||||
Expanded(child: Container()),
|
Expanded(child: Container()),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: 10),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.skip_next_outlined),
|
|
||||||
color: Colors.white,
|
|
||||||
onPressed: _onNextAction,
|
|
||||||
),
|
|
||||||
Container(width: 15),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1096,6 +1102,196 @@ class _WebToonZoomReaderState extends _WebToonReaderState {
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class _ListViewReaderState extends _ImageReaderContentState
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
final List<Size?> _trueSizes = [];
|
||||||
|
final _transformationController = TransformationController();
|
||||||
|
late TapDownDetails _doubleTapDetails;
|
||||||
|
late final _animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: Duration(milliseconds: 100),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
widget.struct.images.forEach((e) {
|
||||||
|
if (e.downloadLocalPath != null) {
|
||||||
|
_trueSizes.add(Size(e.width!.toDouble(), e.height!.toDouble()));
|
||||||
|
} else {
|
||||||
|
_trueSizes.add(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_transformationController.dispose();
|
||||||
|
_animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void _needJumpTo(int index, bool animation) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildViewer() {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
child: _buildList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList() {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
|
// reload _images size
|
||||||
|
List<Widget> _images = [];
|
||||||
|
for (var index = 0; index < widget.struct.images.length; index++) {
|
||||||
|
late Size renderSize;
|
||||||
|
if (_trueSizes[index] != null) {
|
||||||
|
if (widget.pagerDirection == ReaderDirection.TOP_TO_BOTTOM) {
|
||||||
|
renderSize = Size(
|
||||||
|
constraints.maxWidth,
|
||||||
|
constraints.maxWidth *
|
||||||
|
_trueSizes[index]!.height /
|
||||||
|
_trueSizes[index]!.width,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
renderSize = Size(
|
||||||
|
constraints.maxHeight *
|
||||||
|
_trueSizes[index]!.width /
|
||||||
|
_trueSizes[index]!.height,
|
||||||
|
constraints.maxHeight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (widget.pagerDirection == ReaderDirection.TOP_TO_BOTTOM) {
|
||||||
|
renderSize = Size(constraints.maxWidth, constraints.maxWidth / 2);
|
||||||
|
} else {
|
||||||
|
// ReaderDirection.LEFT_TO_RIGHT
|
||||||
|
// ReaderDirection.RIGHT_TO_LEFT
|
||||||
|
renderSize =
|
||||||
|
Size(constraints.maxWidth / 2, constraints.maxHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var currentIndex = index;
|
||||||
|
var onTrueSize = (Size size) {
|
||||||
|
setState(() {
|
||||||
|
_trueSizes[currentIndex] = size;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var e = widget.struct.images[index];
|
||||||
|
if (e.downloadLocalPath != null) {
|
||||||
|
_images.add(_WebToonDownloadImage(
|
||||||
|
fileServer: e.fileServer,
|
||||||
|
path: e.path,
|
||||||
|
localPath: e.downloadLocalPath!,
|
||||||
|
fileSize: e.fileSize!,
|
||||||
|
width: e.width!,
|
||||||
|
height: e.height!,
|
||||||
|
format: e.format!,
|
||||||
|
size: renderSize,
|
||||||
|
onTrueSize: onTrueSize,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
_images.add(_WebToonRemoteImage(
|
||||||
|
e.fileServer,
|
||||||
|
e.path,
|
||||||
|
renderSize,
|
||||||
|
onTrueSize,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var list = ListView.builder(
|
||||||
|
scrollDirection:
|
||||||
|
widget.pagerDirection == ReaderDirection.TOP_TO_BOTTOM
|
||||||
|
? Axis.vertical
|
||||||
|
: Axis.horizontal,
|
||||||
|
reverse: widget.pagerDirection == ReaderDirection.RIGHT_TO_LEFT,
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
// 不管全屏与否, 滚动方向如何, 顶部永远保持间距
|
||||||
|
top: super._topBarHeight(),
|
||||||
|
bottom: widget.pagerDirection == ReaderDirection.TOP_TO_BOTTOM
|
||||||
|
? 130 // 纵向滚动 底部永远都是130的空白
|
||||||
|
: ( // 横向滚动
|
||||||
|
widget.struct.fullScreen
|
||||||
|
? super._topBarHeight() // 全屏时底部和顶部到屏幕边框距离一样保持美观
|
||||||
|
: super._bottomBarHeight())
|
||||||
|
// 非全屏时, 顶部去掉顶部BAR的高度, 底部去掉底部BAR的高度, 形成看似填充的效果
|
||||||
|
,
|
||||||
|
),
|
||||||
|
itemCount: widget.struct.images.length + 1,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
if (widget.struct.images.length == index) {
|
||||||
|
return _buildNextEp();
|
||||||
|
}
|
||||||
|
return _images[index];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
var viewer = InteractiveViewer(
|
||||||
|
transformationController: _transformationController,
|
||||||
|
minScale: 1,
|
||||||
|
maxScale: 2,
|
||||||
|
child: list,
|
||||||
|
);
|
||||||
|
return GestureDetector(
|
||||||
|
onDoubleTap: _handleDoubleTap,
|
||||||
|
onDoubleTapDown: _handleDoubleTapDown,
|
||||||
|
child: viewer,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildNextEp() {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
child: MaterialButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (super._hasNextEp()) {
|
||||||
|
super._onNextAction();
|
||||||
|
} else {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
textColor: Colors.white,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(top: 40, bottom: 40),
|
||||||
|
child: Text(super._hasNextEp() ? '下一章' : '结束阅读'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleDoubleTapDown(TapDownDetails details) {
|
||||||
|
_doubleTapDetails = details;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleDoubleTap() {
|
||||||
|
if (_animationController.isAnimating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_transformationController.value != Matrix4.identity()) {
|
||||||
|
_transformationController.value = Matrix4.identity();
|
||||||
|
} else {
|
||||||
|
var position = _doubleTapDetails.localPosition;
|
||||||
|
var animation = Tween(begin: 0, end: 1.0).animate(_animationController);
|
||||||
|
animation.addListener(() {
|
||||||
|
_transformationController.value = Matrix4.identity()
|
||||||
|
..translate(
|
||||||
|
-position.dx * animation.value, -position.dy * animation.value)
|
||||||
|
..scale(animation.value + 1.0);
|
||||||
|
});
|
||||||
|
_animationController.forward(from: 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class _GalleryReaderState extends _ImageReaderContentState {
|
class _GalleryReaderState extends _ImageReaderContentState {
|
||||||
late PageController _pageController;
|
late PageController _pageController;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue