Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
F
flutter_plugin
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
林生雨
flutter_plugin
Commits
f0733818
Commit
f0733818
authored
Nov 04, 2019
by
林生雨
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
commit
parent
2f543fef
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
2296 additions
and
212 deletions
+2296
-212
workspace.xml
.idea/workspace.xml
+115
-97
build.gradle
android/build.gradle
+1
-0
AndroidManifest.xml
android/src/main/AndroidManifest.xml
+1
-0
GengmeiFlutterPlugin.kt
...om/example/gengmei_flutter_plugin/GengmeiFlutterPlugin.kt
+21
-1
ImageRespository.kt
...flutter_plugin/ImagePlugin/repository/ImageRespository.kt
+63
-36
MediaFile.kt
...i_flutter_plugin/ImagePlugin/repository/bean/MediaFile.kt
+2
-1
ImageScanner.kt
...utter_plugin/ImagePlugin/repository/local/ImageScanner.kt
+6
-2
ThumbUtil.java
...lutter_plugin/ImagePlugin/repository/local/ThumbUtil.java
+2
-0
PreviewActivity.kt
...com/example/gengmei_flutter_plugin/act/PreviewActivity.kt
+32
-0
VideoActivity.kt
...a/com/example/gengmei_flutter_plugin/act/VideoActivity.kt
+9
-0
PinchImageView.kt
...example/gengmei_flutter_plugin/act/view/PinchImageView.kt
+1756
-0
MyUtil.kt
...n/java/com/example/gengmei_flutter_plugin/utils/MyUtil.kt
+17
-1
preview_act.xml
android/src/main/res/layout/preview_act.xml
+25
-0
AlbumModel.dart
example/lib/AlbumModel/page/album/AlbumModel.dart
+19
-18
GengmeiFlutterPlugin.m
ios/Classes/GengmeiFlutterPlugin.m
+78
-48
MyPlayerViewController.m
ios/Classes/MyPlayerViewController.m
+2
-2
MyPreviewController.h
ios/Classes/MyPreviewController.h
+20
-0
MyPreviewController.m
ios/Classes/MyPreviewController.m
+121
-0
ScanImagePlugn.dart
lib/ScanImagePlugn.dart
+4
-4
gengmei_flutter_plugin.dart
lib/gengmei_flutter_plugin.dart
+2
-2
No files found.
.idea/workspace.xml
View file @
f0733818
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"AndroidLogFilters"
>
<option
name=
"TOOL_WINDOW_LOG_LEVEL"
value=
"
error
"
/>
<option
name=
"TOOL_WINDOW_LOG_LEVEL"
value=
"
verbose
"
/>
<option
name=
"TOOL_WINDOW_CONFIGURED_FILTER"
value=
"Show only selected application"
/>
</component>
<component
name=
"ChangeListManager"
>
<list
default=
"true"
id=
"5be6bbb5-7d6e-4540-a24f-d2b3bf78b3ba"
name=
"Default Changelist"
comment=
""
>
<change
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/act/VideoActivity.kt"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/android/src/main/res/layout/video_act.xml"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/android/src/main/res/values/colors.xml"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/android/src/main/res/values/styles.xml"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/act/VideoActivity.kt"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/res/layout/video_act.xml"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/res/values/colors.xml"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/res/values/styles.xml"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/act/PreviewActivity.kt"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/act/view/PinchImageView.kt"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/android/src/main/res/layout/preview_act.xml"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/act/PreviewActivity.kt"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/act/view/PinchImageView.kt"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/res/layout/preview_act.xml"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/ios/Classes/MyPreviewController.h"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/ios/Classes/MyPreviewController.m"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/ios/Classes/MyPreviewController.h"
afterDir=
"false"
/>
<change
afterPath=
"$PROJECT_DIR$/ios/Classes/MyPreviewController.m"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/.idea/workspace.xml"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/.idea/workspace.xml"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/android/build.gradle"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/android/build.gradle"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/android/src/main/AndroidManifest.xml"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/android/src/main/AndroidManifest.xml"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/GengmeiFlutterPlugin.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/GengmeiFlutterPlugin.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/ImageRespository.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/ImageRespository.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/bean/MediaFile.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/bean/MediaFile.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ImageScanner.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ImageScanner.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ThumbUtil.java"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ThumbUtil.java"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/act/VideoActivity.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/act/VideoActivity.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/utils/MyUtil.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/android/src/main/java/com/example/gengmei_flutter_plugin/utils/MyUtil.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/.idea/workspace.xml"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/.idea/workspace.xml"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/build.gradle"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/build.gradle"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/AndroidManifest.xml"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/AndroidManifest.xml"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/GengmeiFlutterPlugin.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/GengmeiFlutterPlugin.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/ImageRespository.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/ImageRespository.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/bean/MediaFile.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/bean/MediaFile.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ImageScanner.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ImageScanner.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ThumbUtil.java"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ThumbUtil.java"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/act/VideoActivity.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/act/VideoActivity.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/utils/MyUtil.kt"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/android/src/main/java/com/example/gengmei_flutter_plugin/utils/MyUtil.kt"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/example/lib/AlbumModel/page/album/AlbumModel.dart"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/example/lib/AlbumModel/page/album/AlbumModel.dart"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/example/lib/AlbumModel/page/album/AlbumPage.dart"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/example/lib/AlbumModel/page/album/AlbumPage.dart"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/ios/Classes/GengmeiFlutterPlugin.m"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/ios/Classes/GengmeiFlutterPlugin.m"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/ios/Classes/MyPlayerViewController.m"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/ios/Classes/MyPlayerViewController.m"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/lib/ScanImagePlugn.dart"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/lib/ScanImagePlugn.dart"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/lib/gengmei_flutter_plugin.dart"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/ios/.symlinks/plugins/gengmei_flutter_plugin/lib/gengmei_flutter_plugin.dart"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumModel.dart"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumModel.dart"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumPage.dart"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumPage.dart"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/ios/Classes/GengmeiFlutterPlugin.m"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/ios/Classes/GengmeiFlutterPlugin.m"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/ios/Classes/MyPlayerViewController.m"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/ios/Classes/MyPlayerViewController.m"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/lib/ScanImagePlugn.dart"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/lib/ScanImagePlugn.dart"
afterDir=
"false"
/>
<change
beforePath=
"$PROJECT_DIR$/lib/gengmei_flutter_plugin.dart"
beforeDir=
"false"
afterPath=
"$PROJECT_DIR$/lib/gengmei_flutter_plugin.dart"
afterDir=
"false"
/>
</list>
<ignored
path=
"$PROJECT_DIR$/.dart_tool/"
/>
<ignored
path=
"$PROJECT_DIR$/.idea/"
/>
...
...
@@ -42,14 +62,14 @@
<component
name=
"DefaultGradleProjectSettings"
>
<option
name=
"isMigrated"
value=
"true"
/>
</component>
<component
name=
"ExecutionTargetManager"
SELECTED_TARGET=
"
AKC0218316000622
"
/>
<component
name=
"ExecutionTargetManager"
SELECTED_TARGET=
"
Pixel_2_API_25
"
/>
<component
name=
"FileEditorManager"
>
<leaf
SIDE_TABS_SIZE_LIMIT_KEY=
"300"
>
<file
pinned=
"false"
current-in-tab=
"false"
>
<entry
file=
"file://$PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumModel.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"
2045
"
>
<caret
line=
"3
78"
column=
"29"
selection-start-line=
"378"
selection-start-column=
"29"
selection-end-line=
"378"
selection-end-column=
"29
"
/>
<state
relative-caret-position=
"
8184
"
>
<caret
line=
"3
87"
column=
"36"
selection-start-line=
"387"
selection-start-column=
"36"
selection-end-line=
"387"
selection-end-column=
"36
"
/>
<folding>
<element
signature=
"e#46#66#0"
expanded=
"true"
/>
</folding>
...
...
@@ -57,14 +77,11 @@
</provider>
</entry>
</file>
<file
pinned=
"false"
current-in-tab=
"
tru
e"
>
<entry
file=
"file://$PROJECT_DIR$/example/lib/AlbumModel/page/preview/AlbumPreview
Page
.dart"
>
<file
pinned=
"false"
current-in-tab=
"
fals
e"
>
<entry
file=
"file://$PROJECT_DIR$/example/lib/AlbumModel/page/preview/AlbumPreview
Model
.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"253"
>
<caret
line=
"36"
column=
"30"
selection-start-line=
"36"
selection-start-column=
"30"
selection-end-line=
"36"
selection-end-column=
"30"
/>
<folding>
<element
signature=
"e#45#62#0"
expanded=
"true"
/>
</folding>
<state
relative-caret-position=
"242"
>
<caret
line=
"11"
selection-start-line=
"11"
selection-end-line=
"11"
/>
</state>
</provider>
</entry>
...
...
@@ -72,7 +89,7 @@
<file
pinned=
"false"
current-in-tab=
"false"
>
<entry
file=
"file://$USER_HOME$/Downloads/flutter/packages/flutter/lib/src/widgets/image.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"
-109
"
>
<state
relative-caret-position=
"
6556
"
>
<caret
line=
"326"
column=
"8"
selection-start-line=
"326"
selection-start-column=
"8"
selection-end-line=
"326"
selection-end-column=
"8"
/>
</state>
</provider>
...
...
@@ -81,7 +98,7 @@
<file
pinned=
"false"
current-in-tab=
"false"
>
<entry
file=
"file://$USER_HOME$/Downloads/flutter/bin/cache/dart-sdk/lib/async/zone.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"2
2
"
>
<state
relative-caret-position=
"2
4838
"
>
<caret
line=
"1131"
column=
"37"
selection-start-line=
"1131"
selection-start-column=
"37"
selection-end-line=
"1131"
selection-end-column=
"37"
/>
</state>
</provider>
...
...
@@ -90,7 +107,7 @@
<file
pinned=
"false"
current-in-tab=
"false"
>
<entry
file=
"file://$PROJECT_DIR$/example/lib/main.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"
242
"
>
<state
relative-caret-position=
"
3674
"
>
<caret
line=
"173"
column=
"47"
selection-start-line=
"173"
selection-start-column=
"47"
selection-end-line=
"173"
selection-end-column=
"47"
/>
<folding>
<element
signature=
"e#0#17#0"
expanded=
"true"
/>
...
...
@@ -102,7 +119,7 @@
<file
pinned=
"false"
current-in-tab=
"false"
>
<entry
file=
"file://$USER_HOME$/Downloads/flutter/bin/cache/pkg/sky_engine/lib/core/double.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"
135
"
>
<state
relative-caret-position=
"
594
"
>
<caret
line=
"29"
column=
"22"
selection-start-line=
"29"
selection-start-column=
"22"
selection-end-line=
"29"
selection-end-column=
"22"
/>
</state>
</provider>
...
...
@@ -111,8 +128,8 @@
<file
pinned=
"false"
current-in-tab=
"false"
>
<entry
file=
"file://$PROJECT_DIR$/lib/ScanImagePlugn.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"
210
"
>
<caret
line=
"
64"
column=
"6"
selection-start-line=
"64"
selection-start-column=
"6"
selection-end-line=
"64"
selection-end-column=
"6
"
/>
<state
relative-caret-position=
"
1078
"
>
<caret
line=
"
53"
column=
"20"
selection-start-line=
"53"
selection-start-column=
"20"
selection-end-line=
"53"
selection-end-column=
"20
"
/>
<folding>
<element
signature=
"e#45#86#0"
expanded=
"true"
/>
</folding>
...
...
@@ -121,10 +138,19 @@
</entry>
</file>
<file
pinned=
"false"
current-in-tab=
"false"
>
<entry
file=
"file://$USER_HOME$/Downloads/flutter/packages/flutter/lib/src/services/platform_channel.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"6534"
>
<caret
line=
"306"
column=
"12"
selection-start-line=
"306"
selection-start-column=
"12"
selection-end-line=
"306"
selection-end-column=
"12"
/>
</state>
</provider>
</entry>
</file>
<file
pinned=
"false"
current-in-tab=
"true"
>
<entry
file=
"file://$PROJECT_DIR$/lib/gengmei_flutter_plugin.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"3
63
"
>
<caret
line=
"3
4"
column=
"41"
selection-start-line=
"34"
selection-start-column=
"41"
selection-end-line=
"34"
selection-end-column=
"41
"
/>
<state
relative-caret-position=
"3
57
"
>
<caret
line=
"3
2"
selection-start-line=
"32"
selection-end-line=
"32
"
/>
<folding>
<element
signature=
"e#0#20#0"
expanded=
"true"
/>
</folding>
...
...
@@ -135,7 +161,7 @@
<file
pinned=
"false"
current-in-tab=
"false"
>
<entry
file=
"file://$PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumPage.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"3
55
"
>
<state
relative-caret-position=
"3
916
"
>
<caret
line=
"187"
column=
"18"
selection-start-line=
"187"
selection-start-column=
"18"
selection-end-line=
"187"
selection-end-column=
"18"
/>
<folding>
<element
signature=
"e#45#62#0"
expanded=
"true"
/>
...
...
@@ -144,15 +170,6 @@
</provider>
</entry>
</file>
<file
pinned=
"false"
current-in-tab=
"false"
>
<entry
file=
"file://$USER_HOME$/Downloads/flutter/packages/flutter/lib/src/widgets/scroll_view.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"135"
>
<caret
line=
"929"
column=
"22"
selection-start-line=
"929"
selection-start-column=
"22"
selection-end-line=
"929"
selection-end-column=
"22"
/>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component
name=
"FileTemplateManagerImpl"
>
...
...
@@ -215,16 +232,16 @@
<option
value=
"$PROJECT_DIR$/example/lib/AlbumModel/repository/AlbumRepository.dart"
/>
<option
value=
"$PROJECT_DIR$/example/lib/AlbumModel/page/preview/AlbumPreviewModel.dart"
/>
<option
value=
"$PROJECT_DIR$/example/lib/AlbumModel/page/preview/AlbumPreviewPage.dart"
/>
<option
value=
"$PROJECT_DIR$/lib/gengmei_flutter_plugin.dart"
/>
<option
value=
"$PROJECT_DIR$/lib/ScanImagePlugn.dart"
/>
<option
value=
"$PROJECT_DIR$/example/lib/main.dart"
/>
<option
value=
"$PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumPage.dart"
/>
<option
value=
"$PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumModel.dart"
/>
<option
value=
"$PROJECT_DIR$/lib/ScanImagePlugn.dart"
/>
<option
value=
"$PROJECT_DIR$/lib/gengmei_flutter_plugin.dart"
/>
</list>
</option>
</component>
<component
name=
"ProjectFrameBounds"
>
<option
name=
"x"
value=
"
30
"
/>
<option
name=
"x"
value=
"
29
"
/>
<option
name=
"y"
value=
"23"
/>
<option
name=
"width"
value=
"1440"
/>
<option
name=
"height"
value=
"811"
/>
...
...
@@ -235,9 +252,8 @@
<foldersAlwaysOnTop
value=
"true"
/>
</navigator>
<panes>
<pane
id=
"Scope"
/>
<pane
id=
"PackagesPane"
/>
<pane
id=
"
AndroidView
"
/>
<pane
id=
"
Scope
"
/>
<pane
id=
"ProjectPane"
>
<subPane>
<expand>
...
...
@@ -329,6 +345,7 @@
<select
/>
</subPane>
</pane>
<pane
id=
"AndroidView"
/>
</panes>
</component>
<component
name=
"PropertiesComponent"
>
...
...
@@ -375,10 +392,10 @@
<servers
/>
</component>
<component
name=
"ToolWindowManager"
>
<frame
x=
"
30
"
y=
"23"
width=
"1440"
height=
"811"
extended-state=
"0"
/>
<frame
x=
"
29
"
y=
"23"
width=
"1440"
height=
"811"
extended-state=
"0"
/>
<editor
active=
"true"
/>
<layout>
<window_info
active=
"true"
content_ui=
"combo"
id=
"Project"
order=
"0"
visible=
"true"
weight=
"0.19742489"
/>
<window_info
content_ui=
"combo"
id=
"Project"
order=
"0"
visible=
"true"
weight=
"0.19742489"
/>
<window_info
id=
"Captures"
order=
"1"
side_tool=
"true"
/>
<window_info
id=
"Structure"
order=
"2"
side_tool=
"true"
/>
<window_info
id=
"Image Layers"
order=
"3"
/>
...
...
@@ -388,12 +405,12 @@
<window_info
id=
"Capture Tool"
order=
"7"
/>
<window_info
id=
"Favorites"
order=
"8"
side_tool=
"true"
/>
<window_info
anchor=
"bottom"
id=
"Dart Analysis"
order=
"0"
weight=
"0.32963988"
/>
<window_info
anchor=
"bottom"
id=
"Run"
order=
"1"
visible=
"true"
weight=
"0.37413073
"
/>
<window_info
anchor=
"bottom"
id=
"Run"
order=
"1"
weight=
"0.37361112
"
/>
<window_info
anchor=
"bottom"
id=
"TODO"
order=
"2"
/>
<window_info
anchor=
"bottom"
id=
"Android Profiler"
order=
"3"
show_stripe_button=
"false"
/>
<window_info
anchor=
"bottom"
id=
"Logcat"
order=
"4"
weight=
"0.
42420027
"
/>
<window_info
anchor=
"bottom"
id=
"Logcat"
order=
"4"
weight=
"0.
68194443
"
/>
<window_info
anchor=
"bottom"
id=
"Debug"
order=
"5"
/>
<window_info
anchor=
"bottom"
id=
"Terminal"
order=
"6"
weight=
"0.
39499304
"
/>
<window_info
anchor=
"bottom"
id=
"Terminal"
order=
"6"
weight=
"0.
22361112
"
/>
<window_info
anchor=
"bottom"
id=
"Event Log"
order=
"7"
side_tool=
"true"
/>
<window_info
anchor=
"bottom"
id=
"Flutter Performance"
order=
"8"
side_tool=
"true"
/>
<window_info
anchor=
"bottom"
id=
"Version Control"
order=
"9"
/>
...
...
@@ -572,13 +589,6 @@
<entry
file=
"file://$PROJECT_DIR$/ios/Classes/Image/ResultManager.m"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
/>
</entry>
<entry
file=
"file://$USER_HOME$/Downloads/flutter/packages/flutter/lib/src/services/platform_channel.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"121"
>
<caret
line=
"342"
column=
"20"
selection-start-line=
"342"
selection-start-column=
"20"
selection-end-line=
"342"
selection-end-column=
"20"
/>
</state>
</provider>
</entry>
<entry
file=
"file://$PROJECT_DIR$/example/lib/AlbumModel/repository/AlbumRepository.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"251"
>
...
...
@@ -589,13 +599,6 @@
</state>
</provider>
</entry>
<entry
file=
"file://$PROJECT_DIR$/example/lib/AlbumModel/page/preview/AlbumPreviewModel.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"242"
>
<caret
line=
"11"
selection-start-line=
"11"
selection-end-line=
"11"
/>
</state>
</provider>
</entry>
<entry
file=
"file://$PROJECT_DIR$/lib/SharedPlugin.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"330"
>
...
...
@@ -632,86 +635,100 @@
</state>
</provider>
</entry>
<entry
file=
"file://$
USER_HOME$/Downloads/flutter/bin/cache/pkg/sky_engine/lib/core/doubl
e.dart"
>
<entry
file=
"file://$
PROJECT_DIR$/example/lib/AlbumModel/page/preview/AlbumPreviewPag
e.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"135"
>
<caret
line=
"29"
column=
"22"
selection-start-line=
"29"
selection-start-column=
"22"
selection-end-line=
"29"
selection-end-column=
"22"
/>
<state
relative-caret-position=
"638"
>
<caret
line=
"36"
column=
"30"
selection-start-line=
"36"
selection-start-column=
"30"
selection-end-line=
"36"
selection-end-column=
"30"
/>
<folding>
<element
signature=
"e#45#62#0"
expanded=
"true"
/>
</folding>
</state>
</provider>
</entry>
<entry
file=
"file://$PROJECT_DIR$/example/lib/
main
.dart"
>
<entry
file=
"file://$PROJECT_DIR$/example/lib/
AlbumModel/page/album/AlbumModel
.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"
242
"
>
<caret
line=
"
173"
column=
"47"
selection-start-line=
"173"
selection-start-column=
"47"
selection-end-line=
"173"
selection-end-column=
"47
"
/>
<state
relative-caret-position=
"
8184
"
>
<caret
line=
"
387"
column=
"36"
selection-start-line=
"387"
selection-start-column=
"36"
selection-end-line=
"387"
selection-end-column=
"36
"
/>
<folding>
<element
signature=
"e#
0#17
#0"
expanded=
"true"
/>
<element
signature=
"e#
46#66
#0"
expanded=
"true"
/>
</folding>
</state>
</provider>
</entry>
<entry
file=
"file://$PROJECT_DIR$/
lib/gengmei_flutter_plugin
.dart"
>
<entry
file=
"file://$PROJECT_DIR$/
example/lib/AlbumModel/page/preview/AlbumPreviewModel
.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"363"
>
<caret
line=
"34"
column=
"41"
selection-start-line=
"34"
selection-start-column=
"41"
selection-end-line=
"34"
selection-end-column=
"41"
/>
<folding>
<element
signature=
"e#0#20#0"
expanded=
"true"
/>
</folding>
<state
relative-caret-position=
"242"
>
<caret
line=
"11"
selection-start-line=
"11"
selection-end-line=
"11"
/>
</state>
</provider>
</entry>
<entry
file=
"file://$
PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumP
age.dart"
>
<entry
file=
"file://$
USER_HOME$/Downloads/flutter/packages/flutter/lib/src/widgets/im
age.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"355"
>
<caret
line=
"187"
column=
"18"
selection-start-line=
"187"
selection-start-column=
"18"
selection-end-line=
"187"
selection-end-column=
"18"
/>
<folding>
<element
signature=
"e#45#62#0"
expanded=
"true"
/>
</folding>
<state
relative-caret-position=
"6556"
>
<caret
line=
"326"
column=
"8"
selection-start-line=
"326"
selection-start-column=
"8"
selection-end-line=
"326"
selection-end-column=
"8"
/>
</state>
</provider>
</entry>
<entry
file=
"file://$USER_HOME$/Downloads/flutter/bin/cache/dart-sdk/lib/async/zone.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"2
2
"
>
<state
relative-caret-position=
"2
4838
"
>
<caret
line=
"1131"
column=
"37"
selection-start-line=
"1131"
selection-start-column=
"37"
selection-end-line=
"1131"
selection-end-column=
"37"
/>
</state>
</provider>
</entry>
<entry
file=
"file://$PROJECT_DIR$/
lib/ScanImagePlug
n.dart"
>
<entry
file=
"file://$PROJECT_DIR$/
example/lib/mai
n.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"
210
"
>
<caret
line=
"
64"
column=
"6"
selection-start-line=
"64"
selection-start-column=
"6"
selection-end-line=
"64"
selection-end-column=
"6
"
/>
<state
relative-caret-position=
"
3674
"
>
<caret
line=
"
173"
column=
"47"
selection-start-line=
"173"
selection-start-column=
"47"
selection-end-line=
"173"
selection-end-column=
"47
"
/>
<folding>
<element
signature=
"e#
45#86
#0"
expanded=
"true"
/>
<element
signature=
"e#
0#17
#0"
expanded=
"true"
/>
</folding>
</state>
</provider>
</entry>
<entry
file=
"file://$
PROJECT_DIR$/example/lib/AlbumModel/page/album/AlbumModel
.dart"
>
<entry
file=
"file://$
USER_HOME$/Downloads/flutter/bin/cache/pkg/sky_engine/lib/core/double
.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"2045"
>
<caret
line=
"378"
column=
"29"
selection-start-line=
"378"
selection-start-column=
"29"
selection-end-line=
"378"
selection-end-column=
"29"
/>
<state
relative-caret-position=
"594"
>
<caret
line=
"29"
column=
"22"
selection-start-line=
"29"
selection-start-column=
"22"
selection-end-line=
"29"
selection-end-column=
"22"
/>
</state>
</provider>
</entry>
<entry
file=
"file://$PROJECT_DIR$/lib/ScanImagePlugn.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"1078"
>
<caret
line=
"53"
column=
"20"
selection-start-line=
"53"
selection-start-column=
"20"
selection-end-line=
"53"
selection-end-column=
"20"
/>
<folding>
<element
signature=
"e#4
6#6
6#0"
expanded=
"true"
/>
<element
signature=
"e#4
5#8
6#0"
expanded=
"true"
/>
</folding>
</state>
</provider>
</entry>
<entry
file=
"file://$USER_HOME$/Downloads/flutter/packages/flutter/lib/src/
widgets/image
.dart"
>
<entry
file=
"file://$USER_HOME$/Downloads/flutter/packages/flutter/lib/src/
services/platform_channel
.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"
-109
"
>
<caret
line=
"3
26"
column=
"8"
selection-start-line=
"326"
selection-start-column=
"8"
selection-end-line=
"326"
selection-end-column=
"8
"
/>
<state
relative-caret-position=
"
6534
"
>
<caret
line=
"3
06"
column=
"12"
selection-start-line=
"306"
selection-start-column=
"12"
selection-end-line=
"306"
selection-end-column=
"12
"
/>
</state>
</provider>
</entry>
<entry
file=
"file://$PROJECT_DIR$/example/lib/AlbumModel/page/
preview/AlbumPreview
Page.dart"
>
<entry
file=
"file://$PROJECT_DIR$/example/lib/AlbumModel/page/
album/Album
Page.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"
253
"
>
<caret
line=
"
36"
column=
"30"
selection-start-line=
"36"
selection-start-column=
"30"
selection-end-line=
"36"
selection-end-column=
"30
"
/>
<state
relative-caret-position=
"
3916
"
>
<caret
line=
"
187"
column=
"18"
selection-start-line=
"187"
selection-start-column=
"18"
selection-end-line=
"187"
selection-end-column=
"18
"
/>
<folding>
<element
signature=
"e#45#62#0"
expanded=
"true"
/>
</folding>
</state>
</provider>
</entry>
<entry
file=
"file://$PROJECT_DIR$/lib/gengmei_flutter_plugin.dart"
>
<provider
selected=
"true"
editor-type-id=
"text-editor"
>
<state
relative-caret-position=
"357"
>
<caret
line=
"32"
selection-start-line=
"32"
selection-end-line=
"32"
/>
<folding>
<element
signature=
"e#0#20#0"
expanded=
"true"
/>
</folding>
</state>
</provider>
</entry>
</component>
</project>
\ No newline at end of file
android/build.gradle
View file @
f0733818
...
...
@@ -50,4 +50,5 @@ dependencies {
implementation
"io.reactivex.rxjava2:rxjava:$rxJavaVersion"
implementation
"io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
implementation
'com.android.support:appcompat-v7:28.0.0'
implementation
'com.github.bumptech.glide:glide:4.8.0'
}
android/src/main/AndroidManifest.xml
View file @
f0733818
...
...
@@ -3,6 +3,7 @@
<application>
<activity
android:name=
".act.VideoActivity"
/>
<activity
android:name=
".act.PreviewActivity"
/>
</application>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
...
...
android/src/main/java/com/example/gengmei_flutter_plugin/GengmeiFlutterPlugin.kt
View file @
f0733818
...
...
@@ -14,6 +14,7 @@ import android.provider.MediaStore
import
android.support.v4.content.FileProvider
import
android.util.Log
import
android.widget.Toast
import
com.example.gengmei_flutter_plugin.act.PreviewActivity
import
com.example.gengmei_flutter_plugin.act.VideoActivity
import
com.example.gengmei_flutter_plugin.result.ResultManager
import
com.example.gengmei_flutter_plugin.sharedPrefernces.SharedManager
...
...
@@ -66,6 +67,7 @@ class GengmeiFlutterPlugin : MethodCallHandler {
const
val
GET_BOOLEAN_SHARED
=
"GET_BOOLEAN_SHARED"
const
val
GET_STRINGLIST_SHARED
=
"GET_STRINGLIST_SHARED"
const
val
ALBUM_PLAY_VIDEO
=
"play_album_video"
const
val
PREVIEW_IMAGE
=
"PREVIEW_IMAGE"
const
val
CLEAR_SHARE
=
"CLEAR_SHARE"
const
val
PREMISSION
=
10090
...
...
@@ -248,6 +250,24 @@ class GengmeiFlutterPlugin : MethodCallHandler {
resign
.
activity
().
startActivity
(
intent
)
}
}
PREVIEW_IMAGE
->
{
val
result
=
resultKey
;
val
path
=
call
.
arguments
as
String
?
path
?.
run
{
val
intent
=
Intent
(
resign
.
activity
(),
PreviewActivity
::
class
.
java
)
intent
.
putExtra
(
"PATH"
,
this
)
resign
.
activity
().
startActivity
(
intent
)
ResultManager
.
getInstance
().
resultSuccess
(
result
,
true
)
// ImageRespository.getInstance().getPreviewImg(resign.context().applicationContext,this, 800.0f).subscribe({
// val resutlMap=HashMap<String,String>(5);
// resutlMap.put("path",path)
// resutlMap.put("realImagePath",it)
// ResultManager.getInstance().resultSuccess(result, resutlMap);
// }, {
// DebugUtil.printStackTrace(it);
// }).addTo(disposable);
}
}
else
->
result
.
notImplemented
()
}
}
...
...
@@ -357,7 +377,7 @@ class GengmeiFlutterPlugin : MethodCallHandler {
// 最后通知图库更新
resign
.
context
().
applicationContext
.
sendBroadcast
(
Intent
(
Intent
.
ACTION_MEDIA_SCANNER_SCAN_FILE
,
Uri
.
fromFile
(
nativeImage
)))
ImageRespository
.
getInstance
().
recordImageListMap
.
add
(
map
)
ImageRespository
.
getInstance
().
recordImageListMap
.
add
(
0
,
map
)
ResultManager
.
getInstance
().
resultSuccess
(
gotoNativeCameraKey
,
map
);
},
{
DebugUtil
.
printStackTrace
(
it
)
...
...
android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/ImageRespository.kt
View file @
f0733818
...
...
@@ -45,7 +45,7 @@ class ImageRespository {
fun
scareImg
(
realPath
:
String
):
Observable
<
Pair
<
String
,
String
>>
{
return
Observable
.
create
(
ObservableOnSubscribe
<
Pair
<
String
,
String
>>
{
val
pair
=
Pair
<
String
,
String
>(
realPath
,
MyUtil
.
scareImg
(
realPath
,
200f
,
fileDir
+
"/"
+
getFileName
(
realPath
)
!!
+
".png"
,
75
))
val
pair
=
Pair
<
String
,
String
>(
realPath
,
MyUtil
.
scareImg
(
realPath
,
200f
,
fileDir
+
"/"
+
getFileName
(
realPath
)
!!
+
".png"
,
75
,
0
))
it
.
onNext
(
pair
)
}).
subscribeOn
(
Schedulers
.
io
()).
observeOn
(
AndroidSchedulers
.
mainThread
());
}
...
...
@@ -72,7 +72,7 @@ class ImageRespository {
val
it2
=
iterator
.
next
()
for
(
item
in
images
)
{
if
(
item
.
id
==
it2
.
id
)
{
if
(
item
.
path
!=
null
&&
!
TextUtils
.
isEmpty
(
item
.
path
)
&&
File
(
item
.
path
).
exists
()
)
{
if
(
!
TextUtils
.
isEmpty
(
item
.
path
)
&&
File
(
item
.
path
).
exists
()
&&
it2
.
degree
==
0
)
{
it2
.
path
=
item
.
path
}
it2
.
isVideo
=
false
...
...
@@ -88,7 +88,7 @@ class ImageRespository {
val
it1
=
iteratorVideo
.
next
()
for
(
item
in
videos
)
{
if
(
item
.
id
==
it1
.
id
)
{
if
(
item
.
path
!=
null
&&
!
TextUtils
.
isEmpty
(
item
.
path
)
&&
File
(
item
.
path
).
exists
())
{
if
(!
TextUtils
.
isEmpty
(
item
.
path
)
&&
File
(
item
.
path
).
exists
())
{
it1
.
path
=
item
.
path
}
it1
.
isVideo
=
true
...
...
@@ -109,40 +109,52 @@ class ImageRespository {
fun
savePreviewImg
(
context
:
Context
,
listener
:
savePreviewListener
)
{
val
start
=
System
.
currentTimeMillis
();
val
needSize
=
recordImageListMap
.
size
;
var
currentSize
=
0
;
var
currentSize
=
0
var
letSize
=
10
var
noPathSize
=
0
;
for
(
map
in
recordImageListMap
)
{
// recordImageListMap.forEach {
if
(
map
[
"realPath"
]
==
null
)
{
continue
}
var
noPathSize
=
0
globalThreadPool
.
execute
{
val
it
=
map
Log
.
e
(
"lsy"
,
"START THREADMAME "
+
Thread
.
currentThread
().
name
)
for
(
index
in
0
..
(
recordImageListMap
.
size
-
1
))
{
val
it
=
recordImageListMap
[
index
]
// if (it["realPath"] == null) {
// continue
// }
val
any
=
it
[
"path"
]
val
realPath
=
it
[
"realPath"
]
as
String
if
(
any
==
null
)
{
Log
.
e
(
"lsy"
,
" THREADMAME "
+
Thread
.
currentThread
().
name
)
if
(
any
!=
null
&&
!
TextUtils
.
isEmpty
(
any
as
String
))
{
synchronized
(
this
)
{
currentSize
++
Log
.
e
(
"lsy"
,
"${noPathSize} ${currentSize} ${needSize}"
)
if
(
currentSize
==
needSize
)
{
Log
.
e
(
"lsy"
,
" 压缩完成 耗时:${System.currentTimeMillis() - start}"
)
//FINISH
listener
.
onSuccess
(
toMapSync
(
context
,
recordImageListMap
))
}
}
}
else
{
val
tempFilePngString
=
fileDir
+
"/"
+
getFileName
(
realPath
)
!!
+
".png"
;
val
tempFilePngExists
=
File
(
tempFilePngString
).
exists
()
// val tempFileJpgString = fileDir + "/" + getFileName(realPath)!! + ".jpg";
// val tempFileJpgExists = File(tempFileJpgString).exists()
// val tempFileJpegString = fileDir + "/" + getFileName(realPath)!! + ".jpeg"
// val tempFileJpegExists = File(tempFileJpegString).exists()
if
(
tempFilePngExists
)
{
it
[
"path"
]
=
tempFilePngString
;
synchronized
(
this
)
{
currentSize
++;
Log
.
e
(
"lsy"
,
"HAVE PATH ${noPathSize} ${currentSize} ${needSize}"
)
currentSize
++
recordImageListMap
[
index
][
"path"
]
=
tempFilePngString
Log
.
e
(
"lsy"
,
"${noPathSize} ${currentSize} ${needSize}"
)
if
(
currentSize
==
needSize
)
{
Log
.
e
(
"lsy"
,
" 压缩完成 耗时:${System.currentTimeMillis() - start}"
)
//FINISH
listener
.
onSuccess
(
toMapSync
(
context
,
recordImageListMap
))
}
}
return
@execute
}
else
if
(
it
[
"isVideo"
]
==
"T"
)
{
it
[
"path"
]
=
MyUtil
.
saveVideoImg
(
"${fileDir}/${getFileName(realPath)!!}.png"
,
}
else
{
globalThreadPool
.
execute
{
Log
.
e
(
"lsy"
,
" THREADMAME "
+
Thread
.
currentThread
().
name
)
var
path
=
""
var
degree
=
0
if
(
it
[
"degree"
]
!=
null
)
{
degree
=
it
[
"degree"
]
as
Int
;
}
if
(
it
[
"isVideo"
]
==
"T"
)
{
path
=
MyUtil
.
saveVideoImg
(
"${fileDir}/${getFileName(realPath)!!}.png"
,
realPath
,
MediaStore
.
Images
.
Thumbnails
.
MICRO_KIND
,
220
,
220
)
}
else
{
val
time
=
System
.
currentTimeMillis
();
...
...
@@ -150,13 +162,15 @@ class ImageRespository {
// .setName(getFileName(realPath)!!)
// .get(realPath);
// it["path"] = get.absolutePath;
it
[
"path"
]
=
MyUtil
.
scareImg
(
realPath
,
200f
,
tempFilePngString
,
75
)
path
=
MyUtil
.
scareImg
(
realPath
,
200f
,
tempFilePngString
,
75
,
degree
)
Log
.
e
(
"lsy"
,
"TIMM${System.currentTimeMillis() - time}"
)
}
//
getImageCacheDir(context, Luban.DEFAULT_DISK_CACHE_DIR)!!.absolutePath
//
getImageCacheDir(context, Luban.DEFAULT_DISK_CACHE_DIR)!!.absolutePath
synchronized
(
this
)
{
currentSize
++;
noPathSize
++;
currentSize
++
noPathSize
++
recordImageListMap
[
index
][
"path"
]
=
path
Log
.
e
(
"lsy"
,
"${noPathSize} ${currentSize} ${needSize}"
)
if
(
currentSize
==
needSize
)
{
Log
.
e
(
"lsy"
,
" 压缩完成 耗时:${System.currentTimeMillis() - start}"
)
...
...
@@ -169,14 +183,6 @@ class ImageRespository {
}
}
}
}
else
{
synchronized
(
this
)
{
currentSize
++;
Log
.
e
(
"lsy"
,
"HAVE PATH ${noPathSize} ${currentSize} ${needSize}"
)
if
(
currentSize
==
needSize
)
{
Log
.
e
(
"lsy"
,
" 压缩完成 耗时:${System.currentTimeMillis() - start}"
)
//FINISH
listener
.
onSuccess
(
toMapSync
(
context
,
recordImageListMap
))
}
}
}
...
...
@@ -241,6 +247,9 @@ class ImageRespository {
it
.
path
?.
run
{
itemMap
.
put
(
"path"
,
this
)
}
it
.
degree
?.
run
{
itemMap
.
put
(
"degree"
,
this
)
}
it
.
size
?.
run
{
itemMap
.
put
(
"size"
,
this
)
}
...
...
@@ -284,12 +293,16 @@ class ImageRespository {
if
(
png
.
exists
())
{
it
[
"path"
]
=
png
.
absolutePath
}
else
{
var
degree
=
0
if
(
it
[
"degree"
]
!=
null
)
{
degree
=
it
[
"degree"
]
as
Int
}
// val get = Luban.with(context).setTargetDir(
// fileDir
// )
// .setName(getFileName(realPath)!!)
// .get(realPath);
it
[
"path"
]
=
MyUtil
.
scareImg
(
realPath
,
200f
,
fileDir
+
"/"
+
getFileName
(
realPath
)
!!
+
".png"
,
75
)
it
[
"path"
]
=
MyUtil
.
scareImg
(
realPath
,
200f
,
fileDir
+
"/"
+
getFileName
(
realPath
)
!!
+
".png"
,
75
,
degree
)
// Log.e("lsy", "封面照片 ${get.absolutePath}");
}
}
...
...
@@ -320,6 +333,20 @@ class ImageRespository {
return
finalList
;
}
fun
getPreviewImg
(
context
:
Context
,
realPath
:
String
,
scareSize
:
Float
):
Observable
<
String
>
{
val
copyPath
=
fileDir
+
"/"
+
getFileName
(
realPath
)
!!
+
"_preview.png"
if
(
File
(
copyPath
).
exists
())
{
return
Observable
.
just
(
copyPath
)
}
return
Observable
.
create
(
ObservableOnSubscribe
<
String
>
{
// val path = Luban.with(context).setTargetDir(fileDir).quality(100)
// .setName(getFileName(realPath)!! + "_preview.png")
// .get(realPath);
val
path
=
MyUtil
.
scareImg
(
realPath
,
scareSize
,
copyPath
,
90
,
0
);
it
.
onNext
(
path
)
}).
subscribeOn
(
Schedulers
.
io
()).
observeOn
(
AndroidSchedulers
.
mainThread
());
}
companion
object
{
private
var
instance
:
ImageRespository
?
=
null
const
val
TAG
:
String
=
"Image_Picker"
...
...
android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/bean/MediaFile.kt
View file @
f0733818
...
...
@@ -19,5 +19,6 @@ data class MediaFile(
var
realPath
:
String
?
=
null
,
var
isVideo
:
Boolean
?
=
null
,
var
width
:
Int
?
=
0
,
var
height
:
Int
?
=
0
var
height
:
Int
?
=
0
,
var
degree
:
Int
?
=
0
)
android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ImageScanner.kt
View file @
f0733818
...
...
@@ -30,7 +30,9 @@ class ImageScanner(var context: Context) : AbsMediaScanner<MediaFile>(context) {
MediaStore
.
Images
.
Media
.
BUCKET_DISPLAY_NAME
,
MediaStore
.
Images
.
Media
.
DATE_TAKEN
,
MediaStore
.
Images
.
Media
.
SIZE
,
MediaStore
.
Audio
.
Media
.
_ID
)
MediaStore
.
Audio
.
Media
.
_ID
,
MediaStore
.
Images
.
Media
.
ORIENTATION
)
override
val
selection
:
String
get
()
=
...
...
@@ -63,7 +65,8 @@ class ImageScanner(var context: Context) : AbsMediaScanner<MediaFile>(context) {
val
folderName
=
cursor
.
getString
(
1
)
val
dateToken
=
cursor
.
getLong
(
2
)
val
size
=
cursor
.
getLong
(
3
)
val
id
=
cursor
.
getInt
(
4
);
val
id
=
cursor
.
getInt
(
4
)
val
degree
=
cursor
.
getInt
(
5
)
val
mediaFile
=
MediaFile
()
mediaFile
.
id
=
id
// mediaFile.mime = mime
...
...
@@ -73,6 +76,7 @@ class ImageScanner(var context: Context) : AbsMediaScanner<MediaFile>(context) {
mediaFile
.
size
=
size
mediaFile
.
realPath
=
path
mediaFile
.
isVideo
=
false
mediaFile
.
degree
=
degree
// val dir = Environment.getExternalStorageDirectory().absolutePath + "/GMAlbum/.album";
//// val dir = getImageCacheDir(context, Luban.DEFAULT_DISK_CACHE_DIR)!!.absolutePath;
// val tempFilePngString = dir + "/" + getFileName(path)!! + ".png";
...
...
android/src/main/java/com/example/gengmei_flutter_plugin/ImagePlugin/repository/local/ThumbUtil.java
View file @
f0733818
...
...
@@ -3,11 +3,13 @@ package com.example.gengmei_flutter_plugin.ImagePlugin.repository.local;
import
android.content.ContentResolver
;
import
android.content.Context
;
import
android.database.Cursor
;
import
android.media.ExifInterface
;
import
android.provider.MediaStore
;
import
android.util.Log
;
import
com.example.myimagepicker.bean.MediaFile
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
...
...
android/src/main/java/com/example/gengmei_flutter_plugin/act/PreviewActivity.kt
0 → 100644
View file @
f0733818
package
com.example.gengmei_flutter_plugin.act
import
android.os.Bundle
import
android.support.v7.app.AppCompatActivity
import
android.widget.ImageView
import
android.widget.LinearLayout
import
com.bumptech.glide.Glide
import
com.example.gengmei_flutter_plugin.R
import
com.example.gengmei_flutter_plugin.act.view.PinchImageView
import
com.example.gengmei_flutter_plugin.utils.MyUtil
/**
* @author lsy
* @date 2019-11-02
*/
class
PreviewActivity
:
AppCompatActivity
()
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
setTheme
(
R
.
style
.
FLUTTERAPPTHEMEM
)
MyUtil
.
setTransparent
(
this
)
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
preview_act
)
findViewById
<
LinearLayout
>(
R
.
id
.
main
).
setBackgroundColor
(
0
xff000000
.
toInt
())
val
path
=
intent
.
getStringExtra
(
"PATH"
)
if
(
path
==
null
){
finish
()
}
else
{
Glide
.
with
(
this
).
load
(
path
).
into
(
findViewById
<
PinchImageView
>(
R
.
id
.
image
))
}
}
}
\ No newline at end of file
android/src/main/java/com/example/gengmei_flutter_plugin/act/VideoActivity.kt
View file @
f0733818
...
...
@@ -16,6 +16,7 @@ import com.example.gengmei_flutter_plugin.utils.MyUtil
class
VideoActivity
:
AppCompatActivity
()
{
lateinit
var
videoView
:
VideoView
var
pathIsNull
=
false
;
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
setTheme
(
R
.
style
.
FLUTTERAPPTHEMEM
)
...
...
@@ -25,15 +26,22 @@ class VideoActivity : AppCompatActivity() {
findViewById
<
LinearLayout
>(
R
.
id
.
main
).
setBackgroundColor
(
0
xff000000
.
toInt
());
videoView
=
findViewById
(
R
.
id
.
video
)
val
path
=
intent
.
getStringExtra
(
"PATH"
)
if
(
path
==
null
)
{
pathIsNull
=
true
finish
()
}
else
{
videoView
.
setVideoPath
(
path
);
val
mediaController
=
MediaController
(
this
);
videoView
.
setMediaController
(
mediaController
);
videoView
.
requestFocus
();
}
}
override
fun
onResume
()
{
super
.
onResume
()
if
(!
pathIsNull
)
{
videoView
.
start
()
}
}
}
\ No newline at end of file
android/src/main/java/com/example/gengmei_flutter_plugin/act/view/PinchImageView.kt
0 → 100755
View file @
f0733818
package
com.example.gengmei_flutter_plugin.act.view
import
android.animation.ValueAnimator
import
android.content.Context
import
android.graphics.*
import
android.support.v7.widget.AppCompatImageView
import
android.util.AttributeSet
import
android.util.Log
import
android.view.GestureDetector
import
android.view.MotionEvent
import
java.util.*
/**
* 手势缩放ImageView
* Create by: chenWei.li
* Date: 2018/10/7
* Time: 下午5:29
* Email: lichenwei.me@foxmail.com
*/
class
PinchImageView
:
AppCompatImageView
{
////////////////////////////////监听器////////////////////////////////
/**
* 外界点击事件
*
* @see .setOnClickListener
*/
private
var
mOnClickListener
:
OnClickListener
?
=
null
/**
* 外界长按事件
*
* @see .setOnLongClickListener
*/
private
var
mOnLongClickListener
:
OnLongClickListener
?
=
null
/**
* 外层变换矩阵,如果是单位矩阵,那么图片是fit center状态
*
* @see .getOuterMatrix
* @see .outerMatrixTo
*/
val
mOuterMatrix
=
Matrix
()
val
mTranMatrix
=
Matrix
()
/**
* 矩形遮罩
*
* @see .getMask
* @see .zoomMaskTo
*/
var
mMask
:
RectF
?
=
null
/**
* 当前手势状态
*
* @see .getPinchMode
* @see .PINCH_MODE_FREE
*
* @see .PINCH_MODE_SCROLL
*
* @see .PINCH_MODE_SCALE
*/
/**
* 获取当前手势状态
*
* @see .PINCH_MODE_FREE
*
* @see .PINCH_MODE_SCROLL
*
* @see .PINCH_MODE_SCALE
*/
var
pinchMode
=
PINCH_MODE_FREE
private
set
/**
* 获取当前设置的mask
*
* @return 返回当前的mask对象副本, 如果当前没有设置mask则返回null
*/
val
mask
:
RectF
?
get
()
=
if
(
mMask
!=
null
)
{
RectF
(
mMask
)
}
else
{
null
}
/**
* 所有OuterMatrixChangedListener监听列表
*
* @see .addOuterMatrixChangedListener
* @see .removeOuterMatrixChangedListener
*/
private
var
mOuterMatrixChangedListeners
:
MutableList
<
OuterMatrixChangedListener
>?
=
null
/**
* 当mOuterMatrixChangedListeners被锁定不允许修改时,临时将修改写到这个副本中
*
* @see .mOuterMatrixChangedListeners
*/
private
var
mOuterMatrixChangedListenersCopy
:
MutableList
<
OuterMatrixChangedListener
>?
=
null
/**
* mOuterMatrixChangedListeners的修改锁定
*
*
* 当进入dispatchOuterMatrixChanged方法时,被加1,退出前被减1
*
* @see .dispatchOuterMatrixChanged
* @see .addOuterMatrixChangedListener
* @see .removeOuterMatrixChangedListener
*/
private
var
mDispatchOuterMatrixChangedLock
:
Int
=
0
////////////////////////////////用于重载定制////////////////////////////////
/**
* 获取图片最大可放大的比例
*
*
* 如果放大大于这个比例则不被允许.
* 在双手缩放过程中如果图片放大比例大于这个值,手指释放将回弹到这个比例.
* 在双击放大过程中不允许放大比例大于这个值.
* 覆盖此方法可以定制不同情况使用不同的最大可放大比例.
*
* @return 缩放比例
* @see .scaleEnd
* @see .doubleTap
*/
protected
val
maxScale
:
Float
get
()
=
MAX_SCALE
////////////////////////////////有效性判断////////////////////////////////
/**
* 判断当前情况是否能执行手势相关计算
*
*
* 包括:是否有图片,图片是否有尺寸,控件是否有尺寸.
*
* @return 是否能执行手势相关计算
*/
private
val
isReady
:
Boolean
get
()
=
(
getDrawable
()
!=
null
&&
getDrawable
().
getIntrinsicWidth
()
>
0
&&
getDrawable
().
getIntrinsicHeight
()
>
0
&&
getWidth
()
>
0
&&
getHeight
()
>
0
)
////////////////////////////////mask动画处理////////////////////////////////
/**
* mask修改的动画
*
*
* 和图片的动画相互独立.
*
* @see .zoomMaskTo
*/
private
var
mMaskAnimator
:
MaskAnimator
?
=
null
////////////////////////////////手势动画处理////////////////////////////////
/**
* 在单指模式下:
* 记录上一次手指的位置,用于计算新的位置和上一次位置的差值.
*
*
* 双指模式下:
* 记录两个手指的中点,作为和mScaleCenter绑定的点.
* 这个绑定可以保证mScaleCenter无论如何都会跟随这个中点.
*
* @see .mScaleCenter
*
* @see .scale
* @see .scaleEnd
*/
private
val
mLastMovePoint
=
PointF
()
/**
* 缩放模式下图片的缩放中点.
*
*
* 为其指代的点经过innerMatrix变换之后的值.
* 其指代的点在手势过程中始终跟随mLastMovePoint.
* 通过双指缩放时,其为缩放中心点.
*
* @see .saveScaleContext
* @see .mLastMovePoint
*
* @see .scale
*/
private
val
mScaleCenter
=
PointF
()
/**
* 缩放模式下的基础缩放比例
*
*
* 为外层缩放值除以开始缩放时两指距离.
* 其值乘上最新的两指之间距离为最新的图片缩放比例.
*
* @see .saveScaleContext
* @see .scale
*/
private
var
mScaleBase
=
0f
/**
* 图片缩放动画
*
*
* 缩放模式把图片的位置大小超出限制之后触发.
* 双击图片放大或缩小时触发.
* 手动调用outerMatrixTo触发.
*
* @see .scaleEnd
* @see .doubleTap
* @see .outerMatrixTo
*/
private
var
mScaleAnimator
:
ScaleAnimator
?
=
null
/**
* 滑动产生的惯性动画
*
* @see .fling
*/
private
var
mFlingAnimator
:
FlingAnimator
?
=
null
/**
* 常用手势处理
*
*
* 在onTouchEvent末尾被执行.
*/
private
val
mGestureDetector
=
GestureDetector
(
this
@PinchImageView
.
getContext
(),
object
:
GestureDetector
.
SimpleOnGestureListener
()
{
override
fun
onFling
(
e1
:
MotionEvent
,
e2
:
MotionEvent
,
velocityX
:
Float
,
velocityY
:
Float
):
Boolean
{
//只有在单指模式结束之后才允许执行fling
if
(
pinchMode
==
PINCH_MODE_FREE
&&
!(
mScaleAnimator
!=
null
&&
mScaleAnimator
!!
.
isRunning
))
{
fling
(
velocityX
,
velocityY
)
}
return
true
}
override
fun
onLongPress
(
e
:
MotionEvent
)
{
//触发长按
if
(
mOnLongClickListener
!=
null
)
{
mOnLongClickListener
!!
.
onLongClick
(
this
@PinchImageView
)
}
}
override
fun
onDoubleTap
(
e
:
MotionEvent
):
Boolean
{
//当手指快速第二次按下触发,此时必须是单指模式才允许执行doubleTap
if
(
pinchMode
==
PINCH_MODE_SCROLL
&&
!(
mScaleAnimator
!=
null
&&
mScaleAnimator
!!
.
isRunning
))
{
doubleTap
(
e
.
x
,
e
.
y
)
}
return
true
}
override
fun
onSingleTapConfirmed
(
e
:
MotionEvent
):
Boolean
{
//触发点击
if
(
mOnClickListener
!=
null
)
{
mOnClickListener
!!
.
onClick
(
this
@PinchImageView
)
}
return
true
}
})
override
fun
setOnClickListener
(
l
:
OnClickListener
)
{
//默认的click会在任何点击情况下都会触发,所以搞成自己的
mOnClickListener
=
l
}
override
fun
setOnLongClickListener
(
l
:
OnLongClickListener
)
{
//默认的long click会在任何长按情况下都会触发,所以搞成自己的
mOnLongClickListener
=
l
}
/**
* 获取外部变换矩阵.
*
*
* 外部变换矩阵记录了图片手势操作的最终结果,是相对于图片fit center状态的变换.
* 默认值为单位矩阵,此时图片为fit center状态.
*
* @param matrix 用于填充结果的对象
* @return 如果传了matrix参数则将matrix填充后返回, 否则new一个填充返回
*/
fun
getOuterMatrix
(
matrix
:
Matrix
?):
Matrix
{
var
matrix
=
matrix
if
(
matrix
==
null
)
{
matrix
=
Matrix
(
mOuterMatrix
)
}
else
{
matrix
.
set
(
mOuterMatrix
)
}
return
matrix
}
/**
* 获取内部变换矩阵.
*
*
* 内部变换矩阵是原图到fit center状态的变换,当原图尺寸变化或者控件大小变化都会发生改变
* 当尚未布局或者原图不存在时,其值无意义.所以在调用前需要确保前置条件有效,否则将影响计算结果.
*
* @param matrix 用于填充结果的对象
* @return 如果传了matrix参数则将matrix填充后返回, 否则new一个填充返回
*/
fun
getInnerMatrix
(
matrix
:
Matrix
?):
Matrix
{
var
matrix
=
matrix
if
(
matrix
==
null
)
{
matrix
=
Matrix
()
}
else
{
matrix
.
reset
()
}
if
(
isReady
)
{
//原图大小
val
tempSrc
=
MathUtils
.
rectFTake
(
0f
,
0f
,
getDrawable
().
getIntrinsicWidth
().
toFloat
(),
getDrawable
().
getIntrinsicHeight
().
toFloat
())
val
tempDst
=
MathUtils
.
rectFTake
(
0f
,
0f
,
getWidth
().
toFloat
(),
getHeight
().
toFloat
())
//控件大小
//计算fit center矩阵
matrix
.
setRectToRect
(
tempSrc
,
tempDst
,
Matrix
.
ScaleToFit
.
CENTER
)
//释放临时对象
MathUtils
.
rectFGiven
(
tempDst
)
MathUtils
.
rectFGiven
(
tempSrc
)
}
return
matrix
}
/**
* 获取图片总变换矩阵.
*
*
* 总变换矩阵为内部变换矩阵x外部变换矩阵,决定了原图到所见最终状态的变换
* 当尚未布局或者原图不存在时,其值无意义.所以在调用前需要确保前置条件有效,否则将影响计算结果.
*
* @param matrix 用于填充结果的对象
* @return 如果传了matrix参数则将matrix填充后返回, 否则new一个填充返回
* @see .getOuterMatrix
* @see .getInnerMatrix
*/
fun
getCurrentImageMatrix
(
matrix
:
Matrix
):
Matrix
{
var
matrix
=
matrix
//获取内部变换矩阵
matrix
=
getInnerMatrix
(
matrix
)
//乘上外部变换矩阵
matrix
.
postConcat
(
mOuterMatrix
)
return
matrix
}
/**
* 获取当前变换后的图片位置和尺寸
*
*
* 当尚未布局或者原图不存在时,其值无意义.所以在调用前需要确保前置条件有效,否则将影响计算结果.
*
* @param rectF 用于填充结果的对象
* @return 如果传了rectF参数则将rectF填充后返回, 否则new一个填充返回
* @see .getCurrentImageMatrix
*/
fun
getImageBound
(
rectF
:
RectF
?):
RectF
{
var
rectF
=
rectF
if
(
rectF
==
null
)
{
rectF
=
RectF
()
}
else
{
rectF
.
setEmpty
()
}
if
(!
isReady
)
{
return
rectF
}
else
{
//申请一个空matrix
val
matrix
=
MathUtils
.
matrixTake
()
//获取当前总变换矩阵
getCurrentImageMatrix
(
matrix
)
//对原图矩形进行变换得到当前显示矩形
rectF
.
set
(
0f
,
0f
,
getDrawable
().
getIntrinsicWidth
().
toFloat
(),
getDrawable
().
getIntrinsicHeight
().
toFloat
())
matrix
.
mapRect
(
rectF
)
//释放临时matrix
MathUtils
.
matrixGiven
(
matrix
)
return
rectF
}
}
/**
* 与ViewPager结合的时候使用
*
* @param direction
* @return
*/
override
fun
canScrollHorizontally
(
direction
:
Int
):
Boolean
{
if
(
pinchMode
==
PinchImageView
.
PINCH_MODE_SCALE
)
{
return
true
}
val
bound
=
getImageBound
(
null
)
?:
return
false
if
(
bound
.
isEmpty
)
{
return
false
}
return
if
(
direction
>
0
)
{
bound
.
right
>
getWidth
()
}
else
{
bound
.
left
<
0
}
}
/**
* 与ViewPager结合的时候使用
*
* @param direction
* @return
*/
override
fun
canScrollVertically
(
direction
:
Int
):
Boolean
{
if
(
pinchMode
==
PinchImageView
.
PINCH_MODE_SCALE
)
{
return
true
}
val
bound
=
getImageBound
(
null
)
?:
return
false
if
(
bound
.
isEmpty
)
{
return
false
}
return
if
(
direction
>
0
)
{
bound
.
bottom
>
getHeight
()
}
else
{
bound
.
top
<
0
}
}
////////////////////////////////公共状态设置////////////////////////////////
/**
* 执行当前outerMatrix到指定outerMatrix渐变的动画
*
*
* 调用此方法会停止正在进行中的手势以及手势动画.
* 当duration为0时,outerMatrix值会被立即设置而不会启动动画.
*
* @param endMatrix 动画目标矩阵
* @param duration 动画持续时间
* @see .getOuterMatrix
*/
fun
outerMatrixTo
(
endMatrix
:
Matrix
?,
duration
:
Long
)
{
if
(
endMatrix
==
null
)
{
return
}
//将手势设置为PINCH_MODE_FREE将停止后续手势的触发
pinchMode
=
PINCH_MODE_FREE
//停止所有正在进行的动画
cancelAllAnimator
()
//如果时间不合法立即执行结果
if
(
duration
<=
0
)
{
mOuterMatrix
.
set
(
endMatrix
)
dispatchOuterMatrixChanged
()
invalidate
()
}
else
{
//创建矩阵变化动画
mScaleAnimator
=
ScaleAnimator
(
mOuterMatrix
,
endMatrix
,
duration
)
mScaleAnimator
!!
.
start
()
}
}
/**
* 执行当前mask到指定mask的变化动画
*
*
* 调用此方法不会停止手势以及手势相关动画,但会停止正在进行的mask动画.
* 当前mask为null时,则不执行动画立即设置为目标mask.
* 当duration为0时,立即将当前mask设置为目标mask,不会执行动画.
*
* @param mask 动画目标mask
* @param duration 动画持续时间
* @see .getMask
*/
fun
zoomMaskTo
(
mask
:
RectF
?,
duration
:
Long
)
{
if
(
mask
==
null
)
{
return
}
//停止mask动画
if
(
mMaskAnimator
!=
null
)
{
mMaskAnimator
!!
.
cancel
()
mMaskAnimator
=
null
}
//如果duration为0或者之前没有设置过mask,不执行动画,立即设置
if
(
duration
<=
0
||
mMask
==
null
)
{
if
(
mMask
==
null
)
{
mMask
=
RectF
()
}
mMask
!!
.
set
(
mask
)
invalidate
()
}
else
{
//执行mask动画
mMaskAnimator
=
MaskAnimator
(
mMask
!!
,
mask
,
duration
)
mMaskAnimator
!!
.
start
()
}
}
/**
* 重置所有状态
*
*
* 重置位置到fit center状态,清空mask,停止所有手势,停止所有动画.
* 但不清空drawable,以及事件绑定相关数据.
*/
fun
reset
()
{
//重置位置到fit
mOuterMatrix
.
reset
()
dispatchOuterMatrixChanged
()
//清空mask
mMask
=
null
//停止所有手势
pinchMode
=
PINCH_MODE_FREE
mLastMovePoint
.
set
(
0f
,
0f
)
mScaleCenter
.
set
(
0f
,
0f
)
mScaleBase
=
0f
//停止所有动画
if
(
mMaskAnimator
!=
null
)
{
mMaskAnimator
!!
.
cancel
()
mMaskAnimator
=
null
}
cancelAllAnimator
()
//重绘
invalidate
()
}
////////////////////////////////对外广播事件////////////////////////////////
/**
* 外部矩阵变化事件通知监听器
*/
interface
OuterMatrixChangedListener
{
/**
* 外部矩阵变化回调
*
*
* 外部矩阵的任何变化后都收到此回调.
* 外部矩阵变化后,总变化矩阵,图片的展示位置都将发生变化.
*
* @param pinchImageView
* @see .getOuterMatrix
* @see .getCurrentImageMatrix
* @see .getImageBound
*/
fun
onOuterMatrixChanged
(
pinchImageView
:
PinchImageView
)
}
/**
* 添加外部矩阵变化监听
*
* @param listener
*/
fun
addOuterMatrixChangedListener
(
listener
:
OuterMatrixChangedListener
?)
{
if
(
listener
==
null
)
{
return
}
//如果监听列表没有被修改锁定直接将监听添加到监听列表
if
(
mDispatchOuterMatrixChangedLock
==
0
)
{
if
(
mOuterMatrixChangedListeners
==
null
)
{
mOuterMatrixChangedListeners
=
ArrayList
()
}
mOuterMatrixChangedListeners
!!
.
add
(
listener
)
}
else
{
//如果监听列表修改被锁定,那么尝试在监听列表副本上添加
//监听列表副本将会在锁定被解除时替换到监听列表里
if
(
mOuterMatrixChangedListenersCopy
==
null
)
{
if
(
mOuterMatrixChangedListeners
!=
null
)
{
mOuterMatrixChangedListenersCopy
=
ArrayList
(
mOuterMatrixChangedListeners
!!
)
}
else
{
mOuterMatrixChangedListenersCopy
=
ArrayList
()
}
}
mOuterMatrixChangedListenersCopy
!!
.
add
(
listener
)
}
}
/**
* 删除外部矩阵变化监听
*
* @param listener
*/
fun
removeOuterMatrixChangedListener
(
listener
:
OuterMatrixChangedListener
?)
{
if
(
listener
==
null
)
{
return
}
//如果监听列表没有被修改锁定直接在监听列表数据结构上修改
if
(
mDispatchOuterMatrixChangedLock
==
0
)
{
if
(
mOuterMatrixChangedListeners
!=
null
)
{
mOuterMatrixChangedListeners
!!
.
remove
(
listener
)
}
}
else
{
//如果监听列表被修改锁定,那么就在其副本上修改
//其副本将会在锁定解除时替换回监听列表
if
(
mOuterMatrixChangedListenersCopy
==
null
)
{
if
(
mOuterMatrixChangedListeners
!=
null
)
{
mOuterMatrixChangedListenersCopy
=
ArrayList
(
mOuterMatrixChangedListeners
!!
)
}
}
if
(
mOuterMatrixChangedListenersCopy
!=
null
)
{
mOuterMatrixChangedListenersCopy
!!
.
remove
(
listener
)
}
}
}
/**
* 触发外部矩阵修改事件
*
*
* 需要在每次给外部矩阵设置值时都调用此方法.
*
* @see .mOuterMatrix
*/
private
fun
dispatchOuterMatrixChanged
()
{
if
(
mOuterMatrixChangedListeners
==
null
)
{
return
}
//增加锁
//这里之所以用计数器做锁定是因为可能在锁定期间又间接调用了此方法产生递归
//使用boolean无法判断递归结束
mDispatchOuterMatrixChangedLock
++
//在列表循环过程中不允许修改列表,否则将引发崩溃
for
(
listener
in
mOuterMatrixChangedListeners
!!
)
{
listener
.
onOuterMatrixChanged
(
this
)
}
//减锁
mDispatchOuterMatrixChangedLock
--
//如果是递归的情况,mDispatchOuterMatrixChangedLock可能大于1,只有减到0才能算列表的锁定解除
if
(
mDispatchOuterMatrixChangedLock
==
0
)
{
//如果期间有修改列表,那么副本将不为null
if
(
mOuterMatrixChangedListenersCopy
!=
null
)
{
//将副本替换掉正式的列表
mOuterMatrixChangedListeners
=
mOuterMatrixChangedListenersCopy
//清空副本
mOuterMatrixChangedListenersCopy
=
null
}
}
}
/**
* 计算双击之后图片接下来应该被缩放的比例
*
*
* 如果值大于getMaxScale或者小于fit center尺寸,则实际使用取边界值.
* 通过覆盖此方法可以定制不同的图片被双击时使用不同的放大策略.
*
* @param innerScale 当前内部矩阵的缩放值
* @param outerScale 当前外部矩阵的缩放值
* @return 接下来的缩放比例
* @see .doubleTap
* @see .getMaxScale
*/
protected
fun
calculateNextScale
(
innerScale
:
Float
,
outerScale
:
Float
):
Float
{
val
currentScale
=
innerScale
*
outerScale
return
if
(
currentScale
<
MAX_SCALE
)
{
MAX_SCALE
}
else
{
innerScale
}
}
////////////////////////////////初始化////////////////////////////////
constructor
(
context
:
Context
)
:
super
(
context
)
{
initView
()
}
constructor
(
context
:
Context
,
attrs
:
AttributeSet
)
:
super
(
context
,
attrs
)
{
initView
()
}
constructor
(
context
:
Context
,
attrs
:
AttributeSet
,
defStyle
:
Int
)
:
super
(
context
,
attrs
,
defStyle
)
{
initView
()
}
private
fun
initView
()
{
//强制设置图片scaleType为matrix
super
.
setScaleType
(
ScaleType
.
MATRIX
)
}
//不允许设置scaleType,只能用内部设置的matrix
override
fun
setScaleType
(
scaleType
:
ScaleType
)
{}
////////////////////////////////绘制////////////////////////////////
var
isTran
=
false
override
fun
onDraw
(
canvas
:
Canvas
)
{
//在绘制前设置变换矩阵
if
(
isReady
)
{
val
matrix
=
MathUtils
.
matrixTake
()
if
(
isTran
)
{
setImageMatrix
(
mTranMatrix
)
}
else
{
setImageMatrix
(
getCurrentImageMatrix
(
matrix
))
}
MathUtils
.
matrixGiven
(
matrix
)
}
//对图像做遮罩处理
if
(
mMask
!=
null
)
{
canvas
.
save
()
canvas
.
clipRect
(
mMask
!!
)
super
.
onDraw
(
canvas
)
canvas
.
restore
()
}
else
{
super
.
onDraw
(
canvas
)
}
}
/**
* mask变换动画
*
*
* 将mask从一个rect动画到另外一个rect
*/
private
inner
class
MaskAnimator
/**
* 创建mask变换动画
*
* @param start 动画起始状态
* @param end 动画终点状态
* @param duration 动画持续时间
*/
(
start
:
RectF
,
end
:
RectF
,
duration
:
Long
)
:
ValueAnimator
(),
ValueAnimator
.
AnimatorUpdateListener
{
/**
* 开始mask
*/
private
val
mStart
=
FloatArray
(
4
)
/**
* 结束mask
*/
private
val
mEnd
=
FloatArray
(
4
)
/**
* 中间结果mask
*/
private
val
mResult
=
FloatArray
(
4
)
init
{
setFloatValues
(
0f
,
1f
)
setDuration
(
duration
)
addUpdateListener
(
this
)
//将起点终点拷贝到数组方便计算
mStart
[
0
]
=
start
.
left
mStart
[
1
]
=
start
.
top
mStart
[
2
]
=
start
.
right
mStart
[
3
]
=
start
.
bottom
mEnd
[
0
]
=
end
.
left
mEnd
[
1
]
=
end
.
top
mEnd
[
2
]
=
end
.
right
mEnd
[
3
]
=
end
.
bottom
}
override
fun
onAnimationUpdate
(
animation
:
ValueAnimator
)
{
//获取动画进度,0-1范围
val
value
=
animation
.
animatedValue
as
Float
//根据进度对起点终点之间做插值
for
(
i
in
0
..
3
)
{
mResult
[
i
]
=
mStart
[
i
]
+
(
mEnd
[
i
]
-
mStart
[
i
])
*
value
}
//期间mask有可能被置空了,所以判断一下
if
(
mMask
==
null
)
{
mMask
=
RectF
()
}
//设置新的mask并绘制
mMask
!!
.
set
(
mResult
[
0
],
mResult
[
1
],
mResult
[
2
],
mResult
[
3
])
invalidate
()
}
}
override
fun
onTouchEvent
(
event
:
MotionEvent
):
Boolean
{
super
.
onTouchEvent
(
event
)
val
action
=
event
.
action
and
MotionEvent
.
ACTION_MASK
//最后一个点抬起或者取消,结束所有模式
if
(
action
==
MotionEvent
.
ACTION_UP
||
action
==
MotionEvent
.
ACTION_CANCEL
)
{
//如果之前是缩放模式,还需要触发一下缩放结束动画
if
(
pinchMode
==
PINCH_MODE_SCALE
)
{
scaleEnd
()
}
pinchMode
=
PINCH_MODE_FREE
}
else
if
(
action
==
MotionEvent
.
ACTION_POINTER_UP
)
{
//多个手指情况下抬起一个手指,此时需要是缩放模式才触发
if
(
pinchMode
==
PINCH_MODE_SCALE
)
{
//抬起的点如果大于2,那么缩放模式还有效,但是有可能初始点变了,重新测量初始点
if
(
event
.
pointerCount
>
2
)
{
//如果还没结束缩放模式,但是第一个点抬起了,那么让第二个点和第三个点作为缩放控制点
if
(
event
.
action
shr
8
==
0
)
{
saveScaleContext
(
event
.
getX
(
1
),
event
.
getY
(
1
),
event
.
getX
(
2
),
event
.
getY
(
2
))
//如果还没结束缩放模式,但是第二个点抬起了,那么让第一个点和第三个点作为缩放控制点
}
else
if
(
event
.
action
shr
8
==
1
)
{
saveScaleContext
(
event
.
getX
(
0
),
event
.
getY
(
0
),
event
.
getX
(
2
),
event
.
getY
(
2
))
}
}
//如果抬起的点等于2,那么此时只剩下一个点,也不允许进入单指模式,因为此时可能图片没有在正确的位置上
}
//第一个点按下,开启滚动模式,记录开始滚动的点
}
else
if
(
action
==
MotionEvent
.
ACTION_DOWN
)
{
//在矩阵动画过程中不允许启动滚动模式
if
(!(
mScaleAnimator
!=
null
&&
mScaleAnimator
!!
.
isRunning
))
{
//停止所有动画
cancelAllAnimator
()
//切换到滚动模式
pinchMode
=
PINCH_MODE_SCROLL
//保存触发点用于move计算差值
mLastMovePoint
.
set
(
event
.
x
,
event
.
y
)
}
//非第一个点按下,关闭滚动模式,开启缩放模式,记录缩放模式的一些初始数据
}
else
if
(
action
==
MotionEvent
.
ACTION_POINTER_DOWN
)
{
//停止所有动画
cancelAllAnimator
()
//切换到缩放模式
pinchMode
=
PINCH_MODE_SCALE
//保存缩放的两个手指
saveScaleContext
(
event
.
getX
(
0
),
event
.
getY
(
0
),
event
.
getX
(
1
),
event
.
getY
(
1
))
}
else
if
(
action
==
MotionEvent
.
ACTION_MOVE
)
{
if
(!(
mScaleAnimator
!=
null
&&
mScaleAnimator
!!
.
isRunning
))
{
//在滚动模式下移动
if
(
pinchMode
==
PINCH_MODE_SCROLL
)
{
//每次移动产生一个差值累积到图片位置上
scrollBy
(
event
.
x
-
mLastMovePoint
.
x
,
event
.
y
-
mLastMovePoint
.
y
)
//记录新的移动点
mLastMovePoint
.
set
(
event
.
x
,
event
.
y
)
//在缩放模式下移动
}
else
if
(
pinchMode
==
PINCH_MODE_SCALE
&&
event
.
pointerCount
>
1
)
{
//两个缩放点间的距离
val
distance
=
MathUtils
.
getDistance
(
event
.
getX
(
0
),
event
.
getY
(
0
),
event
.
getX
(
1
),
event
.
getY
(
1
))
//保存缩放点中点
val
lineCenter
=
MathUtils
.
getCenterPoint
(
event
.
getX
(
0
),
event
.
getY
(
0
),
event
.
getX
(
1
),
event
.
getY
(
1
))
mLastMovePoint
.
set
(
lineCenter
[
0
],
lineCenter
[
1
])
//处理缩放
scale
(
mScaleCenter
,
mScaleBase
,
distance
,
mLastMovePoint
)
}
}
}
//无论如何都处理各种外部手势
mGestureDetector
.
onTouchEvent
(
event
)
return
true
}
/**
* 让图片移动一段距离
*
*
* 不能移动超过可移动范围,超过了就到可移动范围边界为止.
*
* @param xDiff 移动距离
* @param yDiff 移动距离
* @return 是否改变了位置
*/
private
fun
scrollBy
(
xDiff
:
Float
,
yDiff
:
Float
):
Boolean
{
var
xDiff
=
xDiff
var
yDiff
=
yDiff
if
(!
isReady
)
{
return
false
}
//原图方框
val
bound
=
MathUtils
.
rectFTake
()
getImageBound
(
bound
)
//控件大小
val
displayWidth
=
getWidth
()
val
displayHeight
=
getHeight
()
//如果当前图片宽度小于控件宽度,则不能移动
if
(
bound
.
right
-
bound
.
left
<
displayWidth
)
{
xDiff
=
0f
//如果图片左边在移动后超出控件左边
}
else
if
(
bound
.
left
+
xDiff
>
0
)
{
//如果在移动之前是没超出的,计算应该移动的距离
if
(
bound
.
left
<
0
)
{
xDiff
=
-
bound
.
left
//否则无法移动
}
else
{
xDiff
=
0f
}
//如果图片右边在移动后超出控件右边
}
else
if
(
bound
.
right
+
xDiff
<
displayWidth
)
{
//如果在移动之前是没超出的,计算应该移动的距离
if
(
bound
.
right
>
displayWidth
)
{
xDiff
=
displayWidth
-
bound
.
right
//否则无法移动
}
else
{
xDiff
=
0f
}
}
//以下同理
if
(
bound
.
bottom
-
bound
.
top
<
displayHeight
)
{
yDiff
=
0f
}
else
if
(
bound
.
top
+
yDiff
>
0
)
{
if
(
bound
.
top
<
0
)
{
yDiff
=
-
bound
.
top
}
else
{
yDiff
=
0f
}
}
else
if
(
bound
.
bottom
+
yDiff
<
displayHeight
)
{
if
(
bound
.
bottom
>
displayHeight
)
{
yDiff
=
displayHeight
-
bound
.
bottom
}
else
{
yDiff
=
0f
}
}
MathUtils
.
rectFGiven
(
bound
)
//应用移动变换
mOuterMatrix
.
postTranslate
(
xDiff
,
yDiff
)
dispatchOuterMatrixChanged
()
//触发重绘
invalidate
()
//检查是否有变化
return
if
(
xDiff
!=
0f
||
yDiff
!=
0f
)
{
true
}
else
{
false
}
}
/**
* 记录缩放前的一些信息
*
*
* 保存基础缩放值.
* 保存图片缩放中点.
*
* @param x1 缩放第一个手指
* @param y1 缩放第一个手指
* @param x2 缩放第二个手指
* @param y2 缩放第二个手指
*/
private
fun
saveScaleContext
(
x1
:
Float
,
y1
:
Float
,
x2
:
Float
,
y2
:
Float
)
{
//记录基础缩放值,其中图片缩放比例按照x方向来计算
//理论上图片应该是等比的,x和y方向比例相同
//但是有可能外部设定了不规范的值.
//但是后续的scale操作会将xy不等的缩放值纠正,改成和x方向相同
mScaleBase
=
MathUtils
.
getMatrixScale
(
mOuterMatrix
)[
0
]
/
MathUtils
.
getDistance
(
x1
,
y1
,
x2
,
y2
)
//两手指的中点在屏幕上落在了图片的某个点上,图片上的这个点在经过总矩阵变换后和手指中点相同
//现在我们需要得到图片上这个点在图片是fit center状态下在屏幕上的位置
//因为后续的计算都是基于图片是fit center状态下进行变换
//所以需要把两手指中点除以外层变换矩阵得到mScaleCenter
val
center
=
MathUtils
.
inverseMatrixPoint
(
MathUtils
.
getCenterPoint
(
x1
,
y1
,
x2
,
y2
),
mOuterMatrix
)
mScaleCenter
.
set
(
center
[
0
],
center
[
1
])
}
/**
* 对图片按照一些手势信息进行缩放
*
* @param scaleCenter mScaleCenter
* @param scaleBase mScaleBase
* @param distance 手指两点之间距离
* @param lineCenter 手指两点之间中点
* @see .mScaleCenter
*
* @see .mScaleBase
*/
private
fun
scale
(
scaleCenter
:
PointF
,
scaleBase
:
Float
,
distance
:
Float
,
lineCenter
:
PointF
)
{
if
(!
isReady
)
{
return
}
//计算图片从fit center状态到目标状态的缩放比例
val
scale
=
scaleBase
*
distance
val
matrix
=
MathUtils
.
matrixTake
()
//按照图片缩放中心缩放,并且让缩放中心在缩放点中点上
matrix
.
postScale
(
scale
,
scale
,
scaleCenter
.
x
,
scaleCenter
.
y
)
//让图片的缩放中点跟随手指缩放中点
matrix
.
postTranslate
(
lineCenter
.
x
-
scaleCenter
.
x
,
lineCenter
.
y
-
scaleCenter
.
y
)
//应用变换
mOuterMatrix
.
set
(
matrix
)
MathUtils
.
matrixGiven
(
matrix
)
dispatchOuterMatrixChanged
()
//重绘
invalidate
()
}
/**
* 双击后放大或者缩小
*
*
* 将图片缩放比例缩放到nextScale指定的值.
* 但nextScale值不能大于最大缩放值不能小于fit center情况下的缩放值.
* 将双击的点尽量移动到控件中心.
*
* @param x 双击的点
* @param y 双击的点
* @see .calculateNextScale
* @see .getMaxScale
*/
private
fun
doubleTap
(
x
:
Float
,
y
:
Float
)
{
if
(!
isReady
)
{
return
}
//获取第一层变换矩阵
val
innerMatrix
=
MathUtils
.
matrixTake
()
getInnerMatrix
(
innerMatrix
)
//当前总的缩放比例
val
innerScale
=
MathUtils
.
getMatrixScale
(
innerMatrix
)[
0
]
val
outerScale
=
MathUtils
.
getMatrixScale
(
mOuterMatrix
)[
0
]
val
currentScale
=
innerScale
*
outerScale
//控件大小
val
displayWidth
=
getWidth
()
val
displayHeight
=
getHeight
()
//最大放大大小
val
maxScale
=
maxScale
//接下来要放大的大小
var
nextScale
=
calculateNextScale
(
innerScale
,
outerScale
)
//如果接下来放大大于最大值或者小于fit center值,则取边界
if
(
nextScale
>
maxScale
)
{
nextScale
=
maxScale
}
if
(
nextScale
<
innerScale
)
{
nextScale
=
innerScale
}
//开始计算缩放动画的结果矩阵
val
animEnd
=
MathUtils
.
matrixTake
(
mOuterMatrix
)
//计算还需缩放的倍数
animEnd
.
postScale
(
nextScale
/
currentScale
,
nextScale
/
currentScale
,
x
,
y
)
//将放大点移动到控件中心
animEnd
.
postTranslate
(
displayWidth
/
2f
-
x
,
displayHeight
/
2f
-
y
)
//得到放大之后的图片方框
val
testMatrix
=
MathUtils
.
matrixTake
(
innerMatrix
)
testMatrix
.
postConcat
(
animEnd
)
val
testBound
=
MathUtils
.
rectFTake
(
0f
,
0f
,
getDrawable
().
getIntrinsicWidth
().
toFloat
(),
getDrawable
().
getIntrinsicHeight
().
toFloat
())
testMatrix
.
mapRect
(
testBound
)
//修正位置
var
postX
=
0f
var
postY
=
0f
if
(
testBound
.
right
-
testBound
.
left
<
displayWidth
)
{
postX
=
displayWidth
/
2f
-
(
testBound
.
right
+
testBound
.
left
)
/
2f
}
else
if
(
testBound
.
left
>
0
)
{
postX
=
-
testBound
.
left
}
else
if
(
testBound
.
right
<
displayWidth
)
{
postX
=
displayWidth
-
testBound
.
right
}
if
(
testBound
.
bottom
-
testBound
.
top
<
displayHeight
)
{
postY
=
displayHeight
/
2f
-
(
testBound
.
bottom
+
testBound
.
top
)
/
2f
}
else
if
(
testBound
.
top
>
0
)
{
postY
=
-
testBound
.
top
}
else
if
(
testBound
.
bottom
<
displayHeight
)
{
postY
=
displayHeight
-
testBound
.
bottom
}
//应用修正位置
animEnd
.
postTranslate
(
postX
,
postY
)
//清理当前可能正在执行的动画
cancelAllAnimator
()
//启动矩阵动画
mScaleAnimator
=
ScaleAnimator
(
mOuterMatrix
,
animEnd
)
mScaleAnimator
!!
.
start
()
//清理临时变量
MathUtils
.
rectFGiven
(
testBound
)
MathUtils
.
matrixGiven
(
testMatrix
)
MathUtils
.
matrixGiven
(
animEnd
)
MathUtils
.
matrixGiven
(
innerMatrix
)
}
/**
* 当缩放操作结束动画
*
*
* 如果图片超过边界,找到最近的位置动画恢复.
* 如果图片缩放尺寸超过最大值或者最小值,找到最近的值动画恢复.
*/
private
fun
scaleEnd
()
{
if
(!
isReady
)
{
return
}
//是否修正了位置
var
change
=
false
//获取图片整体的变换矩阵
val
currentMatrix
=
MathUtils
.
matrixTake
()
getCurrentImageMatrix
(
currentMatrix
)
//整体缩放比例
val
currentScale
=
MathUtils
.
getMatrixScale
(
currentMatrix
)[
0
]
//第二层缩放比例
val
outerScale
=
MathUtils
.
getMatrixScale
(
mOuterMatrix
)[
0
]
//控件大小
val
displayWidth
=
getWidth
()
val
displayHeight
=
getHeight
()
//最大缩放比例
val
maxScale
=
maxScale
//比例修正
var
scalePost
=
1f
//位置修正
var
postX
=
0f
var
postY
=
0f
//如果整体缩放比例大于最大比例,进行缩放修正
if
(
currentScale
>
maxScale
)
{
scalePost
=
maxScale
/
currentScale
}
//如果缩放修正后整体导致第二层缩放小于1(就是图片比fit center状态还小),重新修正缩放
if
(
outerScale
*
scalePost
<
1f
)
{
scalePost
=
1f
/
outerScale
}
//如果缩放修正不为1,说明进行了修正
if
(
scalePost
!=
1f
)
{
change
=
true
}
//尝试根据缩放点进行缩放修正
val
testMatrix
=
MathUtils
.
matrixTake
(
currentMatrix
)
testMatrix
.
postScale
(
scalePost
,
scalePost
,
mLastMovePoint
.
x
,
mLastMovePoint
.
y
)
val
testBound
=
MathUtils
.
rectFTake
(
0f
,
0f
,
getDrawable
().
getIntrinsicWidth
().
toFloat
(),
getDrawable
().
getIntrinsicHeight
().
toFloat
())
//获取缩放修正后的图片方框
testMatrix
.
mapRect
(
testBound
)
//检测缩放修正后位置有无超出,如果超出进行位置修正
if
(
testBound
.
right
-
testBound
.
left
<
displayWidth
)
{
postX
=
displayWidth
/
2f
-
(
testBound
.
right
+
testBound
.
left
)
/
2f
}
else
if
(
testBound
.
left
>
0
)
{
postX
=
-
testBound
.
left
}
else
if
(
testBound
.
right
<
displayWidth
)
{
postX
=
displayWidth
-
testBound
.
right
}
if
(
testBound
.
bottom
-
testBound
.
top
<
displayHeight
)
{
postY
=
displayHeight
/
2f
-
(
testBound
.
bottom
+
testBound
.
top
)
/
2f
}
else
if
(
testBound
.
top
>
0
)
{
postY
=
-
testBound
.
top
}
else
if
(
testBound
.
bottom
<
displayHeight
)
{
postY
=
displayHeight
-
testBound
.
bottom
}
//如果位置修正不为0,说明进行了修正
if
(
postX
!=
0f
||
postY
!=
0f
)
{
change
=
true
}
//只有有执行修正才执行动画
if
(
change
)
{
//计算结束矩阵
val
animEnd
=
MathUtils
.
matrixTake
(
mOuterMatrix
)
animEnd
.
postScale
(
scalePost
,
scalePost
,
mLastMovePoint
.
x
,
mLastMovePoint
.
y
)
animEnd
.
postTranslate
(
postX
,
postY
)
//清理当前可能正在执行的动画
cancelAllAnimator
()
//启动矩阵动画
mScaleAnimator
=
ScaleAnimator
(
mOuterMatrix
,
animEnd
)
mScaleAnimator
!!
.
start
()
//清理临时变量
MathUtils
.
matrixGiven
(
animEnd
)
}
//清理临时变量
MathUtils
.
rectFGiven
(
testBound
)
MathUtils
.
matrixGiven
(
testMatrix
)
MathUtils
.
matrixGiven
(
currentMatrix
)
}
/**
* 执行惯性动画
*
*
* 动画在遇到不能移动就停止.
* 动画速度衰减到很小就停止.
*
*
* 其中参数速度单位为 像素/秒
*
* @param vx x方向速度
* @param vy y方向速度
*/
private
fun
fling
(
vx
:
Float
,
vy
:
Float
)
{
if
(!
isReady
)
{
return
}
//清理当前可能正在执行的动画
cancelAllAnimator
()
//创建惯性动画
//FlingAnimator单位为 像素/帧,一秒60帧
mFlingAnimator
=
FlingAnimator
(
vx
/
60f
,
vy
/
60f
)
mFlingAnimator
!!
.
start
()
}
/**
* 停止所有手势动画
*/
private
fun
cancelAllAnimator
()
{
if
(
mScaleAnimator
!=
null
)
{
mScaleAnimator
!!
.
cancel
()
mScaleAnimator
=
null
}
if
(
mFlingAnimator
!=
null
)
{
mFlingAnimator
!!
.
cancel
()
mFlingAnimator
=
null
}
}
/**
* 惯性动画
*
*
* 速度逐渐衰减,每帧速度衰减为原来的FLING_DAMPING_FACTOR,当速度衰减到小于1时停止.
* 当图片不能移动时,动画停止.
*/
private
inner
class
FlingAnimator
/**
* 创建惯性动画
*
*
* 参数单位为 像素/帧
*
* @param vectorX 速度向量
* @param vectorY 速度向量
*/
(
vectorX
:
Float
,
vectorY
:
Float
)
:
ValueAnimator
(),
ValueAnimator
.
AnimatorUpdateListener
{
/**
* 速度向量
*/
private
val
mVector
:
FloatArray
init
{
setFloatValues
(
0f
,
1f
)
duration
=
1000000
addUpdateListener
(
this
)
mVector
=
floatArrayOf
(
vectorX
,
vectorY
)
}
override
fun
onAnimationUpdate
(
animation
:
ValueAnimator
)
{
//移动图像并给出结果
val
result
=
scrollBy
(
mVector
[
0
],
mVector
[
1
])
//衰减速度
mVector
[
0
]
*=
FLING_DAMPING_FACTOR
mVector
[
1
]
*=
FLING_DAMPING_FACTOR
//速度太小或者不能移动了就结束
if
(!
result
||
MathUtils
.
getDistance
(
0f
,
0f
,
mVector
[
0
],
mVector
[
1
])
<
1f
)
{
animation
.
cancel
()
}
}
}
/**
* 缩放动画
*
*
* 在给定时间内从一个矩阵的变化逐渐动画到另一个矩阵的变化
*/
private
inner
class
ScaleAnimator
/**
* 构建一个缩放动画
*
*
* 从一个矩阵变换到另外一个矩阵
*
* @param start 开始矩阵
* @param end 结束矩阵
* @param duration 动画时间
*/
@JvmOverloads
constructor
(
start
:
Matrix
,
end
:
Matrix
,
duration
:
Long
=
SCALE_ANIMATOR_DURATION
.
toLong
())
:
ValueAnimator
(),
ValueAnimator
.
AnimatorUpdateListener
{
/**
* 开始矩阵
*/
private
val
mStart
=
FloatArray
(
9
)
/**
* 结束矩阵
*/
private
val
mEnd
=
FloatArray
(
9
)
/**
* 中间结果矩阵
*/
private
val
mResult
=
FloatArray
(
9
)
init
{
setFloatValues
(
0f
,
1f
)
setDuration
(
duration
)
addUpdateListener
(
this
)
start
.
getValues
(
mStart
)
end
.
getValues
(
mEnd
)
}
override
fun
onAnimationUpdate
(
animation
:
ValueAnimator
)
{
//获取动画进度
val
value
=
animation
.
animatedValue
as
Float
//根据动画进度计算矩阵中间插值
for
(
i
in
0
..
8
)
{
mResult
[
i
]
=
mStart
[
i
]
+
(
mEnd
[
i
]
-
mStart
[
i
])
*
value
}
//设置矩阵并重绘
mOuterMatrix
.
setValues
(
mResult
)
dispatchOuterMatrixChanged
()
invalidate
()
}
}
/**
* 构建一个缩放动画
*
*
* 从一个矩阵变换到另外一个矩阵
*
* @param start 开始矩阵
* @param end 结束矩阵
*/
////////////////////////////////防止内存抖动复用对象////////////////////////////////
/**
* 对象池
*
*
* 防止频繁new对象产生内存抖动.
* 由于对象池最大长度限制,如果吞度量超过对象池容量,仍然会发生抖动.
* 此时需要增大对象池容量,但是会占用更多内存.
*
* @param <T> 对象池容纳的对象类型
</T> */
private
abstract
class
ObjectsPool
<
T
>
/**
* 创建一个对象池
*
* @param size 对象池最大容量
*/
(
/**
* 对象池的最大容量
*/
private
val
mSize
:
Int
)
{
/**
* 对象池队列
*/
private
val
mQueue
:
Queue
<
T
>
init
{
mQueue
=
LinkedList
()
}
/**
* 获取一个空闲的对象
*
*
* 如果对象池为空,则对象池自己会new一个返回.
* 如果对象池内有对象,则取一个已存在的返回.
* take出来的对象用完要记得调用given归还.
* 如果不归还,让然会发生内存抖动,但不会引起泄漏.
*
* @return 可用的对象
* @see .given
*/
fun
take
():
T
{
//如果池内为空就创建一个
return
if
(
mQueue
.
size
==
0
)
{
newInstance
()
}
else
{
//对象池里有就从顶端拿出来一个返回
resetInstance
(
mQueue
.
poll
())
}
}
/**
* 归还对象池内申请的对象
*
*
* 如果归还的对象数量超过对象池容量,那么归还的对象就会被丢弃.
*
* @param obj 归还的对象
* @see .take
*/
fun
given
(
obj
:
T
?)
{
//如果对象池还有空位子就归还对象
if
(
obj
!=
null
&&
mQueue
.
size
<
mSize
)
{
mQueue
.
offer
(
obj
)
}
}
/**
* 实例化对象
*
* @return 创建的对象
*/
protected
abstract
fun
newInstance
():
T
/**
* 重置对象
*
*
* 把对象数据清空到就像刚创建的一样.
*
* @param obj 需要被重置的对象
* @return 被重置之后的对象
*/
protected
abstract
fun
resetInstance
(
obj
:
T
):
T
}
/**
* 矩阵对象池
*/
private
class
MatrixPool
(
size
:
Int
)
:
ObjectsPool
<
Matrix
>(
size
)
{
override
fun
newInstance
():
Matrix
{
return
Matrix
()
}
override
fun
resetInstance
(
obj
:
Matrix
):
Matrix
{
obj
.
reset
()
return
obj
}
}
/**
* 矩形对象池
*/
private
class
RectFPool
(
size
:
Int
)
:
ObjectsPool
<
RectF
>(
size
)
{
override
fun
newInstance
():
RectF
{
return
RectF
()
}
override
fun
resetInstance
(
obj
:
RectF
):
RectF
{
obj
.
setEmpty
()
return
obj
}
}
////////////////////////////////数学计算工具类////////////////////////////////
/**
* 数学计算工具类
*/
object
MathUtils
{
/**
* 矩阵对象池
*/
private
val
mMatrixPool
=
MatrixPool
(
16
)
/**
* 矩形对象池
*/
private
val
mRectFPool
=
RectFPool
(
16
)
/**
* 获取矩阵对象
*/
fun
matrixTake
():
Matrix
{
return
mMatrixPool
.
take
()
}
/**
* 获取某个矩阵的copy
*/
fun
matrixTake
(
matrix
:
Matrix
?):
Matrix
{
val
result
=
mMatrixPool
.
take
()
if
(
matrix
!=
null
)
{
result
.
set
(
matrix
)
}
return
result
}
/**
* 归还矩阵对象
*/
fun
matrixGiven
(
matrix
:
Matrix
)
{
mMatrixPool
.
given
(
matrix
)
}
/**
* 获取矩形对象
*/
fun
rectFTake
():
RectF
{
return
mRectFPool
.
take
()
}
/**
* 按照指定值获取矩形对象
*/
fun
rectFTake
(
left
:
Float
,
top
:
Float
,
right
:
Float
,
bottom
:
Float
):
RectF
{
val
result
=
mRectFPool
.
take
()
result
.
set
(
left
,
top
,
right
,
bottom
)
return
result
}
/**
* 获取某个矩形的副本
*/
fun
rectFTake
(
rectF
:
RectF
?):
RectF
{
val
result
=
mRectFPool
.
take
()
if
(
rectF
!=
null
)
{
result
.
set
(
rectF
)
}
return
result
}
/**
* 归还矩形对象
*/
fun
rectFGiven
(
rectF
:
RectF
)
{
mRectFPool
.
given
(
rectF
)
}
/**
* 获取两点之间距离
*
* @param x1 点1
* @param y1 点1
* @param x2 点2
* @param y2 点2
* @return 距离
*/
fun
getDistance
(
x1
:
Float
,
y1
:
Float
,
x2
:
Float
,
y2
:
Float
):
Float
{
val
x
=
x1
-
x2
val
y
=
y1
-
y2
return
Math
.
sqrt
((
x
*
x
+
y
*
y
).
toDouble
()).
toFloat
()
}
/**
* 获取两点的中点
*
* @param x1 点1
* @param y1 点1
* @param x2 点2
* @param y2 点2
* @return float[]{x, y}
*/
fun
getCenterPoint
(
x1
:
Float
,
y1
:
Float
,
x2
:
Float
,
y2
:
Float
):
FloatArray
{
return
floatArrayOf
((
x1
+
x2
)
/
2f
,
(
y1
+
y2
)
/
2f
)
}
/**
* 获取矩阵的缩放值
*
* @param matrix 要计算的矩阵
* @return float[]{scaleX, scaleY}
*/
fun
getMatrixScale
(
matrix
:
Matrix
?):
FloatArray
{
if
(
matrix
!=
null
)
{
val
value
=
FloatArray
(
9
)
matrix
.
getValues
(
value
)
return
floatArrayOf
(
value
[
0
],
value
[
4
])
}
else
{
return
FloatArray
(
2
)
}
}
/**
* 计算点除以矩阵的值
*
*
* matrix.mapPoints(unknownPoint) -> point
* 已知point和matrix,求unknownPoint的值.
*
* @param point
* @param matrix
* @return unknownPoint
*/
fun
inverseMatrixPoint
(
point
:
FloatArray
?,
matrix
:
Matrix
?):
FloatArray
{
if
(
point
!=
null
&&
matrix
!=
null
)
{
val
dst
=
FloatArray
(
2
)
//计算matrix的逆矩阵
val
inverse
=
matrixTake
()
matrix
.
invert
(
inverse
)
//用逆矩阵变换point到dst,dst就是结果
inverse
.
mapPoints
(
dst
,
point
)
//清除临时变量
matrixGiven
(
inverse
)
return
dst
}
else
{
return
FloatArray
(
2
)
}
}
/**
* 计算两个矩形之间的变换矩阵
*
*
* unknownMatrix.mapRect(to, from)
* 已知from矩形和to矩形,求unknownMatrix
*
* @param from
* @param to
* @param result unknownMatrix
*/
fun
calculateRectTranslateMatrix
(
from
:
RectF
?,
to
:
RectF
?,
result
:
Matrix
?)
{
if
(
from
==
null
||
to
==
null
||
result
==
null
)
{
return
}
if
(
from
.
width
()
==
0f
||
from
.
height
()
==
0f
)
{
return
}
result
.
reset
()
result
.
postTranslate
(-
from
.
left
,
-
from
.
top
)
result
.
postScale
(
to
.
width
()
/
from
.
width
(),
to
.
height
()
/
from
.
height
())
result
.
postTranslate
(
to
.
left
,
to
.
top
)
}
/**
* 计算图片在某个ImageView中的显示矩形
*
* @param container ImageView的Rect
* @param srcWidth 图片的宽度
* @param srcHeight 图片的高度
* @param scaleType 图片在ImageView中的ScaleType
* @param result 图片应该在ImageView中展示的矩形
*/
fun
calculateScaledRectInContainer
(
container
:
RectF
?,
srcWidth
:
Float
,
srcHeight
:
Float
,
scaleType
:
ScaleType
?,
result
:
RectF
?)
{
var
scaleType
=
scaleType
if
(
container
==
null
||
result
==
null
)
{
return
}
if
(
srcWidth
==
0f
||
srcHeight
==
0f
)
{
return
}
//默认scaleType为fit center
if
(
scaleType
==
null
)
{
scaleType
=
ScaleType
.
FIT_CENTER
}
result
.
setEmpty
()
if
(
ScaleType
.
FIT_XY
.
equals
(
scaleType
))
{
result
.
set
(
container
)
}
else
if
(
ScaleType
.
CENTER
.
equals
(
scaleType
))
{
val
matrix
=
matrixTake
()
val
rect
=
rectFTake
(
0f
,
0f
,
srcWidth
,
srcHeight
)
matrix
.
setTranslate
((
container
.
width
()
-
srcWidth
)
*
0.5f
,
(
container
.
height
()
-
srcHeight
)
*
0.5f
)
matrix
.
mapRect
(
result
,
rect
)
rectFGiven
(
rect
)
matrixGiven
(
matrix
)
result
.
left
+=
container
.
left
result
.
right
+=
container
.
left
result
.
top
+=
container
.
top
result
.
bottom
+=
container
.
top
}
else
if
(
ScaleType
.
CENTER_CROP
.
equals
(
scaleType
))
{
val
matrix
=
matrixTake
()
val
rect
=
rectFTake
(
0f
,
0f
,
srcWidth
,
srcHeight
)
val
scale
:
Float
var
dx
=
0f
var
dy
=
0f
if
(
srcWidth
*
container
.
height
()
>
container
.
width
()
*
srcHeight
)
{
scale
=
container
.
height
()
/
srcHeight
dx
=
(
container
.
width
()
-
srcWidth
*
scale
)
*
0.5f
}
else
{
scale
=
container
.
width
()
/
srcWidth
dy
=
(
container
.
height
()
-
srcHeight
*
scale
)
*
0.5f
}
matrix
.
setScale
(
scale
,
scale
)
matrix
.
postTranslate
(
dx
,
dy
)
matrix
.
mapRect
(
result
,
rect
)
rectFGiven
(
rect
)
matrixGiven
(
matrix
)
result
.
left
+=
container
.
left
result
.
right
+=
container
.
left
result
.
top
+=
container
.
top
result
.
bottom
+=
container
.
top
}
else
if
(
ScaleType
.
CENTER_INSIDE
.
equals
(
scaleType
))
{
val
matrix
=
matrixTake
()
val
rect
=
rectFTake
(
0f
,
0f
,
srcWidth
,
srcHeight
)
val
scale
:
Float
val
dx
:
Float
val
dy
:
Float
if
(
srcWidth
<=
container
.
width
()
&&
srcHeight
<=
container
.
height
())
{
scale
=
1f
}
else
{
scale
=
Math
.
min
(
container
.
width
()
/
srcWidth
,
container
.
height
()
/
srcHeight
)
}
dx
=
(
container
.
width
()
-
srcWidth
*
scale
)
*
0.5f
dy
=
(
container
.
height
()
-
srcHeight
*
scale
)
*
0.5f
matrix
.
setScale
(
scale
,
scale
)
matrix
.
postTranslate
(
dx
,
dy
)
matrix
.
mapRect
(
result
,
rect
)
rectFGiven
(
rect
)
matrixGiven
(
matrix
)
result
.
left
+=
container
.
left
result
.
right
+=
container
.
left
result
.
top
+=
container
.
top
result
.
bottom
+=
container
.
top
}
else
if
(
ScaleType
.
FIT_CENTER
.
equals
(
scaleType
))
{
val
matrix
=
matrixTake
()
val
rect
=
rectFTake
(
0f
,
0f
,
srcWidth
,
srcHeight
)
val
tempSrc
=
rectFTake
(
0f
,
0f
,
srcWidth
,
srcHeight
)
val
tempDst
=
rectFTake
(
0f
,
0f
,
container
.
width
(),
container
.
height
())
matrix
.
setRectToRect
(
tempSrc
,
tempDst
,
Matrix
.
ScaleToFit
.
CENTER
)
matrix
.
mapRect
(
result
,
rect
)
rectFGiven
(
tempDst
)
rectFGiven
(
tempSrc
)
rectFGiven
(
rect
)
matrixGiven
(
matrix
)
result
.
left
+=
container
.
left
result
.
right
+=
container
.
left
result
.
top
+=
container
.
top
result
.
bottom
+=
container
.
top
}
else
if
(
ScaleType
.
FIT_START
.
equals
(
scaleType
))
{
val
matrix
=
matrixTake
()
val
rect
=
rectFTake
(
0f
,
0f
,
srcWidth
,
srcHeight
)
val
tempSrc
=
rectFTake
(
0f
,
0f
,
srcWidth
,
srcHeight
)
val
tempDst
=
rectFTake
(
0f
,
0f
,
container
.
width
(),
container
.
height
())
matrix
.
setRectToRect
(
tempSrc
,
tempDst
,
Matrix
.
ScaleToFit
.
START
)
matrix
.
mapRect
(
result
,
rect
)
rectFGiven
(
tempDst
)
rectFGiven
(
tempSrc
)
rectFGiven
(
rect
)
matrixGiven
(
matrix
)
result
.
left
+=
container
.
left
result
.
right
+=
container
.
left
result
.
top
+=
container
.
top
result
.
bottom
+=
container
.
top
}
else
if
(
ScaleType
.
FIT_END
.
equals
(
scaleType
))
{
val
matrix
=
matrixTake
()
val
rect
=
rectFTake
(
0f
,
0f
,
srcWidth
,
srcHeight
)
val
tempSrc
=
rectFTake
(
0f
,
0f
,
srcWidth
,
srcHeight
)
val
tempDst
=
rectFTake
(
0f
,
0f
,
container
.
width
(),
container
.
height
())
matrix
.
setRectToRect
(
tempSrc
,
tempDst
,
Matrix
.
ScaleToFit
.
END
)
matrix
.
mapRect
(
result
,
rect
)
rectFGiven
(
tempDst
)
rectFGiven
(
tempSrc
)
rectFGiven
(
rect
)
matrixGiven
(
matrix
)
result
.
left
+=
container
.
left
result
.
right
+=
container
.
left
result
.
top
+=
container
.
top
result
.
bottom
+=
container
.
top
}
else
{
result
.
set
(
container
)
}
}
}
companion
object
{
////////////////////////////////配置参数////////////////////////////////
/**
* 图片缩放动画时间
*/
val
SCALE_ANIMATOR_DURATION
=
200
/**
* 惯性动画衰减参数
*/
val
FLING_DAMPING_FACTOR
=
0.9f
/**
* 图片最大放大比例
*/
private
val
MAX_SCALE
=
3f
////////////////////////////////公共状态获取////////////////////////////////
/**
* 手势状态:自由状态
*
* @see .getPinchMode
*/
val
PINCH_MODE_FREE
=
0
/**
* 手势状态:单指滚动状态
*
* @see .getPinchMode
*/
val
PINCH_MODE_SCROLL
=
1
/**
* 手势状态:双指缩放状态
*
* @see .getPinchMode
*/
val
PINCH_MODE_SCALE
=
2
}
}
android/src/main/java/com/example/gengmei_flutter_plugin/utils/MyUtil.kt
View file @
f0733818
...
...
@@ -19,7 +19,13 @@ import android.view.WindowManager
import
android.os.Build
import
android.annotation.TargetApi
import
android.graphics.Color
import
android.graphics.Matrix
import
android.view.View
import
android.R.attr.bitmap
import
android.opengl.ETC1.getHeight
import
android.opengl.ETC1.getWidth
/**
...
...
@@ -31,6 +37,7 @@ class MyUtil {
companion
object
{
val
matrix
=
Matrix
()
fun
getImageCacheDir
(
context
:
Context
,
cacheName
:
String
):
File
?
{
val
cacheDir
=
context
.
externalCacheDir
if
(
cacheDir
!=
null
)
{
...
...
@@ -88,7 +95,7 @@ class MyUtil {
return
filePath
}
fun
scareImg
(
imgPath
:
String
,
scareSize
:
Float
,
filePath
:
String
,
quality
:
Int
):
String
{
fun
scareImg
(
imgPath
:
String
,
scareSize
:
Float
,
filePath
:
String
,
quality
:
Int
,
degree
:
Int
):
String
{
val
newOpts
=
BitmapFactory
.
Options
()
// 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容
newOpts
.
inJustDecodeBounds
=
true
...
...
@@ -123,7 +130,16 @@ class MyUtil {
val
file
=
File
(
filePath
)
try
{
val
out
=
FileOutputStream
(
file
)
if
(
degree
!=
0
){
matrix
.
reset
()
matrix
.
postRotate
(
degree
.
toFloat
());
val
resizedBitmap
=
Bitmap
.
createBitmap
(
bitmap
,
0
,
0
,
bitmap
.
width
,
bitmap
.
height
,
matrix
,
true
)
resizedBitmap
.
compress
(
Bitmap
.
CompressFormat
.
PNG
,
quality
,
out
)
resizedBitmap
.
recycle
()
}
else
{
bitmap
.
compress
(
Bitmap
.
CompressFormat
.
PNG
,
quality
,
out
)
}
out
.
flush
()
out
.
close
()
bitmap
.
recycle
()
...
...
android/src/main/res/layout/preview_act.xml
0 → 100644
View file @
f0733818
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:id=
"@+id/main"
android:orientation=
"vertical"
>
<View
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
android:layout_weight=
"1"
/>
<com.example.gengmei_flutter_plugin.act.view.PinchImageView
android:id=
"@+id/image"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
/>
<View
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
android:layout_weight=
"1"
/>
</LinearLayout>
\ No newline at end of file
example/lib/AlbumModel/page/album/AlbumModel.dart
View file @
f0733818
...
...
@@ -385,15 +385,16 @@ class AlbumModel {
if
(
albumLive
.
data
[
index
].
isVideo
)
{
GengmeiFlutterPlugin
.
playAlbumVideo
(
path
);
}
else
{
if
(
previewItemClick
)
{
return
;
}
previewItemClick
=
true
;
iosItem
(
path
,
context
,
(
value
)
{
var
realPath
=
value
[
"realImagePath"
];
Navigator
.
push
(
context
,
CustomRoute
(
AlbumPreviewPage
(
realPath
,
pageName
)));
});
GengmeiFlutterPlugin
.
previewImage
(
path
);
// if (previewItemClick) {
// return;
// }
// previewItemClick = true;
// iosItem(path, context, (value) {
// var realPath = value["realImagePath"];
// Navigator.push(
// context, CustomRoute(AlbumPreviewPage(realPath, pageName)));
// });
}
// Navigator.push(
// context, CustomRoute(AlbumPreviewPage(albumLive.data[index].path, pageName)));
...
...
@@ -401,14 +402,14 @@ class AlbumModel {
}
void
iosItem
(
String
path
,
BuildContext
context
,
Function
fun
)
{
GengmeiFlutterPlugin
.
ios_album_item
(
path
).
then
((
value
)
{
if
(
value
!=
null
)
{
fun
(
Map
<
String
,
String
>.
from
(
value
));
}
previewItemClick
=
false
;
}).
catchError
((
error
)
{
previewItemClick
=
false
;
print
(
error
);
});
//
GengmeiFlutterPlugin.ios_album_item(path).then((value) {
//
if (value != null) {
//
fun(Map<String, String>.from(value));
//
}
//
previewItemClick = false;
//
}).catchError((error) {
//
previewItemClick = false;
//
print(error);
//
});
}
}
ios/Classes/GengmeiFlutterPlugin.m
View file @
f0733818
...
...
@@ -6,6 +6,7 @@
#import <ImageIO/ImageIO.h>
#import <AVKit/AVKit.h>
#import "MyPlayerViewController.h"
#import "MyPreviewController.h"
@interface
GengmeiFlutterPlugin
()
<
UIActionSheetDelegate
,
UIImagePickerControllerDelegate
,
UINavigationControllerDelegate
,
FlutterStreamHandler
>
//一定要声明这三个协议,缺一不可
//@property(nonatomic)FlutterResult result;
...
...
@@ -350,7 +351,7 @@ NSString *cacheDirectory;
}
else
{
[[
ResultManager
sharedSingleton
]
resultSuccess
:[
NSNumber
numberWithLong
:
resultTemp
]
:
nil
];
}
}
else
if
([
@"
IOS_IMAGE_ITEM
"
isEqualToString
:
call
.
method
]){
}
else
if
([
@"
PREVIEW_IMAGE
"
isEqualToString
:
call
.
method
]){
long
resultTemp
=
self
.
resultKey
;
NSString
*
path
=
call
.
arguments
;
PHImageRequestOptions
*
imageRequestOption
=
[[
PHImageRequestOptions
alloc
]
init
];
...
...
@@ -365,50 +366,59 @@ NSString *cacheDirectory;
NSString
*
name
=
[
path
lastPathComponent
];
NSString
*
tempTake
=
[
tempPath
stringByAppendingPathComponent
:[
NSString
stringWithFormat
:
@"%@%@"
,
name
,
@"_preview"
]];
PHAsset
*
assets
=
self
.
scanMap
[
path
];
if
([
fileManager
fileExistsAtPath
:
tempTake
]){
NSMutableDictionary
*
dict
=
[[
NSMutableDictionary
alloc
]
init
];
[
dict
setObject
:
path
forKey
:
@"path"
];
[
dict
setObject
:
tempTake
forKey
:
@"realImagePath"
];
[[
ResultManager
sharedSingleton
]
resultSuccess
:[
NSNumber
numberWithLong
:
resultTemp
]
:
dict
];
}
else
{
dispatch_async
(
queue
,
^
{
CFAbsoluteTime
start
=
CFAbsoluteTimeGetCurrent
();
int
picWidth
=
[
assets
pixelWidth
];
int
picHeight
=
[
assets
pixelHeight
];
float
tempScareSize
=
1
;
float
limit
=
1024
.
0
;
float
max
=
MAX
(
picWidth
,
picHeight
);
if
(
max
>
limit
){
tempScareSize
=
limit
/
max
;
}
CGSize
temp
=
CGSizeMake
(
picWidth
*
tempScareSize
,
picHeight
*
tempScareSize
);
__block
bool
isResult
=
false
;
[[
PHImageManager
defaultManager
]
requestImageForAsset
:
assets
targetSize
:
temp
contentMode
:
PHImageContentModeDefault
options
:
imageRequestOption
resultHandler
:^
(
UIImage
*
_Nullable
result
,
NSDictionary
*
_Nullable
info
)
{
if
(
isResult
){
return
;
}
isResult
=
true
;
@autoreleasepool
{
NSData
*
data
=
UIImageJPEGRepresentation
(
result
,
0
.
7
)
;
[
data
writeToFile
:
tempTake
atomically
:
YES
];
// result=nil;
// data=nil;
}
NSMutableDictionary
*
dict
=
[[
NSMutableDictionary
alloc
]
init
];
[
dict
setObject
:
path
forKey
:
@"path"
];
[
dict
setObject
:
tempTake
forKey
:
@"realImagePath"
];
dispatch_async
(
dispatch_get_main_queue
(),
^
{
NSLog
(
@"压缩预览图片耗时:%f ms Temp路径%@"
,(
CFAbsoluteTimeGetCurrent
()
-
start
)
*
1000
,
tempTake
);
[[
ResultManager
sharedSingleton
]
resultSuccess
:[
NSNumber
numberWithLong
:
resultTemp
]
:
dict
];
});
}];
});
}
MyPreviewController
*
video
=
[[
MyPreviewController
alloc
]
init
];
[
viewController
presentViewController
:
video
animated
:
YES
completion
:
nil
];
[
video
setImagePre
:
assets
];
[[
ResultManager
sharedSingleton
]
resultSuccess
:[
NSNumber
numberWithLong
:
resultTemp
]
:
@YES
];
// if([fileManager fileExistsAtPath:tempTake]){
// NSMutableDictionary *dict=[[NSMutableDictionary alloc] init];
// [dict setObject:path forKey:@"path"];
// [dict setObject:tempTake forKey:@"realImagePath"];
// [[ResultManager sharedSingleton] resultSuccess:[NSNumber numberWithLong:resultTemp] :dict];
// }else{
// dispatch_async(queue, ^{
// CFAbsoluteTime start=CFAbsoluteTimeGetCurrent();
// int picWidth=[assets pixelWidth];
// int picHeight=[assets pixelHeight];
// float tempScareSize=1;
// float limit=1024.0;
// float max=MAX(picWidth, picHeight);
// if(max>limit){
// tempScareSize=limit/max;
// }
// CGSize temp=CGSizeMake(picWidth*tempScareSize, picHeight*tempScareSize);
// __block bool isResult=false;
// [[PHImageManager defaultManager] requestImageForAsset:assets targetSize:temp contentMode:PHImageContentModeDefault options:imageRequestOption resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
// if(isResult){
// return ;
// }
// isResult=true;
// @autoreleasepool{
// NSData *data = UIImageJPEGRepresentation(result, 0.7) ;
// [data writeToFile:tempTake atomically:YES];
// // result=nil;
// // data=nil;
// }
// NSMutableDictionary *dict=[[NSMutableDictionary alloc] init];
// [dict setObject:path forKey:@"path"];
// [dict setObject:tempTake forKey:@"realImagePath"];
// dispatch_async(dispatch_get_main_queue(), ^{
// NSLog(@"压缩预览图片耗时:%f ms Temp路径%@",(CFAbsoluteTimeGetCurrent()-start)*1000,tempTake);
// [[ResultManager sharedSingleton] resultSuccess:[NSNumber numberWithLong:resultTemp] :dict];
// });
// }];
// });
// }
}
else
if
([[
self
.
takePhotoMap
allKeys
]
containsObject
:
path
]){
NSMutableDictionary
*
dict
=
[[
NSMutableDictionary
alloc
]
init
];
[
dict
setObject
:
path
forKey
:
@"path"
];
[
dict
setObject
:
self
.
takePhotoMap
[
path
]
forKey
:
@"realImagePath"
];
[[
ResultManager
sharedSingleton
]
resultSuccess
:[
NSNumber
numberWithLong
:
resultTemp
]
:
dict
];
MyPreviewController
*
video
=
[[
MyPreviewController
alloc
]
init
];
[
viewController
presentViewController
:
video
animated
:
YES
completion
:
nil
];
[
video
setImagePrePath
:
self
.
takePhotoMap
[
path
]];
[[
ResultManager
sharedSingleton
]
resultSuccess
:[
NSNumber
numberWithLong
:
resultTemp
]
:
@YES
];
// NSMutableDictionary *dict=[[NSMutableDictionary alloc] init];
// [dict setObject:path forKey:@"path"];
// [dict setObject:self.takePhotoMap[path] forKey:@"realImagePath"];
// [[ResultManager sharedSingleton] resultSuccess:[NSNumber numberWithLong:resultTemp] :dict];
}
else
{
[[
ResultManager
sharedSingleton
]
resultSuccess
:[
NSNumber
numberWithLong
:
resultTemp
]
:
nil
];
}
...
...
@@ -491,9 +501,17 @@ NSString *cacheDirectory;
NSString
*
tmpPath
=
[
cacheDirectory
stringByAppendingPathComponent
:
tmpFile
];
[
data
writeToFile
:
tmpPath
atomically
:
YES
];
dispatch_async
(
dispatch_get_main_queue
(),
^
{
NSMutableArray
*
arr
=
self
.
finalMap
[
@"GengmeiAlbum"
];
if
(
arr
==
nil
){
self
.
finalMap
[
@"GengmeiAlbum"
]
=
[
NSMutableArray
new
];
}
NSMutableDictionary
*
dict
=
[[
NSMutableDictionary
alloc
]
init
];
[
dict
setObject
:
tmpPath
forKey
:
@"path"
];
[
dict
setObject
:
@"F"
forKey
:
@"isVideo"
];
[
dict
setObject
:[
NSString
stringWithFormat
:
@"%ld"
,
(
long
)[[
NSDate
date
]
timeIntervalSince1970
]
*
1000
]
forKey
:
@"dataToken"
];
[
dict
setObject
:
@"GengmeiAlbum"
forKey
:
@"folderName"
];
[
self
.
finalMap
[
@"GengmeiAlbum"
]
insertObject
:
dict
atIndex
:
0
];
[
self
.
finalMap
[
@"IsGengmeiAlbumAllImages"
]
insertObject
:
dict
atIndex
:
0
];
[
self
.
takePhotoMap
setObject
:
tempTake
forKey
:
tmpPath
];
[[
ResultManager
sharedSingleton
]
resultSuccess
:[
NSNumber
numberWithLong
:
self
.
nativeCameraKey
]
:
dict
];
});
...
...
@@ -672,9 +690,21 @@ CFAbsoluteTime startTime;
}
else
{
[
self
.
finalMap
[
docName
]
addObject
:
queryItemDict
];
}
bool
haveIt
=
false
;
NSMutableArray
*
allArr
=
self
.
finalMap
[
@"IsGengmeiAlbumAllImages"
];
for
(
int
x
=
0
;
x
<
[
allArr
count
];
x
++
)
{
NSString
*
inPath
=
allArr
[
x
][
@"path"
];
NSString
*
outPath
=
queryItemDict
[
@"path"
];
if
([
inPath
isEqualToString
:
outPath
])
{
haveIt
=
true
;
break
;
}
}
if
(
!
haveIt
){
[
self
.
finalMap
[
@"IsGengmeiAlbumAllImages"
]
addObject
:
queryItemDict
];
if
(
self
.
needSize
>
200
){
if
(
self
.
copySize
<=
201
){
}
if
(
self
.
needSize
>
300
){
if
(
self
.
copySize
<=
301
){
NSMutableArray
*
arr
=
self
.
finalMapTemp
[
docName
];
if
(
arr
==
nil
)
{
self
.
finalMapTemp
[
docName
]
=
[
NSMutableArray
array
];
...
...
@@ -684,13 +714,13 @@ CFAbsoluteTime startTime;
}
[
self
.
finalMapTemp
[
@"IsGengmeiAlbumAllImages"
]
insertObject
:
queryItemDict
atIndex
:
self
.
nowSize
-
1
];
}
if
(
self
.
copySize
==
2
01
){
if
(
self
.
copySize
==
3
01
){
[
self
reslutImg
:
resultId
];
}
else
if
(
self
.
nowSize
==
self
.
needSize
){
self
.
finishScanImg
=
true
;
CFAbsoluteTime
linkTime
=
(
CFAbsoluteTimeGetCurrent
()
-
startTime
);
NSLog
(
@"IOS COPY IMAGE 时间:%f ms"
,
linkTime
*
1000
.
0
);
if
(
self
.
copySize
<
210
){
if
(
self
.
copySize
<
301
){
[
self
reslutImgFinalMap
:
resultId
];
}
else
{
[
self
upImgs
];
...
...
ios/Classes/MyPlayerViewController.m
View file @
f0733818
...
...
@@ -12,7 +12,7 @@
#import <AssetsLibrary/AssetsLibrary.h>
#import <Photos/Photos.h>
@interface
MyPlayerViewController
()
@property
(
nonatomic
)
dispatch_queue_t
queue
;
;
@property
(
nonatomic
)
dispatch_queue_t
queue
;
@end
@implementation
MyPlayerViewController
...
...
@@ -20,7 +20,7 @@
-
(
void
)
viewDidLoad
{
[
super
viewDidLoad
];
self
.
view
.
backgroundColor
=
[
UIColor
whiteColor
];
self
.
queue
=
dispatch_queue_create
(
"com.
gengmei_flutter_plugin
"
,
DISPATCH_QUEUE_SERIAL
);
self
.
queue
=
dispatch_queue_create
(
"com.
GM
"
,
DISPATCH_QUEUE_SERIAL
);
// UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
// button.frame = CGRectMake(100, 100, 100, 100);
// [button setTitle:@"TICK" forState:UIControlStateNormal];
...
...
ios/Classes/MyPreviewController.h
0 → 100644
View file @
f0733818
//
// MyPreviewController.h
// Pods
//
// Created by Apple on 2019/11/3.
//
#ifndef MyPreviewController_h
#define MyPreviewController_h
#endif
/* MyPreviewController_h */
#import <AssetsLibrary/AssetsLibrary.h>
#import <Photos/Photos.h>
@interface
MyPreviewController
:
UIViewController
-
(
void
)
setImagePre
:(
PHAsset
*
)
assets
;
-
(
void
)
setImagePrePath
:(
NSString
*
)
path
;
@end
ios/Classes/MyPreviewController.m
0 → 100644
View file @
f0733818
//
// MyPreviewController.m
// gengmei_flutter_plugin
//
// Created by Apple on 2019/11/3.
//
#import <Foundation/Foundation.h>
#include "MyPreviewController.h"
@interface
MyPreviewController
()
<
UIScrollViewDelegate
>
@property
(
nonatomic
,
strong
)
UIScrollView
*
scrollView
;
@property
(
nonatomic
,
strong
)
UIView
*
titleView
;
@property
(
nonatomic
,
strong
)
UILabel
*
titleLab
;
@property
(
nonatomic
,
strong
)
NSMutableArray
*
playerArray
;
@property
(
nonatomic
,
strong
)
UIImageView
*
videoOverLay
;
@property
(
nonatomic
,
assign
)
NSInteger
index
;
@property
(
nonatomic
,
assign
)
BOOL
isHidden
;
@property
(
nonatomic
,
strong
)
id
timeObserver
;
@property
(
nonatomic
,
strong
)
UIImageView
*
imageView
;
@end
@implementation
MyPreviewController
-
(
void
)
viewDidLoad
{
[
super
viewDidLoad
];
self
.
view
.
backgroundColor
=
[
UIColor
blackColor
];
self
.
isHidden
=
NO
;
_imageView
=
[[
UIImageView
alloc
]
initWithFrame
:
self
.
view
.
bounds
];
_imageView
.
clipsToBounds
=
YES
;
_imageView
.
contentMode
=
UIViewContentModeScaleAspectFit
;
_imageView
.
contentScaleFactor
=
[[
UIScreen
mainScreen
]
scale
];
_imageView
.
backgroundColor
=
[
UIColor
clearColor
];
_imageView
.
tag
=
1000
;
[
self
.
view
addSubview
:
_imageView
];
// [self configUI];
}
-
(
void
)
viewWillAppear
:
(
BOOL
)
animated
{
[
super
viewWillAppear
:
animated
];
[
self
.
navigationController
setNavigationBarHidden
:
YES
animated
:
NO
];
}
-
(
void
)
viewWillDisappear
:
(
BOOL
)
animated
{
[
super
viewWillDisappear
:
animated
];
[
self
.
navigationController
setNavigationBarHidden
:
NO
animated
:
NO
];
}
#pragma mark - 手势处理
-
(
void
)
doubleTapGestureCallback
:
(
UITapGestureRecognizer
*
)
gesture
{
[
self
resetIndex
];
UIScrollView
*
scrollView
=
[
_scrollView
viewWithTag
:
100
+
_index
];
CGFloat
zoomScale
=
scrollView
.
zoomScale
;
if
(
zoomScale
==
scrollView
.
maximumZoomScale
)
{
zoomScale
=
0
;
}
else
{
zoomScale
=
scrollView
.
maximumZoomScale
;
}
[
UIView
animateWithDuration
:
0
.
35
animations
:
^
{
scrollView
.
zoomScale
=
zoomScale
;
}];
}
#pragma mark - 图像加载|移除
-
(
void
)
setImagePrePath
:
(
NSString
*
)
path
{
self
->
_imageView
.
image
=
[
UIImage
imageWithData
:[
NSData
dataWithContentsOfFile
:
path
]];
}
-
(
void
)
setImagePre
:
(
PHAsset
*
)
asset
{
PHImageRequestOptions
*
imageRequestOption
=
[[
PHImageRequestOptions
alloc
]
init
];
imageRequestOption
.
synchronous
=
NO
;
imageRequestOption
.
networkAccessAllowed
=
YES
;
imageRequestOption
.
deliveryMode
=
PHImageRequestOptionsDeliveryModeHighQualityFormat
;
imageRequestOption
.
resizeMode
=
PHImageRequestOptionsResizeModeFast
;
imageRequestOption
.
version
=
PHImageRequestOptionsVersionUnadjusted
;
int
picWidth
=
[
asset
pixelWidth
];
int
picHeight
=
[
asset
pixelHeight
];
float
tempScareSize
=
1
;
float
limit
=
1560
.
0
;
float
max
=
MAX
(
picWidth
,
picHeight
);
if
(
max
>
limit
){
tempScareSize
=
limit
/
max
;
}
CGSize
temp
=
CGSizeMake
(
picWidth
*
tempScareSize
,
picHeight
*
tempScareSize
);
[[
PHImageManager
defaultManager
]
requestImageForAsset
:
asset
targetSize
:
temp
contentMode
:
PHImageContentModeDefault
options
:
imageRequestOption
resultHandler
:^
(
UIImage
*
_Nullable
result
,
NSDictionary
*
_Nullable
info
)
{
self
->
_imageView
.
image
=
result
;
}];
}
-
(
void
)
resetIndex
{
CGFloat
pageWidth
=
_scrollView
.
frame
.
size
.
width
;
_index
=
floor
((
_scrollView
.
contentOffset
.
x
-
pageWidth
/
2
)
/
pageWidth
)
+
1
;
}
#pragma mark - UIScrollViewDelegate
-
(
UIView
*
)
viewForZoomingInScrollView
:
(
UIScrollView
*
)
scrollView
{
return
[
scrollView
viewWithTag
:
1000
];
}
#pragma mark -
-
(
void
)
didReceiveMemoryWarning
{
[
super
didReceiveMemoryWarning
];
}
@end
lib/ScanImagePlugn.dart
View file @
f0733818
...
...
@@ -47,13 +47,13 @@ class ScanImagePlugn {
return
await
channel
.
invokeMethod
(
"IOS_IMAGE_BY_PATH"
,
path
);
}
static
Future
<
Map
>
ios_album_item
(
MethodChannel
channel
,
String
path
)
async
{
return
await
channel
.
invokeMethod
(
"IOS_IMAGE_ITEM"
,
path
);
}
static
Future
<
bool
>
playAlbumVideo
(
MethodChannel
channel
,
String
path
)
async
{
return
await
channel
.
invokeMethod
(
"play_album_video"
,
path
);
}
static
Future
<
bool
>
previewImage
(
MethodChannel
channel
,
String
path
)
async
{
return
await
channel
.
invokeMethod
(
"PREVIEW_IMAGE"
,
path
);
}
}
class
ScanImageItem
{
...
...
lib/gengmei_flutter_plugin.dart
View file @
f0733818
...
...
@@ -27,8 +27,8 @@ class GengmeiFlutterPlugin {
return
await
ScanImagePlugn
.
ios_album_path
(
_channel
,
path
);
}
static
Future
<
Map
>
ios_album_item
(
String
path
)
async
{
return
await
ScanImagePlugn
.
ios_album_item
(
_channel
,
path
);
static
Future
<
bool
>
previewImage
(
String
path
)
async
{
return
await
ScanImagePlugn
.
previewImage
(
_channel
,
path
);
}
static
Future
<
bool
>
playAlbumVideo
(
String
path
)
async
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment