安卓才判断双指校验

This commit is contained in:
豌杂 2025-04-25 17:49:38 +08:00
parent 4d42562508
commit 1ebdbf101a
1 changed files with 205 additions and 95 deletions

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:flutter/cupertino.dart';
@ -30,7 +31,8 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
HomeworkReviewState get sateData => controller.state;
ZoomState get zoomState => controller.zoomLogic.zoomState;
HomeworkReviewAnnotationsControlState get annotationState => controller.annotationState;
HomeworkReviewAnnotationsControlState get annotationState =>
controller.annotationState;
@override
Widget build(BuildContext context) {
@ -46,11 +48,13 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
if (zoomFileModel == null) {
///
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
WidgetsBinding.instance.addPostFrameCallback((_) => zoomState.zoomFile.value = ZoomFileModel(
viewWidth: constraints.maxWidth,
viewHeight: constraints.maxHeight,
));
return LayoutBuilder(builder:
(BuildContext context, BoxConstraints constraints) {
WidgetsBinding.instance.addPostFrameCallback(
(_) => zoomState.zoomFile.value = ZoomFileModel(
viewWidth: constraints.maxWidth,
viewHeight: constraints.maxHeight,
));
return const SizedBox();
});
}
@ -63,7 +67,11 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
return Stack(
children: [
//
QuestionImageView(maxWidth, maxHeight, annotationState, controller, zoomState: zoomState, sateData: sateData, actualHeight: zoomFileModel.actualHeight!),
QuestionImageView(
maxWidth, maxHeight, annotationState, controller,
zoomState: zoomState,
sateData: sateData,
actualHeight: zoomFileModel.actualHeight!),
//
// Positioned(right: 3.w, bottom: 4.h, child: const $ContinueToReview(isFloatingAction: true)),
//
@ -77,15 +85,19 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
heroTag: '点击前往上一题',
tooltip: '点击前往上一题',
focusColor: Theme.of(context).primaryColor,
backgroundColor: const Color.fromRGBO(24, 32, 32, 0.05),
backgroundColor:
const Color.fromRGBO(24, 32, 32, 0.05),
elevation: 10.r,
onPressed: () => easyThrottle('TestQuestionSwitch', () {
onPressed: () =>
easyThrottle('TestQuestionSwitch', () {
var param = sateData.param.value;
param.studentId = lastPageVal.studentId;
param.templateId = lastPageVal.templateId;
sateData.param.value = DoPaperDetailsParam.fromJson(param.toJson());
sateData.param.value =
DoPaperDetailsParam.fromJson(param.toJson());
}),
child: Icon(Icons.arrow_back_ios, color: Colors.white, size: 22.sp),
child: Icon(Icons.arrow_back_ios,
color: Colors.white, size: 22.sp),
);
}),
),
@ -101,14 +113,18 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
heroTag: '点击前往下一题',
tooltip: '点击前往下一题',
elevation: 10.r,
backgroundColor: const Color.fromRGBO(24, 32, 32, 0.05),
onPressed: () => easyThrottle('TestQuestionSwitch', () {
backgroundColor:
const Color.fromRGBO(24, 32, 32, 0.05),
onPressed: () =>
easyThrottle('TestQuestionSwitch', () {
var param = sateData.param.value;
param.studentId = nextPageVal.studentId;
param.templateId = nextPageVal.templateId;
sateData.param.value = DoPaperDetailsParam.fromJson(param.toJson());
sateData.param.value =
DoPaperDetailsParam.fromJson(param.toJson());
}),
child: Icon(Icons.arrow_forward_ios, color: Colors.white, size: 22.sp),
child: Icon(Icons.arrow_forward_ios,
color: Colors.white, size: 22.sp),
);
}),
),
@ -145,7 +161,8 @@ class DataErrorThenRequestAgainButton extends StatelessWidget {
child: CupertinoButton(
color: Colors.grey[300],
onPressed: () => easyThrottle('home_work_reload_data', () {
sateData.param.value = DoPaperDetailsParam.fromJson(sateData.param.value.toJson());
sateData.param.value =
DoPaperDetailsParam.fromJson(sateData.param.value.toJson());
}),
child: quickText('再次请求', color: Colors.black38),
),
@ -156,7 +173,8 @@ class DataErrorThenRequestAgainButton extends StatelessWidget {
//
@swidget
Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData) {
Widget $totalSubmitCountView(
BuildContext context, HomeworkReviewState sateData) {
return Obx(() {
var data = sateData.data.value;
if (data == null) return Container();
@ -171,7 +189,10 @@ Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData)
context: context,
elevation: 10,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(10.r), topRight: Radius.circular(10.r))),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.r),
topRight: Radius.circular(10.r))),
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 2.w),
@ -189,7 +210,8 @@ Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData)
SizedBox(height: 10.h),
Expanded(
child: ListView(
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w),
padding: EdgeInsets.symmetric(
vertical: 8.h, horizontal: 4.w),
children: [
Wrap(
spacing: 7.2.w, // ()
@ -200,27 +222,39 @@ Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData)
alignment: const FractionalOffset(0.05, 0.09),
children: [
Container(
padding: EdgeInsets.only(top: 1.2.h, bottom: 1.5.h, left: 13.w, right: 5.w),
padding: EdgeInsets.only(
top: 1.2.h,
bottom: 1.5.h,
left: 13.w,
right: 5.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.r),
color: const Color.fromRGBO(239, 242, 255, 1),
color: const Color.fromRGBO(
239, 242, 255, 1),
),
child: quickText(
e.name,
size: 12.sp,
wordSpacing: 2,
color: const Color.fromRGBO(80, 94, 110, 1),
color:
const Color.fromRGBO(80, 94, 110, 1),
),
),
Stack(
alignment: const FractionalOffset(0.52, 0.24),
alignment:
const FractionalOffset(0.52, 0.24),
children: [
Icon(
const IconData(0xe63d, fontFamily: "AlibabaIcon"),
const IconData(0xe63d,
fontFamily: "AlibabaIcon"),
size: 12.sp,
color: e.isPriority ? Theme.of(context).primaryColor : const Color.fromRGBO(164, 164, 164, 1),
color: e.isPriority
? Theme.of(context).primaryColor
: const Color.fromRGBO(
164, 164, 164, 1),
),
quickText('优先', size: 4.sp, color: Colors.white),
quickText('优先',
size: 4.sp, color: Colors.white),
],
),
],
@ -242,11 +276,17 @@ Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData)
children: [
Padding(
padding: EdgeInsets.only(bottom: 1.h),
child: quickText('已阅', color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
child: quickText('已阅',
color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
),
quickText(data.annotatedCount, color: Theme.of(context).primaryColor, size: 12.sp, fontWeight: FontWeight.bold),
quickText('/', color: const Color.fromRGBO(117, 117, 117, 1), size: 12.sp),
quickText(data.submitCount - data.annotatedCount, color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
quickText(data.annotatedCount,
color: Theme.of(context).primaryColor,
size: 12.sp,
fontWeight: FontWeight.bold),
quickText('/',
color: const Color.fromRGBO(117, 117, 117, 1), size: 12.sp),
quickText(data.submitCount - data.annotatedCount,
color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
],
),
),
@ -255,7 +295,8 @@ Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData)
}
//
class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar> {
class QuestionImageView extends HookWidget
with EventBusMixin<BottomOperationBar> {
final double maxWidth;
final double maxHeight;
final double actualHeight;
@ -263,7 +304,12 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
final ZoomState zoomState;
final HomeworkReviewState sateData;
final HomeworkReviewAnnotationsControlState annotationState;
QuestionImageView(this.maxWidth, this.maxHeight, this.annotationState, this.logic, {required this.actualHeight, required this.zoomState, required this.sateData, super.key});
QuestionImageView(
this.maxWidth, this.maxHeight, this.annotationState, this.logic,
{required this.actualHeight,
required this.zoomState,
required this.sateData,
super.key});
///
int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) {
@ -310,7 +356,6 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
// lastDrop = getLastDrop(vals, index);
// }
// }
}
return lastDrop;
}
@ -318,10 +363,12 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
@override
Widget build(BuildContext context) {
final theMaxHeight = useState<double>(maxHeight);
useValueChanged<double, void>(maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight);
useValueChanged<double, void>(
maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight);
var zoomKey = useState<GlobalKey>(GlobalKey());
useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId, (old, __) {
useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId,
(old, __) {
zoomKey.value = GlobalKey();
});
@ -340,9 +387,13 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
});
var streamSubscriptionSlide = sateData.slide.listen((e) {
if (sateData.panQuestView != null && sateData.panQuestView == false && initPosition.value?.dy.abs().toInt().toDouble() != sateData.slide.value) {
if (sateData.panQuestView != null &&
sateData.panQuestView == false &&
initPosition.value?.dy.abs().toInt().toDouble() !=
sateData.slide.value) {
if (sateData.zoomOffset != null) {
sateData.zoomOffset = Offset(sateData.zoomOffset!.dx, -sateData.slide.value);
sateData.zoomOffset =
Offset(sateData.zoomOffset!.dx, -sateData.slide.value);
}
initPosition.value = sateData.zoomOffset;
}
@ -363,7 +414,17 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
bool? res = await showDialog<bool>(
context: Get.context ?? context,
builder: (context1) {
return AlertDialog(content: quickText("是否撤销全部批注痕迹?"), actions: <Widget>[TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)), TextButton(child: quickText("确定", color: Theme.of(context1).primaryColor), onPressed: () => Navigator.pop(context1, true))]);
return AlertDialog(
content: quickText("是否撤销全部批注痕迹?"),
actions: <Widget>[
TextButton(
child: quickText("取消"),
onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定",
color: Theme.of(context1).primaryColor),
onPressed: () => Navigator.pop(context1, true))
]);
},
);
if (res == true) vnHandWritings.value = [];
@ -373,33 +434,43 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
await showDialog<bool>(
context: Get.context ?? context,
builder: (context1) {
return AlertDialog(content: quickText("是否撤销上次批阅批注痕迹?"), actions: <Widget>[
TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定", color: Theme.of(context1).primaryColor),
onPressed: () => easyThrottle(
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
() {
Navigator.pop(context1, true);
sateData.data.value?.zgtAnnotate = null;
sateData.data.value?.showZgtAnnotate = null;
if (sateData.data.value != null) {
sateData.data.update((_) {
var theStudentQuestions = sateData.studentQuestions.value;
if (theStudentQuestions?.isNotEmpty ?? false) {
var noMarking = theStudentQuestions?.firstWhereOrNull((e) => e.studentScore == null);
if (noMarking != null) {
ToastUtils.showInfo("未提交!请为第${noMarking.questionNo}题打分,再手动提交");
return;
}
return AlertDialog(
content: quickText("是否撤销上次批阅批注痕迹?"),
actions: <Widget>[
TextButton(
child: quickText("取消"),
onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定",
color: Theme.of(context1).primaryColor),
onPressed: () => easyThrottle(
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
() {
Navigator.pop(context1, true);
sateData.data.value?.zgtAnnotate = null;
sateData.data.value?.showZgtAnnotate = null;
if (sateData.data.value != null) {
sateData.data.update((_) {
var theStudentQuestions =
sateData.studentQuestions.value;
if (theStudentQuestions?.isNotEmpty ??
false) {
var noMarking =
theStudentQuestions?.firstWhereOrNull(
(e) => e.studentScore == null);
if (noMarking != null) {
ToastUtils.showInfo(
"未提交!请为第${noMarking.questionNo}题打分,再手动提交");
return;
}
}
logic.submit(Get.context ?? context);
});
}
logic.submit(Get.context ?? context);
});
}
},
),
)
]);
},
),
)
]);
},
);
}
@ -414,23 +485,29 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
await showDialog<bool>(
context: context,
builder: (context1) {
return AlertDialog(content: quickText("是否撤销上次批阅批注痕迹?"), actions: <Widget>[
TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定", color: Theme.of(context).primaryColor),
onPressed: () => easyThrottle(
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
() {
Navigator.pop(context1, true);
sateData.data.value?.zgtAnnotate = null;
sateData.data.value?.showZgtAnnotate = null;
if (sateData.data.value != null) {
sateData.data.update((_) => logic.submit(Get.context ?? context));
}
},
),
)
]);
return AlertDialog(
content: quickText("是否撤销上次批阅批注痕迹?"),
actions: <Widget>[
TextButton(
child: quickText("取消"),
onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定",
color: Theme.of(context).primaryColor),
onPressed: () => easyThrottle(
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
() {
Navigator.pop(context1, true);
sateData.data.value?.zgtAnnotate = null;
sateData.data.value?.showZgtAnnotate = null;
if (sateData.data.value != null) {
sateData.data.update(
(_) => logic.submit(Get.context ?? context));
}
},
),
)
]);
},
);
}
@ -462,6 +539,7 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
//
_activePointers = _activePointers + 1;
///
toTimer(vnHandWritings);
sateData.panQuestView = true;
@ -489,13 +567,17 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
Offset localPosition = event.localPosition; //
var zoomFile = zoomState.zoomFile.value!;
// var imageHeightOffsetStart = zoomFile.imageHeightOffsetStart??0;
var imageHeightOffsetStart = zoomState.zoomFile.value!.getZoomFileOffsetStart(zoomState.initScale.value ?? 1);
var imageHeightOffsetStart = zoomState.zoomFile.value!
.getZoomFileOffsetStart(zoomState.initScale.value ?? 1);
// print("位置:$localPosition; 图片所在位置:$imageHeightOffsetStart");
if (imageHeightOffsetStart == 0) return;
var dy = localPosition.dy;
// print(zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1));
if (dy < imageHeightOffsetStart || dy > zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1)) return; //
if (dy < imageHeightOffsetStart ||
dy >
zoomFile.getZoomFileHeightOffsetEnd(
zoomState.initScale.value ?? 1)) return; //
var theScale = zoomState.initScale.value ?? 1;
// if (theScale != 1) {
@ -516,21 +598,40 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
// }
// (dy / theScale) - (max(0, imageHeightOffsetStart) / theScale) + ((sateData.zoomOffset?.dy.abs() ?? 0) / theScale),
var zoomWtdthSpaceVal = zoomFile.getZoomFileOffsetStartWidth(zoomState.initScale.value ?? 1);
var zoomWtdthSpaceVal = zoomFile
.getZoomFileOffsetStartWidth(zoomState.initScale.value ?? 1);
localPosition = Offset(
(localPosition.dx - zoomFile.getZoomFileOffsetStartWidth(zoomState.initScale.value ?? 1) + ((zoomWtdthSpaceVal <= 0.1) ? (sateData.zoomOffset?.dx.abs() ?? 0) : 0)) / theScale,
(dy - max(0, imageHeightOffsetStart) + ((zoomFile.imageHeightOffsetStart == null || zoomFile.imageHeightOffsetStart! <= 0.1) ? (sateData.zoomOffset?.dy.abs() ?? 0) : 0)) / theScale,
(localPosition.dx -
zoomFile.getZoomFileOffsetStartWidth(
zoomState.initScale.value ?? 1) +
((zoomWtdthSpaceVal <= 0.1)
? (sateData.zoomOffset?.dx.abs() ?? 0)
: 0)) /
theScale,
(dy -
max(0, imageHeightOffsetStart) +
((zoomFile.imageHeightOffsetStart == null ||
zoomFile.imageHeightOffsetStart! <= 0.1)
? (sateData.zoomOffset?.dy.abs() ?? 0)
: 0)) /
theScale,
);
///
var lastDrop = getLastDrop(vnHandWritings.value, vnHandWritings.value.length-1);
if (lastDrop != null && ((lastDrop.dx - localPosition.dx).abs() > 65 || (lastDrop.dy - localPosition.dy).abs() > 65)) {
/// X点和上一个x点相差 10
return;
if (Platform.isAndroid) {
var lastDrop = getLastDrop(
vnHandWritings.value, vnHandWritings.value.length - 1);
if (lastDrop != null &&
((lastDrop.dx - localPosition.dx).abs() > 65 ||
(lastDrop.dy - localPosition.dy).abs() > 65)) {
/// X点和上一个x点相差 10
return;
}
}
// print("最终位置 $localPosition");
vnHandWritings.value = List.from(vnHandWritings.value)..add(localPosition);
vnHandWritings.value = List.from(vnHandWritings.value)
..add(localPosition);
sateData.handwritings = vnHandWritings.value;
},
child: Obx(() {
@ -563,13 +664,15 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
$TheCachedNetworkImage(
imgWidth: maxWidth,
imageUrl: sateData.data.value!.zgtAnswer,
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
(_, imageProvider) =>
Image(image: imageProvider, fit: BoxFit.fitWidth),
),
RepaintBoundary(
key: logic.pictureOverviewKey,
child: CustomPaint(
// isComplex: true,
size: Size(maxWidth, zoomState.zoomFile.value!.actualHeight!),
size: Size(
maxWidth, zoomState.zoomFile.value!.actualHeight!),
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
// child: $TheCachedNetworkImage(
// imgWidth: maxWidth,
@ -577,7 +680,13 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
// (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
// ),
child: showZgtAnnotate != null ? $TheCachedNetworkImage(imgWidth: maxWidth, imageUrl: showZgtAnnotate, (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth)) : null,
child: showZgtAnnotate != null
? $TheCachedNetworkImage(
imgWidth: maxWidth,
imageUrl: showZgtAnnotate,
(_, imageProvider) => Image(
image: imageProvider, fit: BoxFit.fitWidth))
: null,
),
),
],
@ -606,7 +715,8 @@ class DrawingPainter extends CustomPainter {
for (int i = 0; i < pointsLength; i++) {
Offset? offsetData = points[i];
Offset? nextOffsetData = pointsLength - 1 == i ? null : points[i + 1];
if (offsetData != null && nextOffsetData != null) canvas.drawLine(offsetData, nextOffsetData, paintBrush);
if (offsetData != null && nextOffsetData != null)
canvas.drawLine(offsetData, nextOffsetData, paintBrush);
}
}