├── .gitignore ├── .idea ├── caches │ ├── build_file_checksums.ser │ └── gradle_models.ser ├── codeStyles │ └── Project.xml ├── encodings.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── androidX-README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── photoLibrary ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── awen │ │ └── photo │ │ ├── Awen.java │ │ ├── FrescoBaseActivity.java │ │ ├── FrescoImageLoader.java │ │ ├── controller │ │ └── ToolBarHelper.java │ │ └── photopick │ │ ├── adapter │ │ ├── PhotoGalleryAdapter.java │ │ └── PhotoPickAdapter.java │ │ ├── bean │ │ ├── PageReferenceBean.java │ │ ├── Photo.java │ │ ├── PhotoDirectory.java │ │ ├── PhotoPagerBean.java │ │ ├── PhotoPickBean.java │ │ ├── PhotoPreviewBean.java │ │ └── PhotoResultBean.java │ │ ├── controller │ │ ├── PhotoPagerConfig.java │ │ ├── PhotoPickConfig.java │ │ └── PhotoPreviewConfig.java │ │ ├── data │ │ └── Data.java │ │ ├── loader │ │ ├── MediaStoreHelper.java │ │ ├── MediaType.java │ │ ├── PhotoCursorLoader.java │ │ ├── PhotoDirectoryLoader.java │ │ └── VideoCursorLoader.java │ │ ├── ui │ │ ├── ClipPictureActivity.java │ │ ├── PhotoPagerActivity.java │ │ ├── PhotoPickActivity.java │ │ ├── PhotoPreviewActivity.java │ │ ├── VideoPlayActivity.java │ │ └── VideoPlayLayout.java │ │ ├── util │ │ ├── AppPathUtil.java │ │ ├── BitmapUtil.java │ │ ├── FileSizeUtil.java │ │ ├── ImagePipelineConfigFactory.java │ │ ├── ImageUtils.java │ │ ├── PermissionUtil.java │ │ └── ViewUtil.java │ │ └── widget │ │ ├── HackyViewPager.java │ │ ├── RoundProgressBar.java │ │ ├── RoundProgressBarDrawable.java │ │ ├── ScalePhotoView.java │ │ ├── clipimage │ │ ├── ClipImageBorderView.java │ │ ├── ClipImageLayout.java │ │ └── ClipZoomImageView.java │ │ └── photodraweeview │ │ ├── Attacher.java │ │ ├── DefaultOnDoubleTapListener.java │ │ ├── IAttacher.java │ │ ├── OnPhotoTapListener.java │ │ ├── OnScaleChangeListener.java │ │ ├── OnScaleDragGestureListener.java │ │ ├── OnTouchEventAndScaleChangeListener.java │ │ ├── OnViewTapListener.java │ │ ├── PhotoDraweeView.java │ │ └── ScaleDragDetector.java │ └── res │ ├── anim │ ├── bottom_in.xml │ ├── bottom_out.xml │ ├── image_pager_enter_animation.xml │ └── image_pager_exit_animation.xml │ ├── layout │ ├── activity_clipicture.xml │ ├── activity_photo_detail_pager.xml │ ├── activity_photo_pick.xml │ ├── activity_photo_select.xml │ ├── activity_video_play.xml │ ├── item_photo_gallery.xml │ ├── item_photo_pick.xml │ └── toolbar_layout.xml │ ├── menu │ ├── menu_ok.xml │ └── menu_pick.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ ├── failure_image.jpg │ ├── gif_icon.png │ ├── ic_launcher.png │ ├── select_icon.png │ ├── take_photo.png │ ├── video_icon.png │ └── video_play_icon.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-v21 │ └── dimens.xml │ ├── values-v26 │ └── styles.xml │ ├── values-v27 │ └── styles.xml │ ├── values-v29 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── pictrue ├── 304079-052c8fd0c9d22efd.gif ├── 304079-8d726553c6c0b6ba.gif ├── 304079-e4c819f695ed83c0.gif ├── WechatIMG20.jpeg ├── WechatIMG21.jpeg └── device-2017-10-25-033458.png ├── projectFilesBackup └── .idea │ └── workspace.xml ├── settings.gradle ├── simaple ├── .gitignore ├── build.gradle ├── keystore │ ├── keystore密码 │ └── photo.jks ├── proguard-rules.pro ├── simaple-release.apk └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── test.jpeg │ ├── java │ └── com │ │ └── simaple │ │ ├── App.java │ │ ├── CustomInsetsRelativeLayout.java │ │ ├── ImageProvider.java │ │ ├── MyPhotoBean.java │ │ ├── UserBean.kt │ │ └── activity │ │ ├── CustomPhotoPageActivity.kt │ │ ├── MainActivity.java │ │ └── MyPhotoPagerActivity.java │ └── res │ ├── layout │ ├── activity_custom_pager.xml │ ├── activity_kt_custom_page.xml │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── resx.jpg │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── v1.096-README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/caches/gradle_models.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/.idea/caches/gradle_models.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 21 | 22 | 23 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | xmlns:android 32 | 33 | ^$ 34 | 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 | xmlns:.* 43 | 44 | ^$ 45 | 46 | 47 | BY_NAME 48 | 49 |
50 |
51 | 52 | 53 | 54 | .*:id 55 | 56 | http://schemas.android.com/apk/res/android 57 | 58 | 59 | 60 |
61 |
62 | 63 | 64 | 65 | .*:name 66 | 67 | http://schemas.android.com/apk/res/android 68 | 69 | 70 | 71 |
72 |
73 | 74 | 75 | 76 | name 77 | 78 | ^$ 79 | 80 | 81 | 82 |
83 |
84 | 85 | 86 | 87 | style 88 | 89 | ^$ 90 | 91 | 92 | 93 |
94 |
95 | 96 | 97 | 98 | .* 99 | 100 | ^$ 101 | 102 | 103 | BY_NAME 104 | 105 |
106 |
107 | 108 | 109 | 110 | .* 111 | 112 | http://schemas.android.com/apk/res/android 113 | 114 | 115 | ANDROID_ATTRIBUTE_ORDER 116 | 117 |
118 |
119 | 120 | 121 | 122 | .* 123 | 124 | .* 125 | 126 | 127 | BY_NAME 128 | 129 |
130 |
131 |
132 |
133 |
134 |
-------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.4.0' 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:4.0.0' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | mavenCentral() 22 | jcenter() 23 | maven { url 'https://jitpack.io' } 24 | } 25 | } 26 | 27 | ext { 28 | android = [ 29 | compileSdkVersion : 29, //根据自己项目的来配置 30 | buildToolsVersion : '29.0.3', //根据自己项目的来配置 31 | targetSdkVersion : 29, //根据自己项目的来配置 32 | androidxVersion : '1.0.0', //android support version 33 | frescoVersion : '2.2.0' //fresco的版本,可自行修改 34 | ] 35 | } 36 | 37 | task clean(type: Delete) { 38 | delete rootProject.buildDir 39 | } 40 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | systemProp.http.proxyHost=mirrors.opencas.cn 20 | systemProp.http.proxyPort=80 21 | android.useAndroidX=true 22 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jul 01 17:17:18 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /photoLibrary/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /photoLibrary/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | group='com.github.Awent' 4 | 5 | android { 6 | compileSdkVersion rootProject.ext.android.compileSdkVersion 7 | buildToolsVersion rootProject.ext.android.buildToolsVersion 8 | 9 | defaultConfig { 10 | minSdkVersion 16 11 | targetSdkVersion rootProject.ext.android.targetSdkVersion 12 | versionCode 33 13 | versionName "3.3" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(dir: 'libs', include: ['*.jar']) 25 | implementation "androidx.appcompat:appcompat:1.2.0" 26 | implementation "androidx.recyclerview:recyclerview:1.1.0" 27 | implementation "com.facebook.fresco:fresco:2.3.0" 28 | // 支持 GIF 动图,需要添加 29 | implementation "com.facebook.fresco:animated-gif:2.3.0" 30 | // 支持 WebP (静态图+动图),需要添加 31 | implementation "com.facebook.fresco:animated-webp:2.3.0" 32 | implementation "com.facebook.fresco:webpsupport:2.3.0" 33 | //跟随viewpager的点 34 | implementation 'me.relex:circleindicator:1.1.8@aar' 35 | //上滑控制面板,项目中的potopick中有使用案例 36 | implementation 'com.sothree.slidinguppanel:library:3.3.0' 37 | //android6.0权限工具类 38 | implementation 'com.lovedise:permissiongen:0.1.1' 39 | //加载超长图必备库 40 | implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0' 41 | implementation "androidx.legacy:legacy-support-v4:1.0.0" 42 | } 43 | -------------------------------------------------------------------------------- /photoLibrary/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\android\android-studio\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | 20 | # Explicitly preserve all serialization members. The Serializable interface 21 | # is only a marker interface, so it wouldn't save them. 22 | 23 | -------------------------------------------------------------------------------- /photoLibrary/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/Awen.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.ColorInt; 6 | import androidx.annotation.ColorRes; 7 | 8 | import com.awen.photo.photopick.util.FileSizeUtil; 9 | import com.awen.photo.photopick.util.ImagePipelineConfigFactory; 10 | import com.facebook.drawee.backends.pipeline.Fresco; 11 | import com.facebook.imagepipeline.core.ImagePipelineConfig; 12 | 13 | /** 14 | * Created by Awen 15 | */ 16 | 17 | public class Awen { 18 | 19 | protected Awen() { 20 | } 21 | 22 | @ColorRes 23 | private static int toolbarBackGround; 24 | private static Context mContext; 25 | /** 26 | * 保存图片到本地的地址 27 | */ 28 | private static String path; 29 | 30 | public static void init(Context context) { 31 | init(context, android.R.color.holo_red_light); 32 | } 33 | 34 | public static void init(Context context, @ColorRes int toolbarBackGroundId) { 35 | init(context, toolbarBackGroundId, null); 36 | } 37 | 38 | /** 39 | * @param context 40 | * @param toolbarBackGroundId 41 | * @param saveImageLocalPath 保存图片的路径地址,可以不设置 42 | */ 43 | public static void init(Context context, @ColorRes int toolbarBackGroundId, String saveImageLocalPath) { 44 | init(context, toolbarBackGroundId, saveImageLocalPath, ImagePipelineConfigFactory.getImagePipelineConfig(context)); 45 | } 46 | 47 | /** 48 | * @param context 49 | * @param toolbarBackGroundId 50 | * @param saveImageLocalPath 保存图片的路径地址,可以不设置 51 | * @param imagePipelineConfig Fresco初始化配置 52 | */ 53 | public static void init(Context context, @ColorRes int toolbarBackGroundId, String saveImageLocalPath, ImagePipelineConfig imagePipelineConfig) { 54 | if (mContext != null) {//说明已经初始化过了,不用重复初始化 55 | return; 56 | } 57 | //这里用facebook提供的开源图片处理框架,详细用法可参考文档:http://www.fresco-cn.org/ 58 | //或者:http://blog.csdn.net/android_ls/article/details/53137867 59 | //或者:http://blog.csdn.net/wa991830558/article/details/46005063 60 | //或者:http://www.lxway.com/4406818246.htm 61 | if (!Fresco.hasBeenInitialized()) { 62 | Fresco.initialize(context, imagePipelineConfig); 63 | } 64 | toolbarBackGround = toolbarBackGroundId; 65 | mContext = context.getApplicationContext(); 66 | path = saveImageLocalPath; 67 | } 68 | 69 | public static void checkInit() { 70 | if (mContext == null) { 71 | throw new NullPointerException("photoLibrary was not initialized,please init in your Application"); 72 | } 73 | } 74 | 75 | public static void destroy() { 76 | mContext = null; 77 | path = null; 78 | } 79 | 80 | @ColorInt 81 | public static int getToolbarBackGround() { 82 | return mContext.getResources().getColor(toolbarBackGround); 83 | } 84 | 85 | public static Context getContext() { 86 | return mContext; 87 | } 88 | 89 | public static String getSaveImageLocalPath() { 90 | return path; 91 | } 92 | 93 | /** 94 | * 设置toolbar颜色 95 | * 96 | * @param toolbarBackGroundId resId 97 | */ 98 | public static void setToolbarBackGround(@ColorRes int toolbarBackGroundId) { 99 | toolbarBackGround = toolbarBackGroundId; 100 | } 101 | 102 | /** 103 | * 查看网络大图时,你想把图片保存的地址,保存的图片是可以在手机图库可以看到的 104 | * 105 | * @param saveImageLocalPath 大图存储的地址 106 | */ 107 | public static void setSaveImageLocalPath(String saveImageLocalPath) { 108 | path = saveImageLocalPath; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/FrescoBaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo; 2 | 3 | import android.annotation.TargetApi; 4 | import android.graphics.Color; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.view.MenuItem; 8 | import android.view.View; 9 | import android.view.Window; 10 | import android.view.WindowManager; 11 | 12 | import androidx.annotation.ColorRes; 13 | import androidx.annotation.Nullable; 14 | import androidx.appcompat.app.AppCompatActivity; 15 | import androidx.appcompat.widget.Toolbar; 16 | 17 | import com.awen.photo.controller.ToolBarHelper; 18 | 19 | /** 20 | * Created by Awen 21 | */ 22 | public class FrescoBaseActivity extends AppCompatActivity { 23 | 24 | public Toolbar toolbar; 25 | private boolean openToolBar = true;//true:开启toolbar,false:不开启 26 | private boolean openNavigationBar; 27 | 28 | @Override 29 | protected void onCreate(@Nullable Bundle arg0) { 30 | super.onCreate(arg0); 31 | // 5.0系统以上才开启沉浸式状态栏 32 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 33 | Window window = getWindow(); 34 | if (openNavigationBar) { 35 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); 36 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 37 | window.setNavigationBarColor(Color.TRANSPARENT); 38 | } else { 39 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 40 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 41 | } 42 | window.setStatusBarColor(Color.TRANSPARENT); 43 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 44 | } 45 | } 46 | 47 | /** 48 | * 1、从写此方法,如果{@link FrescoBaseActivity#openToolBar} is true,open toolBar

49 | * 这里默认开启ToolBar,如果不需要开启的在{@link #setContentView(int)}之前调用这个:{@code openToolBar = false;},注意,如果不开启ToolBar,不要调用这里的toolBar对象 50 | * 51 | * @param layoutResID 52 | */ 53 | public void setContentView(int layoutResID) { 54 | if (openToolBar) { 55 | ToolBarHelper mToolBarHelper = new ToolBarHelper(this, layoutResID); 56 | toolbar = mToolBarHelper.getToolBar(); 57 | setContentView(mToolBarHelper.getContentView()); 58 | //把toolbar设置到Activity中 59 | toolbar.setTitle(R.string.app_name); 60 | toolbar.setBackgroundColor(Awen.getToolbarBackGround()); 61 | setSupportActionBar(toolbar); 62 | } else { 63 | setContentView(View.inflate(this, layoutResID, null)); 64 | } 65 | } 66 | 67 | @Override 68 | public boolean onOptionsItemSelected(MenuItem item) { 69 | if (item.getItemId() == android.R.id.home) { 70 | finish(); 71 | return true; 72 | } 73 | return super.onOptionsItemSelected(item); 74 | } 75 | 76 | public void setOpenToolBar(boolean openToolBar) { 77 | this.openToolBar = openToolBar; 78 | } 79 | 80 | public void setOpenNavigationBar(boolean openNavigationBar) { 81 | this.openNavigationBar = openNavigationBar; 82 | } 83 | 84 | @TargetApi(21) 85 | public void setNavigationBarColor(@ColorRes int navigationBarColor){ 86 | getWindow().setNavigationBarColor(getResources().getColor(navigationBarColor)); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/FrescoImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.net.Uri; 7 | import android.text.TextUtils; 8 | import android.util.Log; 9 | 10 | import com.awen.photo.photopick.ui.VideoPlayActivity; 11 | import com.awen.photo.photopick.util.FileSizeUtil; 12 | import com.facebook.binaryresource.BinaryResource; 13 | import com.facebook.binaryresource.FileBinaryResource; 14 | import com.facebook.cache.common.CacheKey; 15 | import com.facebook.common.util.UriUtil; 16 | import com.facebook.datasource.DataSource; 17 | import com.facebook.drawee.backends.pipeline.Fresco; 18 | import com.facebook.imagepipeline.cache.DefaultCacheKeyFactory; 19 | import com.facebook.imagepipeline.core.ImagePipeline; 20 | import com.facebook.imagepipeline.core.ImagePipelineFactory; 21 | import com.facebook.imagepipeline.request.ImageRequest; 22 | 23 | import java.io.File; 24 | import java.io.IOException; 25 | 26 | /** 27 | * 以下四个方法可能是你想用的 28 | * 29 | * @see #setToolbarBackGround(int) 30 | * @see #setSaveImageLocalPath(String) 31 | * @see #getSDCacheSizeFormat() 32 | * @see #clearCaches() 33 | * Created by Awen 34 | */ 35 | 36 | public final class FrescoImageLoader extends Awen { 37 | 38 | private FrescoImageLoader() { 39 | } 40 | 41 | public static void evictFromMemoryCache(String url){ 42 | if (url != null && !TextUtils.isEmpty(url)) { 43 | ImagePipeline imagePipeline = Fresco.getImagePipeline(); 44 | Uri uri = Uri.parse(url); 45 | if (imagePipeline.isInBitmapMemoryCache(uri)) { 46 | imagePipeline.evictFromMemoryCache(uri); 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * 图片是否已经存在了 53 | */ 54 | public static boolean isCached(Context context, Uri uri) { 55 | ImagePipeline imagePipeline = Fresco.getImagePipeline(); 56 | DataSource dataSource = imagePipeline.isInDiskCache(uri); 57 | if (dataSource == null) { 58 | return false; 59 | } 60 | ImageRequest imageRequest = ImageRequest.fromUri(uri); 61 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(imageRequest, context); 62 | BinaryResource resource = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey); 63 | return resource != null && dataSource.getResult() != null && dataSource.getResult(); 64 | } 65 | 66 | /** 67 | * 本地缓存文件 68 | */ 69 | public static File getCache(Context context, Uri uri) { 70 | if (!isCached(context, uri)) 71 | return null; 72 | ImageRequest imageRequest = ImageRequest.fromUri(uri); 73 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(imageRequest, context); 74 | BinaryResource resource = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey); 75 | return ((FileBinaryResource) resource).getFile(); 76 | } 77 | 78 | public static File getCache(ImageRequest request) { 79 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(request, null); 80 | BinaryResource bRes = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey); 81 | return ((FileBinaryResource) bRes).getFile(); 82 | } 83 | 84 | public static byte[] getByte(Context context, Uri uri) throws IOException { 85 | if (!isCached(context, uri)) 86 | return null; 87 | ImageRequest imageRequest = ImageRequest.fromUri(uri); 88 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(imageRequest, null); 89 | BinaryResource bRes = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey); 90 | return bRes.read(); 91 | } 92 | 93 | public static byte[] getByte(ImageRequest request) throws IOException { 94 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(request, null); 95 | BinaryResource bRes = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey); 96 | return bRes.read(); 97 | } 98 | 99 | /** 100 | * 清空所有内存缓存 101 | */ 102 | public static void clearMemoryCaches() { 103 | Fresco.getImagePipeline().clearMemoryCaches(); 104 | } 105 | 106 | /** 107 | * 清空所有磁盘缓存 108 | */ 109 | public static void clearDiskCaches() { 110 | Fresco.getImagePipeline().clearDiskCaches(); 111 | } 112 | 113 | /** 114 | * 清除所有缓存(包括内存+磁盘) 115 | */ 116 | public static void clearCaches() { 117 | clearMemoryCaches(); 118 | clearDiskCaches(); 119 | } 120 | 121 | /** 122 | * 获取磁盘上主缓存文件缓存的大小 123 | */ 124 | public static long getMainFileCache() { 125 | Fresco.getImagePipelineFactory().getMainFileCache().trimToMinimum(); 126 | return Fresco.getImagePipelineFactory().getMainFileCache().getSize(); 127 | } 128 | 129 | /** 130 | * 获取磁盘上副缓存(小文件)文件缓存的大小 131 | */ 132 | public static long getSmallImageFileCache() { 133 | Fresco.getImagePipelineFactory().getSmallImageFileCache().trimToMinimum(); 134 | return Fresco.getImagePipelineFactory().getSmallImageFileCache().getSize(); 135 | } 136 | 137 | /** 138 | * 获取磁盘上缓存文件的大小 139 | */ 140 | public static long getSDCacheSize() { 141 | return getMainFileCache() + getSmallImageFileCache(); 142 | } 143 | 144 | /** 145 | * 获取磁盘上缓存文件的大小,带单位,比如:20MB 146 | */ 147 | public static String getSDCacheSizeFormat() { 148 | return FileSizeUtil.formatFileSize(getSDCacheSize()); 149 | } 150 | 151 | public static String getFileUrl(String url) { 152 | return UriUtil.LOCAL_FILE_SCHEME + ":///" + url; 153 | } 154 | 155 | public static String getResUrl(int resId) { 156 | return UriUtil.LOCAL_RESOURCE_SCHEME + ":///" + resId; 157 | } 158 | 159 | public static String getAssetUrl(String assetId) { 160 | return UriUtil.LOCAL_ASSET_SCHEME + ":///" + assetId; 161 | } 162 | 163 | /** 164 | * 启动一个视频播放 165 | * @param activity 166 | * @param videoUrl 本地视频地址或网络视频地址 167 | */ 168 | public static void startVideoActivity(Activity activity, String videoUrl){ 169 | activity.startActivity(new Intent(activity, VideoPlayActivity.class) 170 | .putExtra("videoUrl", videoUrl)); 171 | activity.overridePendingTransition(R.anim.image_pager_enter_animation, 0); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/controller/ToolBarHelper.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.controller; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.LinearLayout; 8 | import androidx.appcompat.widget.Toolbar; 9 | import com.awen.photo.R; 10 | 11 | /** 12 | * Created by Awen 13 | */ 14 | public class ToolBarHelper { 15 | 16 | /*base view*/ 17 | private LinearLayout mContentView; 18 | 19 | /*toolbar*/ 20 | private Toolbar mToolBar; 21 | 22 | public ToolBarHelper(Context context, int layoutId) { 23 | init(context,R.layout.toolbar_layout,layoutId); 24 | } 25 | 26 | public ToolBarHelper(Context context, int toolBarId, int layoutId) { 27 | init(context,toolBarId,layoutId); 28 | } 29 | 30 | private void init(Context context,int toolBarId, int layoutId){ 31 | LayoutInflater mInflater = LayoutInflater.from(context); 32 | /*初始化整个内容-直接创建一个帧布局,作为视图容器的父容器*/ 33 | mContentView = new LinearLayout(context); 34 | ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 35 | ViewGroup.LayoutParams.MATCH_PARENT); 36 | mContentView.setOrientation(LinearLayout.VERTICAL); 37 | mContentView.setLayoutParams(params); 38 | 39 | /*初始化toolbar-通过inflater获取toolbar的布局文件*/ 40 | View toolbar = mInflater.inflate(toolBarId, mContentView); 41 | mToolBar = (Toolbar) toolbar.findViewById(R.id.toolbar); 42 | 43 | /**初始化用户定义的布局*/ 44 | View mUserView = mInflater.inflate(layoutId, null); 45 | mContentView.addView(mUserView,new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 46 | } 47 | 48 | public LinearLayout getContentView() { 49 | return mContentView; 50 | } 51 | 52 | public Toolbar getToolBar() { 53 | return mToolBar; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/adapter/PhotoGalleryAdapter.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.adapter; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.awen.photo.R; 15 | import com.awen.photo.photopick.bean.Photo; 16 | import com.awen.photo.photopick.bean.PhotoDirectory; 17 | import com.facebook.common.util.UriUtil; 18 | import com.facebook.drawee.backends.pipeline.Fresco; 19 | import com.facebook.drawee.interfaces.DraweeController; 20 | import com.facebook.drawee.view.SimpleDraweeView; 21 | import com.facebook.imagepipeline.common.ResizeOptions; 22 | import com.facebook.imagepipeline.request.ImageRequest; 23 | import com.facebook.imagepipeline.request.ImageRequestBuilder; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | /** 29 | * Created by Awen 30 | */ 31 | public class PhotoGalleryAdapter extends RecyclerView.Adapter { 32 | private final String TAG = getClass().getSimpleName(); 33 | private Context context; 34 | private int selected; 35 | private List directories = new ArrayList<>(); 36 | private int imageSize; 37 | 38 | public PhotoGalleryAdapter(Context context) { 39 | this.context = context; 40 | this.imageSize = context.getResources().getDisplayMetrics().widthPixels / 6; 41 | } 42 | 43 | public void refresh(List directories) { 44 | this.directories.clear(); 45 | this.directories.addAll(directories); 46 | notifyDataSetChanged(); 47 | } 48 | 49 | @NonNull 50 | @Override 51 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 52 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_photo_gallery, parent, false); 53 | return new ViewHolder(view); 54 | } 55 | 56 | @Override 57 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 58 | holder.setData(getItem(position), position); 59 | } 60 | 61 | @Override 62 | public int getItemCount() { 63 | return directories.size(); 64 | } 65 | 66 | private PhotoDirectory getItem(int position) { 67 | return this.directories.get(position); 68 | } 69 | 70 | private void changeSelect(int position) { 71 | this.selected = position; 72 | notifyDataSetChanged(); 73 | } 74 | 75 | public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 76 | private SimpleDraweeView imageView; 77 | private ImageView photo_gallery_select; 78 | private TextView name, num; 79 | 80 | ViewHolder(View itemView) { 81 | super(itemView); 82 | imageView = itemView.findViewById(R.id.imageView); 83 | name = itemView.findViewById(R.id.name); 84 | num = itemView.findViewById(R.id.num); 85 | photo_gallery_select = itemView.findViewById(R.id.photo_gallery_select); 86 | imageView.getLayoutParams().height = imageSize; 87 | imageView.getLayoutParams().width = imageSize; 88 | itemView.setOnClickListener(this); 89 | } 90 | 91 | void setData(PhotoDirectory directory, int position) { 92 | if (directory == null || directory.getCoverPath() == null) { 93 | return; 94 | } 95 | if (selected == position) { 96 | photo_gallery_select.setImageResource(R.mipmap.select_icon); 97 | } else { 98 | photo_gallery_select.setImageBitmap(null); 99 | } 100 | name.setText(directory.getName()); 101 | num.setText(context.getString(directory.isVideo() ? R.string.gallery_video_num : R.string.gallery_num, 102 | String.valueOf(directory.getPhotos().size()))); 103 | //不设置.setResizeOptions(new ResizeOptions(imageSize, imageSize))会显示有问题 104 | ImageRequest imageRequest = ImageRequestBuilder 105 | .newBuilderWithSource(new Uri.Builder().scheme(UriUtil.LOCAL_FILE_SCHEME).path(directory.getCoverPath()).build()) 106 | .setLocalThumbnailPreviewsEnabled(true) 107 | .setResizeOptions(new ResizeOptions(imageSize, imageSize)).build(); 108 | DraweeController draweeController = Fresco.newDraweeControllerBuilder() 109 | .setImageRequest(imageRequest) 110 | .setOldController(imageView.getController()) 111 | .build(); 112 | imageView.setController(draweeController); 113 | } 114 | 115 | @Override 116 | public void onClick(View v) { 117 | int position = getAdapterPosition(); 118 | if (v.getId() == R.id.photo_gallery_rl) { 119 | if (onItemClickListener != null) { 120 | changeSelect(position); 121 | onItemClickListener.onClick(getItem(position).getPhotos()); 122 | } 123 | } 124 | } 125 | } 126 | 127 | private OnItemClickListener onItemClickListener; 128 | 129 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) { 130 | this.onItemClickListener = onItemClickListener; 131 | } 132 | 133 | public interface OnItemClickListener { 134 | void onClick(List photos); 135 | } 136 | 137 | public void destroy() { 138 | directories.clear(); 139 | directories = null; 140 | onItemClickListener = null; 141 | context = null; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/bean/PageReferenceBean.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.bean; 2 | 3 | import com.awen.photo.photopick.widget.photodraweeview.PhotoDraweeView; 4 | import com.facebook.imagepipeline.request.ImageRequest; 5 | 6 | public class PageReferenceBean { 7 | 8 | private ImageRequest request; 9 | private PhotoDraweeView photoDraweeView; 10 | 11 | public PageReferenceBean(ImageRequest request, PhotoDraweeView photoDraweeView) { 12 | this.request = request; 13 | this.photoDraweeView = photoDraweeView; 14 | } 15 | 16 | public ImageRequest getRequest() { 17 | return request; 18 | } 19 | 20 | public void setRequest(ImageRequest request) { 21 | this.request = request; 22 | } 23 | 24 | public PhotoDraweeView getPhotoDraweeView() { 25 | return photoDraweeView; 26 | } 27 | 28 | public void setPhotoDraweeView(PhotoDraweeView photoDraweeView) { 29 | this.photoDraweeView = photoDraweeView; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/bean/Photo.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.bean; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by Awen 8 | */ 9 | public class Photo implements Parcelable { 10 | 11 | private int id; 12 | private String path; 13 | private long size;//byte 字节 14 | private boolean isLongPhoto;//是否是超长图 15 | private int width; //图片真实宽度 16 | private int height;//图片真实高度 17 | private String mimeType;//图片类型:image/webp、image/jpeg、image/png、image/gif 18 | private long dateAdd;//添加的时间,用作视频跟图片组合后的排序 19 | private long duration;//视频的时长,毫秒 20 | 21 | public Photo(){} 22 | 23 | public Photo(int id, String path, long size, boolean isLongPhoto, int width, int height, String mimeType, long dateAdd, long duration) { 24 | this.id = id; 25 | this.path = path; 26 | this.size = size; 27 | this.isLongPhoto = isLongPhoto; 28 | this.width = width; 29 | this.height = height; 30 | this.mimeType = mimeType; 31 | this.dateAdd = dateAdd; 32 | this.duration = duration; 33 | } 34 | 35 | protected Photo(Parcel in) { 36 | id = in.readInt(); 37 | path = in.readString(); 38 | size = in.readLong(); 39 | isLongPhoto = in.readByte() != 0; 40 | width = in.readInt(); 41 | height = in.readInt(); 42 | mimeType = in.readString(); 43 | dateAdd = in.readLong(); 44 | duration = in.readLong(); 45 | } 46 | 47 | @Override 48 | public void writeToParcel(Parcel dest, int flags) { 49 | dest.writeInt(id); 50 | dest.writeString(path); 51 | dest.writeLong(size); 52 | dest.writeByte((byte) (isLongPhoto ? 1 : 0)); 53 | dest.writeInt(width); 54 | dest.writeInt(height); 55 | dest.writeString(mimeType); 56 | dest.writeLong(dateAdd); 57 | dest.writeLong(duration); 58 | } 59 | 60 | public static final Creator CREATOR = new Creator() { 61 | @Override 62 | public Photo createFromParcel(Parcel in) { 63 | return new Photo(in); 64 | } 65 | 66 | @Override 67 | public Photo[] newArray(int size) { 68 | return new Photo[size]; 69 | } 70 | }; 71 | 72 | @Override 73 | public int describeContents() { 74 | return 0; 75 | } 76 | 77 | @Override 78 | public boolean equals(Object o) { 79 | if (this == o) return true; 80 | if (!(o instanceof Photo)) return false; 81 | 82 | Photo photo = (Photo) o; 83 | 84 | return id == photo.id; 85 | } 86 | 87 | @Override 88 | public int hashCode() { 89 | return id; 90 | } 91 | 92 | public String getPath() { 93 | return path; 94 | } 95 | 96 | public void setPath(String path) { 97 | this.path = path; 98 | } 99 | 100 | public int getId() { 101 | return id; 102 | } 103 | 104 | public void setId(int id) { 105 | this.id = id; 106 | } 107 | 108 | public long getSize() { 109 | return size; 110 | } 111 | 112 | public void setSize(long size) { 113 | this.size = size; 114 | } 115 | 116 | public boolean isLongPhoto() { 117 | return isLongPhoto; 118 | } 119 | 120 | public void setLongPhoto(boolean longPhoto) { 121 | isLongPhoto = longPhoto; 122 | } 123 | 124 | public int getWidth() { 125 | return width; 126 | } 127 | 128 | public void setWidth(int width) { 129 | this.width = width; 130 | } 131 | 132 | public int getHeight() { 133 | return height; 134 | } 135 | 136 | public void setHeight(int height) { 137 | this.height = height; 138 | } 139 | 140 | public String getMimeType() { 141 | return mimeType; 142 | } 143 | 144 | public void setMimeType(String mimeType) { 145 | this.mimeType = mimeType; 146 | } 147 | 148 | public long getDateAdd() { 149 | return dateAdd; 150 | } 151 | 152 | public void setDateAdd(long dateAdd) { 153 | this.dateAdd = dateAdd; 154 | } 155 | 156 | public long getDuration() { 157 | return duration; 158 | } 159 | 160 | public void setDuration(long duration) { 161 | this.duration = duration; 162 | } 163 | 164 | public boolean isVideo(){ 165 | return mimeType.startsWith("video"); 166 | } 167 | 168 | /** 169 | * 是否是gif图片 170 | * @return true代表是gif 171 | */ 172 | public boolean isGif(){ 173 | return mimeType != null && mimeType.equals("image/gif"); 174 | } 175 | 176 | /** 177 | * 是否是webp图片 178 | * @return true代表是webp 179 | */ 180 | public boolean isWebp(){ 181 | return mimeType != null && mimeType.equals("image/webp"); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoDirectory.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.bean; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * Created by Awen 10 | */ 11 | public class PhotoDirectory { 12 | 13 | private String id; 14 | private String coverPath; 15 | private String name; 16 | private long dateAdded; 17 | private List photos = new ArrayList<>(); 18 | private boolean isVideo; 19 | 20 | @Override 21 | public boolean equals(Object o) { 22 | if (this == o) return true; 23 | if (!(o instanceof PhotoDirectory)) return false; 24 | 25 | PhotoDirectory directory = (PhotoDirectory) o; 26 | if(TextUtils.isEmpty(id)) return false; 27 | if (!id.equals(directory.id)) return false; 28 | return name.equals(directory.name); 29 | } 30 | 31 | @Override 32 | public int hashCode() { 33 | int result = id.hashCode(); 34 | result = 31 * result + name.hashCode(); 35 | return result; 36 | } 37 | 38 | public String getId() { 39 | return id; 40 | } 41 | 42 | public void setId(String id) { 43 | this.id = id; 44 | } 45 | 46 | public String getCoverPath() { 47 | return coverPath; 48 | } 49 | 50 | public void setCoverPath(String coverPath) { 51 | this.coverPath = coverPath; 52 | } 53 | 54 | public String getName() { 55 | return name; 56 | } 57 | 58 | public void setName(String name) { 59 | this.name = name; 60 | } 61 | 62 | public long getDateAdded() { 63 | return dateAdded; 64 | } 65 | 66 | public void setDateAdded(long dateAdded) { 67 | this.dateAdded = dateAdded; 68 | } 69 | 70 | public List getPhotos() { 71 | return photos; 72 | } 73 | 74 | public void setPhotos(List photos) { 75 | this.photos = photos; 76 | } 77 | 78 | public void addPhoto(Photo photo) { 79 | photos.add(photo); 80 | } 81 | 82 | public boolean isVideo() { 83 | return isVideo; 84 | } 85 | 86 | public void setVideo(boolean video) { 87 | isVideo = video; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoPagerBean.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.bean; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.awen.photo.photopick.controller.PhotoPagerConfig; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Created by Awen . 12 | */ 13 | public class PhotoPagerBean implements Parcelable { 14 | 15 | /** 16 | * 是否开启图片保存到本地功能 17 | */ 18 | private boolean saveImage; 19 | /** 20 | * 保存图片到本地的地址 21 | */ 22 | private String saveImageLocalPath; 23 | /** 24 | * 展示第几张图片 25 | */ 26 | private int pagePosition; 27 | /** 28 | * 大图 29 | */ 30 | private ArrayList bigImgUrls; 31 | /** 32 | * 小图,可用于在展示大图前进行小图的展示 33 | */ 34 | private ArrayList smallImgUrls; 35 | /** 36 | * 是否开启下滑关闭activity,默认开启。类似微信的图片浏览,可下滑关闭一样,但是没有图片归位效果 37 | */ 38 | private boolean isOpenDownAnimate; 39 | 40 | private PhotoPagerConfig.Builder.OnPhotoSaveCallback onPhotoSaveCallback; 41 | 42 | public PhotoPagerBean() { 43 | } 44 | 45 | 46 | private PhotoPagerBean(Parcel in) { 47 | saveImage = in.readByte() != 0; 48 | saveImageLocalPath = in.readString(); 49 | pagePosition = in.readInt(); 50 | bigImgUrls = in.createStringArrayList(); 51 | smallImgUrls = in.createStringArrayList(); 52 | isOpenDownAnimate = in.readByte() != 0; 53 | } 54 | 55 | @Override 56 | public void writeToParcel(Parcel dest, int flags) { 57 | dest.writeByte((byte) (saveImage ? 1 : 0)); 58 | dest.writeString(saveImageLocalPath); 59 | dest.writeInt(pagePosition); 60 | dest.writeStringList(bigImgUrls); 61 | dest.writeStringList(smallImgUrls); 62 | dest.writeByte((byte) (isOpenDownAnimate ? 1 : 0)); 63 | } 64 | 65 | public static final Creator CREATOR = new Creator() { 66 | @Override 67 | public PhotoPagerBean createFromParcel(Parcel in) { 68 | return new PhotoPagerBean(in); 69 | } 70 | 71 | @Override 72 | public PhotoPagerBean[] newArray(int size) { 73 | return new PhotoPagerBean[size]; 74 | } 75 | }; 76 | 77 | @Override 78 | public int describeContents() { 79 | return 0; 80 | } 81 | 82 | public boolean isSaveImage() { 83 | return saveImage; 84 | } 85 | 86 | public void setSaveImage(boolean saveImage) { 87 | this.saveImage = saveImage; 88 | } 89 | 90 | public String getSaveImageLocalPath() { 91 | return saveImageLocalPath; 92 | } 93 | 94 | public void setSaveImageLocalPath(String saveImageLocalPath) { 95 | this.saveImageLocalPath = saveImageLocalPath; 96 | } 97 | 98 | public int getPagePosition() { 99 | return pagePosition; 100 | } 101 | 102 | public void setPagePosition(int pagePosition) { 103 | this.pagePosition = pagePosition; 104 | } 105 | 106 | public ArrayList getBigImgUrls() { 107 | return bigImgUrls; 108 | } 109 | 110 | public void setBigImgUrls(ArrayList bigImgUrls) { 111 | this.bigImgUrls = bigImgUrls; 112 | } 113 | 114 | /** 115 | * @deprecated use {@link #getSmallImgUrls()} 116 | */ 117 | public ArrayList getLowImgUrls() { 118 | return smallImgUrls; 119 | } 120 | 121 | /** 122 | * @deprecated use {@link #setSmallImgUrls(ArrayList)} 123 | */ 124 | public void setLowImgUrls(ArrayList lowImgUrls) { 125 | setSmallImgUrls(lowImgUrls); 126 | } 127 | 128 | public void setSmallImgUrls(ArrayList smallImgUrls) { 129 | this.smallImgUrls = smallImgUrls; 130 | } 131 | 132 | public ArrayList getSmallImgUrls() { 133 | return smallImgUrls; 134 | } 135 | 136 | /** 137 | * 一张一张大图add进ArrayList 138 | * 139 | * @param bigImageUrl 大图url 140 | */ 141 | public void addSingleBigImageUrl(String bigImageUrl) { 142 | if (bigImageUrl == null) { 143 | throw new NullPointerException("bigImageUrl is null"); 144 | } 145 | if (bigImgUrls == null) { 146 | bigImgUrls = new ArrayList<>(); 147 | } 148 | bigImgUrls.add(bigImageUrl); 149 | } 150 | 151 | /** 152 | * 一张一张小图add进ArrayList 153 | * 154 | * @param smallImageUrl 小图url 155 | */ 156 | public void addSingleSmallImageUrl(String smallImageUrl) { 157 | if (smallImageUrl == null) { 158 | throw new NullPointerException("smallImageUrl is null"); 159 | } 160 | if (smallImgUrls == null) { 161 | smallImgUrls = new ArrayList<>(); 162 | } 163 | smallImgUrls.add(smallImageUrl); 164 | } 165 | 166 | /** 167 | * 一张一张小图add进ArrayList 168 | * 169 | * @param lowImageUrl 小图url 170 | * @deprecated use {@link #addSingleSmallImageUrl(String)} 171 | */ 172 | public void addSingleLowImageUrl(String lowImageUrl) { 173 | addSingleSmallImageUrl(lowImageUrl); 174 | } 175 | 176 | public boolean isOpenDownAnimate() { 177 | return isOpenDownAnimate; 178 | } 179 | 180 | public void setOpenDownAnimate(boolean openDownAnimate) { 181 | isOpenDownAnimate = openDownAnimate; 182 | } 183 | 184 | public PhotoPagerConfig.Builder.OnPhotoSaveCallback getOnPhotoSaveCallback() { 185 | return onPhotoSaveCallback; 186 | } 187 | 188 | public void setOnPhotoSaveCallback(PhotoPagerConfig.Builder.OnPhotoSaveCallback onPhotoSaveCallback) { 189 | this.onPhotoSaveCallback = onPhotoSaveCallback; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoPickBean.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.bean; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | import android.util.Log; 6 | 7 | import com.awen.photo.photopick.controller.PhotoPickConfig; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * Created by Awen . 13 | */ 14 | public class PhotoPickBean implements Parcelable { 15 | 16 | private int maxPickSize; //最多可以选择多少张图片 17 | private int pickMode; //单选还是多选 18 | private int spanCount; //recyclerview有多少列 19 | private boolean showCamera;//是否展示拍照icon 20 | private boolean clipPhoto; //是否启动裁剪图片 21 | private boolean originalPicture;//是否选择的是原图 22 | private boolean showGif;//是否展示gif图片 23 | private int mediaType;//默认0,显示所有图片跟视频,1:只显示图片,2:只显示视频 24 | private PhotoPickConfig.Builder.OnPhotoResultCallback onPhotoResultCallback; 25 | 26 | public PhotoPickBean(){} 27 | 28 | private PhotoPickBean(Parcel in) { 29 | maxPickSize = in.readInt(); 30 | pickMode = in.readInt(); 31 | spanCount = in.readInt(); 32 | showCamera = in.readByte() != 0; 33 | clipPhoto = in.readByte() != 0; 34 | originalPicture = in.readByte() != 0; 35 | showGif = in.readByte() != 0; 36 | mediaType = in.readInt(); 37 | } 38 | 39 | @Override 40 | public void writeToParcel(Parcel dest, int flags) { 41 | dest.writeInt(maxPickSize); 42 | dest.writeInt(pickMode); 43 | dest.writeInt(spanCount); 44 | dest.writeByte((byte) (showCamera ? 1 : 0)); 45 | dest.writeByte((byte) (clipPhoto ? 1 : 0)); 46 | dest.writeByte((byte) (originalPicture ? 1 : 0)); 47 | dest.writeByte((byte) (showGif ? 1 : 0)); 48 | dest.writeInt(mediaType); 49 | } 50 | 51 | public static final Creator CREATOR = new Creator() { 52 | @Override 53 | public PhotoPickBean createFromParcel(Parcel in) { 54 | return new PhotoPickBean(in); 55 | } 56 | 57 | @Override 58 | public PhotoPickBean[] newArray(int size) { 59 | return new PhotoPickBean[size]; 60 | } 61 | }; 62 | 63 | @Override 64 | public int describeContents() { 65 | return 0; 66 | } 67 | 68 | 69 | public int getMaxPickSize() { 70 | return maxPickSize; 71 | } 72 | 73 | public void setMaxPickSize(int maxPickSize) { 74 | this.maxPickSize = maxPickSize; 75 | } 76 | 77 | public int getPickMode() { 78 | return pickMode; 79 | } 80 | 81 | public void setPickMode(int pickMode) { 82 | this.pickMode = pickMode; 83 | } 84 | 85 | public int getSpanCount() { 86 | return spanCount; 87 | } 88 | 89 | public void setSpanCount(int spanCount) { 90 | this.spanCount = spanCount; 91 | } 92 | 93 | public boolean isShowCamera() { 94 | return showCamera; 95 | } 96 | 97 | public void setShowCamera(boolean showCamera) { 98 | this.showCamera = showCamera; 99 | } 100 | 101 | public boolean isClipPhoto() { 102 | return clipPhoto; 103 | } 104 | 105 | public void setClipPhoto(boolean clipPhoto) { 106 | this.clipPhoto = clipPhoto; 107 | } 108 | 109 | public boolean isOriginalPicture() { 110 | return originalPicture; 111 | } 112 | 113 | public void setOriginalPicture(boolean originalPicture) { 114 | this.originalPicture = originalPicture; 115 | } 116 | 117 | public boolean isShowGif() { 118 | return showGif; 119 | } 120 | 121 | public void setShowGif(boolean showGif) { 122 | this.showGif = showGif; 123 | } 124 | 125 | public PhotoPickConfig.Builder.OnPhotoResultCallback getOnPhotoResultCallback() { 126 | return onPhotoResultCallback; 127 | } 128 | 129 | public void setOnPhotoResultCallback(PhotoPickConfig.Builder.OnPhotoResultCallback onPhotoResultCallback) { 130 | this.onPhotoResultCallback = onPhotoResultCallback; 131 | } 132 | 133 | public int getMediaType() { 134 | return mediaType; 135 | } 136 | 137 | public void setMediaType(int mediaType) { 138 | this.mediaType = mediaType; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoPreviewBean.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.bean; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | import java.util.ArrayList; 6 | 7 | /** 8 | * Created by Awen 9 | */ 10 | 11 | public class PhotoPreviewBean implements Parcelable { 12 | 13 | private int position; 14 | private int maxPickSize; 15 | private boolean originalPicture;//是否选择的是原图 16 | private boolean isPreview;//是否是预览 17 | 18 | public PhotoPreviewBean(){} 19 | 20 | 21 | private PhotoPreviewBean(Parcel in) { 22 | position = in.readInt(); 23 | maxPickSize = in.readInt(); 24 | originalPicture = in.readByte() != 0; 25 | isPreview = in.readByte() != 0; 26 | } 27 | 28 | @Override 29 | public void writeToParcel(Parcel dest, int flags) { 30 | dest.writeInt(position); 31 | dest.writeInt(maxPickSize); 32 | dest.writeByte((byte) (originalPicture ? 1 : 0)); 33 | dest.writeByte((byte) (isPreview ? 1 : 0)); 34 | } 35 | 36 | public static final Creator CREATOR = new Creator() { 37 | @Override 38 | public PhotoPreviewBean createFromParcel(Parcel in) { 39 | return new PhotoPreviewBean(in); 40 | } 41 | 42 | @Override 43 | public PhotoPreviewBean[] newArray(int size) { 44 | return new PhotoPreviewBean[size]; 45 | } 46 | }; 47 | 48 | @Override 49 | public int describeContents() { 50 | return 0; 51 | } 52 | 53 | public int getPosition() { 54 | return position; 55 | } 56 | 57 | public void setPosition(int position) { 58 | this.position = position; 59 | } 60 | 61 | public int getMaxPickSize() { 62 | return maxPickSize; 63 | } 64 | 65 | public void setMaxPickSize(int maxPickSize) { 66 | this.maxPickSize = maxPickSize; 67 | } 68 | 69 | public boolean isOriginalPicture() { 70 | return originalPicture; 71 | } 72 | 73 | public void setOriginalPicture(boolean originalPicture) { 74 | this.originalPicture = originalPicture; 75 | } 76 | 77 | public boolean isPreview() { 78 | return isPreview; 79 | } 80 | 81 | public void setPreview(boolean preview) { 82 | isPreview = preview; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoResultBean.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.bean; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 从图库选择了或裁剪了的图片 7 | * Created by Awen 8 | */ 9 | 10 | public class PhotoResultBean { 11 | private boolean isOriginalPicture;//用户选择的是否是原图 12 | private ArrayList photoLists; 13 | 14 | public boolean isOriginalPicture() { 15 | return isOriginalPicture; 16 | } 17 | 18 | public void setOriginalPicture(boolean originalPicture) { 19 | isOriginalPicture = originalPicture; 20 | } 21 | 22 | public ArrayList getPhotoLists() { 23 | return photoLists; 24 | } 25 | 26 | public void setPhotoLists(ArrayList photoLists) { 27 | this.photoLists = photoLists; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/controller/PhotoPreviewConfig.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.controller; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import com.awen.photo.Awen; 7 | import com.awen.photo.R; 8 | import com.awen.photo.photopick.adapter.PhotoPickAdapter; 9 | import com.awen.photo.photopick.bean.Photo; 10 | import com.awen.photo.photopick.bean.PhotoPreviewBean; 11 | import com.awen.photo.photopick.ui.PhotoPreviewActivity; 12 | 13 | import java.util.ArrayList; 14 | 15 | /** 16 | * 仿微信图片预览
17 | * 如何使用:
18 | * 19 | * new PhotoPreviewConfig.Builder((Activity) context)
20 | * .setPosition(position)
21 | * .setMaxPickSize(maxPickSize)
22 | * .setPhotos(photos)
23 | * .setSelectPhotos(selectPhotos)
24 | * .setOriginalPicture(originalPicture)
25 | * .build();
26 | *
27 | * Created by Awen 28 | */ 29 | 30 | public class PhotoPreviewConfig { 31 | public static final String EXTRA_BUNDLE = "extra_bundle"; 32 | public static final String EXTRA_BEAN = "extra_bean"; 33 | public static final String EXTRA_ORIGINAL_PIC = "original_picture"; 34 | public final static int REQUEST_CODE = 10504; 35 | 36 | private PhotoPreviewConfig(Activity activity, Builder builder) { 37 | PhotoPreviewBean photoPreviewBean = builder.bean; 38 | if (photoPreviewBean == null) { 39 | throw new NullPointerException("Builder#photoPagerBean is null"); 40 | } 41 | if (PhotoPickAdapter.selectPhotos != null && (PhotoPickAdapter.selectPhotos.size() > photoPreviewBean.getMaxPickSize())) { 42 | throw new IndexOutOfBoundsException("seleced photo size out maxPickSize size,select photo size = " + PhotoPickAdapter.selectPhotos.size() + ",maxPickSize size = " + photoPreviewBean.getMaxPickSize()); 43 | } 44 | Bundle bundle = new Bundle(); 45 | bundle.putParcelable(EXTRA_BEAN, photoPreviewBean); 46 | Intent intent = new Intent(activity, PhotoPreviewActivity.class); 47 | intent.putExtra(EXTRA_BUNDLE, bundle); 48 | activity.startActivityForResult(intent, REQUEST_CODE); 49 | activity.overridePendingTransition(R.anim.image_pager_enter_animation, 0); 50 | } 51 | 52 | public static class Builder { 53 | private Activity context; 54 | private PhotoPreviewBean bean; 55 | 56 | public Builder(Activity context) { 57 | Awen.checkInit(); 58 | if (context == null) { 59 | throw new NullPointerException("context is null"); 60 | } 61 | this.context = context; 62 | bean = new PhotoPreviewBean(); 63 | } 64 | 65 | public Builder setPhotoPreviewBean(PhotoPreviewBean bean) { 66 | this.bean = bean; 67 | return this; 68 | } 69 | 70 | public Builder setPosition(int position) { 71 | if (position < 0) { 72 | position = 0; 73 | } 74 | bean.setPosition(position); 75 | return this; 76 | } 77 | 78 | public Builder setOriginalPicture(boolean originalPicture) {//是否设置原图,默认false 79 | bean.setOriginalPicture(originalPicture); 80 | return this; 81 | } 82 | 83 | public Builder setMaxPickSize(int maxPickSize) { 84 | bean.setMaxPickSize(maxPickSize); 85 | return this; 86 | } 87 | 88 | /** 89 | * 是否是预览 90 | */ 91 | public Builder setPreview(boolean isPreview){ 92 | bean.setPreview(isPreview); 93 | return this; 94 | } 95 | 96 | public PhotoPreviewConfig build() { 97 | return new PhotoPreviewConfig(context, this); 98 | } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/data/Data.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.data; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.text.TextUtils; 6 | import android.util.Log; 7 | 8 | import com.awen.photo.R; 9 | import com.awen.photo.photopick.bean.Photo; 10 | import com.awen.photo.photopick.bean.PhotoDirectory; 11 | import com.awen.photo.photopick.util.BitmapUtil; 12 | 13 | import java.time.Duration; 14 | import java.util.ArrayList; 15 | import java.util.Collections; 16 | import java.util.Comparator; 17 | import java.util.Date; 18 | import java.util.List; 19 | 20 | import static android.provider.BaseColumns._ID; 21 | import static android.provider.MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME; 22 | import static android.provider.MediaStore.Images.ImageColumns.BUCKET_ID; 23 | import static android.provider.MediaStore.MediaColumns.DATA; 24 | import static android.provider.MediaStore.MediaColumns.DATE_ADDED; 25 | import static android.provider.MediaStore.MediaColumns.DURATION; 26 | import static android.provider.MediaStore.MediaColumns.HEIGHT; 27 | import static android.provider.MediaStore.MediaColumns.SIZE; 28 | import static android.provider.MediaStore.MediaColumns.MIME_TYPE; 29 | import static android.provider.MediaStore.MediaColumns.WIDTH; 30 | 31 | /** 32 | * Created by Awen 33 | */ 34 | public class Data { 35 | public final static int INDEX_ALL_PHOTOS = 0; 36 | 37 | /** 38 | * 获取所有图片 39 | */ 40 | public static List getDataFromCursor(Context context, Cursor data) { 41 | List directories = new ArrayList<>(); 42 | PhotoDirectory photoDirectoryAll = new PhotoDirectory(); 43 | photoDirectoryAll.setName(context.getString(R.string.all_photo)); 44 | photoDirectoryAll.setId("ALL"); 45 | 46 | while (data.moveToNext()) { 47 | int imageId = data.getInt(data.getColumnIndexOrThrow(_ID)); 48 | String bucketId = data.getString(data.getColumnIndexOrThrow(BUCKET_ID)); 49 | String name = data.getString(data.getColumnIndexOrThrow(BUCKET_DISPLAY_NAME)); 50 | String path = data.getString(data.getColumnIndexOrThrow(DATA)); 51 | long size = data.getLong(data.getColumnIndexOrThrow(SIZE)); 52 | String mimeType = data.getString(data.getColumnIndexOrThrow(MIME_TYPE)); 53 | int width = data.getInt(data.getColumnIndexOrThrow(WIDTH)); 54 | int height = data.getInt(data.getColumnIndexOrThrow(HEIGHT)); 55 | long dateAdd = data.getLong(data.getColumnIndexOrThrow(DATE_ADDED)); 56 | 57 | Photo photo = new Photo(); 58 | 59 | if (size <= 0) { 60 | continue; 61 | } 62 | photo.setId(imageId); 63 | photo.setPath(path); 64 | photo.setSize(size); 65 | photo.setMimeType(mimeType); 66 | photo.setWidth(width); 67 | photo.setHeight(height); 68 | photo.setDateAdd(dateAdd); 69 | 70 | PhotoDirectory photoDirectory = new PhotoDirectory(); 71 | photoDirectory.setId(bucketId); 72 | photoDirectory.setName(name); 73 | if (!directories.contains(photoDirectory)) { 74 | photoDirectory.setCoverPath(path); 75 | photoDirectory.addPhoto(photo); 76 | photoDirectory.setDateAdded(dateAdd); 77 | directories.add(photoDirectory); 78 | } else { 79 | directories.get(directories.indexOf(photoDirectory)).addPhoto(photo); 80 | } 81 | photoDirectoryAll.addPhoto(photo); 82 | } 83 | if (photoDirectoryAll.getPhotos().size() > 0) { 84 | photoDirectoryAll.setCoverPath(photoDirectoryAll.getPhotos().get(0).getPath()); 85 | } 86 | photoDirectoryAll.setDateAdded(new Date().getTime()); 87 | directories.add(INDEX_ALL_PHOTOS, photoDirectoryAll); 88 | return directories; 89 | } 90 | 91 | /** 92 | * 获取所有视频,存到一个文件夹bean里面 93 | */ 94 | public static PhotoDirectory getDataFromVideoCursor(Context context, Cursor data) { 95 | PhotoDirectory videoDir = new PhotoDirectory(); 96 | videoDir.setName(context.getString(R.string.all_video)); 97 | videoDir.setId("ALL_VIDEO"); 98 | 99 | while (data.moveToNext()) { 100 | int id = data.getInt(data.getColumnIndexOrThrow(_ID)); 101 | String path = data.getString(data.getColumnIndexOrThrow(DATA)); 102 | long size = data.getLong(data.getColumnIndexOrThrow(SIZE)); 103 | String mimeType = data.getString(data.getColumnIndexOrThrow(MIME_TYPE)); 104 | int width = data.getInt(data.getColumnIndexOrThrow(WIDTH)); 105 | int height = data.getInt(data.getColumnIndexOrThrow(HEIGHT)); 106 | long dateAdd = data.getLong(data.getColumnIndexOrThrow(DATE_ADDED)); 107 | long duration = data.getLong(data.getColumnIndexOrThrow(DURATION)); 108 | 109 | Photo photo = new Photo(); 110 | 111 | if (size <= 0) { 112 | continue; 113 | } 114 | photo.setId(id); 115 | photo.setPath(path); 116 | photo.setSize(size); 117 | photo.setMimeType(mimeType); 118 | photo.setWidth(width); 119 | photo.setHeight(height); 120 | photo.setDateAdd(dateAdd); 121 | photo.setDuration(duration); 122 | 123 | if (TextUtils.isEmpty(videoDir.getCoverPath())) { 124 | videoDir.setCoverPath(path); 125 | } 126 | videoDir.addPhoto(photo); 127 | } 128 | videoDir.setDateAdded(new Date().getTime()); 129 | videoDir.setVideo(true); 130 | return videoDir; 131 | } 132 | 133 | /** 134 | * 合并图片跟视频 135 | * @param parent 所有图片 136 | * @param child 所有视频 137 | */ 138 | public static void mergePhotoAndVideo(Context context,PhotoDirectory parent, PhotoDirectory child) { 139 | parent.setName(context.getString(R.string.select_photo)); 140 | List list = child.getPhotos(); 141 | for (Photo photo : list) { 142 | parent.addPhoto(photo); 143 | } 144 | sort(parent.getPhotos()); 145 | } 146 | 147 | /** 148 | * 把合并后的图片视频进行时间排序 149 | * @param list 150 | */ 151 | public static void sort(List list) { 152 | Collections.sort(list, new Comparator() { 153 | @Override 154 | public int compare(Photo o1, Photo o2) { 155 | try { 156 | if (o1.getDateAdd() < o2.getDateAdd()) { 157 | return 1; 158 | } else { 159 | return -1; 160 | } 161 | } catch (Exception e) { 162 | e.printStackTrace(); 163 | } 164 | return 0; 165 | } 166 | }); 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/loader/MediaStoreHelper.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.loader; 2 | 3 | import android.app.Activity; 4 | import android.content.ContentResolver; 5 | import android.content.Context; 6 | import android.database.Cursor; 7 | import android.os.Bundle; 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.loader.app.LoaderManager; 11 | import androidx.loader.content.Loader; 12 | 13 | import com.awen.photo.photopick.bean.PhotoPickBean; 14 | import com.awen.photo.photopick.data.Data; 15 | import com.awen.photo.photopick.bean.PhotoDirectory; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | 21 | /** 22 | * Created by Awen 23 | */ 24 | public class MediaStoreHelper { 25 | 26 | /** 27 | * 第一种方式 28 | * 29 | * @param context Activity 30 | * @param resultCallback PhotosResultCallback 31 | */ 32 | public static void getPhotoDirs(final Activity context, final PhotoPickBean config, final PhotosResultCallback resultCallback) { 33 | new Thread(new Runnable() { 34 | @Override 35 | public void run() { 36 | ContentResolver contentResolver = context.getContentResolver(); 37 | int mediaType = config.getMediaType(); 38 | PhotoDirectory videoDir = null; 39 | List directories = null; 40 | if (mediaType != MediaType.ONLY_IMAGE) {//不是只显示图片,要查询视频 41 | //视频 42 | VideoCursorLoader vLoader = new VideoCursorLoader(); 43 | Cursor vCursor = contentResolver.query(vLoader.getUri(), 44 | vLoader.getProjection(), 45 | vLoader.getSelection(), 46 | vLoader.getSelectionArgs(), 47 | vLoader.getSortOrder()); 48 | if (vCursor != null) { 49 | videoDir = Data.getDataFromVideoCursor(context, vCursor); 50 | vCursor.close(); 51 | } 52 | } 53 | 54 | if (mediaType != MediaType.ONLY_VIDEO) {//不是只显示视频,要查询图片 55 | //图片 56 | PhotoCursorLoader loader = new PhotoCursorLoader(); 57 | loader.setShowGif(config.isShowGif()); 58 | Cursor cursor = contentResolver.query(loader.getUri(), 59 | loader.getProjection(), 60 | loader.getSelection(), 61 | loader.getSelectionArgs(), 62 | loader.getSortOrder()); 63 | if (cursor != null) { 64 | directories = Data.getDataFromCursor(context, cursor); 65 | cursor.close(); 66 | } 67 | } 68 | 69 | if (directories == null) { 70 | directories = new ArrayList<>(); 71 | } 72 | if (videoDir != null && videoDir.getPhotos().size() > 0) { 73 | if (directories.size() > 0) { 74 | directories.add(1, videoDir); 75 | Data.mergePhotoAndVideo(context,directories.get(0), videoDir); 76 | } else { 77 | directories.add(videoDir); 78 | } 79 | } 80 | 81 | if (resultCallback != null) { 82 | resultCallback.onResultCallback(directories); 83 | } 84 | } 85 | }).start(); 86 | } 87 | 88 | /** 89 | * 第二种方式 90 | * 91 | * @param activity AppCompatActivity 92 | * @param args Bundle 93 | * @param resultCallback PhotosResultCallback 94 | */ 95 | public static void getPhotoDirs(final AppCompatActivity activity, final Bundle args, final PhotosResultCallback resultCallback) { 96 | activity.getSupportLoaderManager() 97 | .initLoader(0, args, new PhotoDirLoaderCallbacks(activity, resultCallback)); 98 | 99 | } 100 | 101 | static class PhotoDirLoaderCallbacks implements LoaderManager.LoaderCallbacks { 102 | 103 | private Context context; 104 | private PhotosResultCallback resultCallback; 105 | 106 | public PhotoDirLoaderCallbacks(Context context, PhotosResultCallback resultCallback) { 107 | this.context = context; 108 | this.resultCallback = resultCallback; 109 | } 110 | 111 | @Override 112 | public Loader onCreateLoader(int id, Bundle args) { 113 | return new PhotoDirectoryLoader(context); 114 | } 115 | 116 | @Override 117 | public void onLoadFinished(Loader loader, Cursor data) { 118 | 119 | if (data == null) return; 120 | 121 | List directories = Data.getDataFromCursor(context, data); 122 | data.close(); 123 | 124 | if (resultCallback != null) { 125 | resultCallback.onResultCallback(directories); 126 | } 127 | } 128 | 129 | @Override 130 | public void onLoaderReset(Loader loader) { 131 | 132 | } 133 | } 134 | 135 | 136 | public interface PhotosResultCallback { 137 | void onResultCallback(List directories); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/loader/MediaType.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.loader; 2 | 3 | public interface MediaType { 4 | 5 | int ALL = 0;//默认,包括图片和视频 6 | 7 | int ONLY_IMAGE = 1;//只有图片 8 | 9 | int ONLY_VIDEO = 2;//只有视频 10 | } 11 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/loader/PhotoCursorLoader.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.loader; 2 | 3 | import android.net.Uri; 4 | import android.provider.MediaStore; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | import static android.provider.MediaStore.MediaColumns.MIME_TYPE; 10 | 11 | /** 12 | * Created by Awen 13 | */ 14 | public class PhotoCursorLoader { 15 | 16 | private final static String IMAGE_JPEG = "image/jpeg"; 17 | private final static String IMAGE_PNG = "image/png"; 18 | private final static String IMAGE_GIF = "image/gif"; 19 | private final static String IMAGE_WEBP = "image/webp"; 20 | 21 | @NonNull 22 | private Uri uri; 23 | @Nullable 24 | private String[] projection; 25 | @Nullable 26 | private String selection; 27 | @Nullable 28 | private String[] selectionArgs; 29 | @Nullable 30 | private String sortOrder; 31 | 32 | private boolean showGif; 33 | 34 | public PhotoCursorLoader() { 35 | String[] IMAGE_PROJECTION = { 36 | MediaStore.Images.Media._ID, 37 | MediaStore.Images.Media.DATA, 38 | MediaStore.Images.Media.BUCKET_ID, 39 | MediaStore.Images.Media.BUCKET_DISPLAY_NAME, 40 | MediaStore.Images.Media.DATE_ADDED, 41 | MediaStore.Images.Media.SIZE, 42 | MediaStore.Images.Media.MIME_TYPE, 43 | MediaStore.Images.Media.WIDTH, 44 | MediaStore.Images.Media.HEIGHT 45 | }; 46 | //default ,默认配置 47 | setShowGif(true);//展示gif 48 | setUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI); 49 | setProjection(IMAGE_PROJECTION); 50 | setSelection(MIME_TYPE + "=? or " + MIME_TYPE + "=? or " + MIME_TYPE + "=? " + (showGif ? ("or " + MIME_TYPE + "=?") : "")); 51 | setShowGif(isShowGif()); 52 | setSelectionArgs(selectionArgs); 53 | setSortOrder(MediaStore.Images.Media.DATE_ADDED + " DESC"); 54 | } 55 | 56 | public PhotoCursorLoader(@NonNull Uri uri, @Nullable String[] projection, 57 | @Nullable String selection, @Nullable String[] selectionArgs, 58 | @Nullable String sortOrder) { 59 | this.uri = uri; 60 | this.projection = projection; 61 | this.selection = selection; 62 | this.selectionArgs = selectionArgs; 63 | this.sortOrder = sortOrder; 64 | } 65 | 66 | @NonNull 67 | public Uri getUri() { 68 | return uri; 69 | } 70 | 71 | public void setUri(@NonNull Uri uri) { 72 | this.uri = uri; 73 | } 74 | 75 | @Nullable 76 | public String[] getProjection() { 77 | return projection; 78 | } 79 | 80 | public void setProjection(@Nullable String[] projection) { 81 | this.projection = projection; 82 | } 83 | 84 | @Nullable 85 | public String getSelection() { 86 | return selection; 87 | } 88 | 89 | public void setSelection(@Nullable String selection) { 90 | this.selection = selection; 91 | } 92 | 93 | @Nullable 94 | public String[] getSelectionArgs() { 95 | return selectionArgs; 96 | } 97 | 98 | public void setSelectionArgs(@Nullable String[] selectionArgs) { 99 | this.selectionArgs = selectionArgs; 100 | } 101 | 102 | @Nullable 103 | public String getSortOrder() { 104 | return sortOrder; 105 | } 106 | 107 | public void setSortOrder(@Nullable String sortOrder) { 108 | this.sortOrder = sortOrder; 109 | } 110 | 111 | public boolean isShowGif() { 112 | return showGif; 113 | } 114 | 115 | public void setShowGif(boolean showGif) { 116 | this.showGif = showGif; 117 | if (showGif) { 118 | selectionArgs = new String[]{IMAGE_JPEG, IMAGE_PNG, IMAGE_WEBP, IMAGE_GIF}; 119 | } else { 120 | selectionArgs = new String[]{IMAGE_JPEG, IMAGE_PNG, IMAGE_WEBP}; 121 | } 122 | setSelection(MIME_TYPE + "=? or " + MIME_TYPE + "=? or " + MIME_TYPE + "=? " + (showGif ? ("or " + MIME_TYPE + "=?") : "")); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/loader/PhotoDirectoryLoader.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.loader; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.provider.MediaStore.Images.Media; 6 | 7 | import androidx.loader.content.CursorLoader; 8 | 9 | import static android.provider.MediaStore.MediaColumns.MIME_TYPE; 10 | 11 | /** 12 | * Created by Awen 13 | */ 14 | public class PhotoDirectoryLoader extends CursorLoader { 15 | 16 | private final static String IMAGE_JPEG = "image/jpeg"; 17 | private final static String IMAGE_PNG = "image/png"; 18 | private final static String IMAGE_GIF = "image/gif"; 19 | private final static String IMAGE_WEBP = "image/webP"; 20 | 21 | final String[] IMAGE_PROJECTION = { 22 | Media._ID, 23 | Media.DATA, 24 | Media.BUCKET_ID, 25 | Media.BUCKET_DISPLAY_NAME, 26 | Media.DATE_ADDED, 27 | Media.SIZE 28 | }; 29 | 30 | public PhotoDirectoryLoader(Context context){ 31 | this(context,false); 32 | } 33 | 34 | public PhotoDirectoryLoader(Context context, boolean showGif) { 35 | super(context); 36 | 37 | setProjection(IMAGE_PROJECTION); 38 | setUri(Media.EXTERNAL_CONTENT_URI); 39 | setSortOrder(Media.DATE_ADDED + " DESC"); 40 | 41 | setSelection( 42 | MIME_TYPE + "=? or " + MIME_TYPE + "=? " + (showGif ? ("or " + MIME_TYPE + "=?") : "")); 43 | String[] selectionArgs; 44 | if (showGif) { 45 | selectionArgs = new String[]{IMAGE_JPEG, IMAGE_PNG, IMAGE_WEBP,IMAGE_GIF}; 46 | } else { 47 | selectionArgs = new String[]{IMAGE_JPEG, IMAGE_PNG,IMAGE_WEBP}; 48 | } 49 | setSelectionArgs(selectionArgs); 50 | } 51 | 52 | 53 | private PhotoDirectoryLoader(Context context, Uri uri, String[] projection, String selection, 54 | String[] selectionArgs, String sortOrder) { 55 | super(context, uri, projection, selection, selectionArgs, sortOrder); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/loader/VideoCursorLoader.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.loader; 2 | 3 | import android.net.Uri; 4 | import android.provider.MediaStore; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | import static android.provider.MediaStore.MediaColumns.MIME_TYPE; 10 | 11 | /** 12 | * Created by Awen 13 | */ 14 | public class VideoCursorLoader { 15 | 16 | @NonNull 17 | private Uri uri; 18 | @Nullable 19 | private String[] projection; 20 | @Nullable 21 | private String selection; 22 | @Nullable 23 | private String[] selectionArgs; 24 | @Nullable 25 | private String sortOrder; 26 | 27 | public VideoCursorLoader() { 28 | String[] PROJECTION = { 29 | MediaStore.Video.Media._ID, 30 | MediaStore.Video.Media.DATA, 31 | MediaStore.Video.Media.BUCKET_ID, 32 | MediaStore.Video.Media.BUCKET_DISPLAY_NAME, 33 | MediaStore.Video.Media.DATE_ADDED, 34 | MediaStore.Video.Media.SIZE, 35 | MediaStore.Video.Media.MIME_TYPE, 36 | MediaStore.Video.Media.WIDTH, 37 | MediaStore.Video.Media.HEIGHT, 38 | MediaStore.Video.Media.DURATION, 39 | }; 40 | 41 | //default ,默认配置 42 | setUri(MediaStore.Video.Media.EXTERNAL_CONTENT_URI); 43 | setProjection(PROJECTION); 44 | setSelectionArgs(selectionArgs); 45 | setSortOrder(MediaStore.Video.Media.DATE_ADDED + " DESC"); 46 | } 47 | 48 | public VideoCursorLoader(@NonNull Uri uri, @Nullable String[] projection, 49 | @Nullable String selection, @Nullable String[] selectionArgs, 50 | @Nullable String sortOrder) { 51 | this.uri = uri; 52 | this.projection = projection; 53 | this.selection = selection; 54 | this.selectionArgs = selectionArgs; 55 | this.sortOrder = sortOrder; 56 | } 57 | 58 | @NonNull 59 | public Uri getUri() { 60 | return uri; 61 | } 62 | 63 | public void setUri(@NonNull Uri uri) { 64 | this.uri = uri; 65 | } 66 | 67 | @Nullable 68 | public String[] getProjection() { 69 | return projection; 70 | } 71 | 72 | public void setProjection(@Nullable String[] projection) { 73 | this.projection = projection; 74 | } 75 | 76 | @Nullable 77 | public String getSelection() { 78 | return selection; 79 | } 80 | 81 | public void setSelection(@Nullable String selection) { 82 | this.selection = selection; 83 | } 84 | 85 | @Nullable 86 | public String[] getSelectionArgs() { 87 | return selectionArgs; 88 | } 89 | 90 | public void setSelectionArgs(@Nullable String[] selectionArgs) { 91 | this.selectionArgs = selectionArgs; 92 | } 93 | 94 | @Nullable 95 | public String getSortOrder() { 96 | return sortOrder; 97 | } 98 | 99 | public void setSortOrder(@Nullable String sortOrder) { 100 | this.sortOrder = sortOrder; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/ui/ClipPictureActivity.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.ui; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Bitmap; 5 | import android.os.Bundle; 6 | import android.view.Menu; 7 | import android.view.MenuItem; 8 | 9 | import com.awen.photo.FrescoBaseActivity; 10 | import com.awen.photo.R; 11 | import com.awen.photo.photopick.util.AppPathUtil; 12 | import com.awen.photo.photopick.util.ImageUtils; 13 | import com.awen.photo.photopick.widget.clipimage.ClipImageLayout; 14 | 15 | /** 16 | * 用户头像裁剪 17 | * 18 | * @author Homk-M 19 | */ 20 | public class ClipPictureActivity extends FrescoBaseActivity { 21 | 22 | public static final String CLIPED_PHOTO_PATH = "cliped_photo_path"; // 已经裁剪的图片地址 23 | public static final String USER_PHOTO_PATH = "user_photo_path"; // 选择头像的本地地址 24 | private ClipImageLayout mClipImageLayout; 25 | private String photoPath; 26 | private Bitmap bitmap; 27 | 28 | @Override 29 | protected void onCreate(Bundle arg0) { 30 | super.onCreate(arg0); 31 | setContentView(R.layout.activity_clipicture); 32 | toolbar.setTitle(R.string.clip); 33 | photoPath = getIntent().getStringExtra(USER_PHOTO_PATH); 34 | if (photoPath == null) { 35 | finish(); 36 | return; 37 | } 38 | int start = photoPath.lastIndexOf("/"); 39 | bitmap = ImageUtils.getBitmap(photoPath, 480, 480); 40 | 41 | mClipImageLayout = (ClipImageLayout) findViewById(R.id.id_clipImageLayout); 42 | mClipImageLayout.init(this, bitmap); 43 | 44 | } 45 | 46 | @Override 47 | public boolean onCreateOptionsMenu(Menu menu) { 48 | getMenuInflater().inflate(R.menu.menu_ok, menu); 49 | MenuItem item = menu.findItem(R.id.ok); 50 | item.setTitle(R.string.clip); 51 | return true; 52 | } 53 | 54 | @Override 55 | public boolean onOptionsItemSelected(MenuItem item) { 56 | if (item.getItemId() == R.id.ok) {// 裁剪 57 | Bitmap bitmap = mClipImageLayout.clip(); 58 | // 保存图片 59 | String photoName = String.valueOf(System.currentTimeMillis()); 60 | // photoName = photoPath.substring(start + 1, photoPath.length()); 61 | photoPath = AppPathUtil.getClipPhotoPath() + photoName; 62 | boolean isSaveStatu = ImageUtils.saveImage2(photoPath, bitmap); 63 | bitmap.recycle(); 64 | Intent intent = new Intent(); 65 | if (isSaveStatu) { 66 | intent.putExtra(CLIPED_PHOTO_PATH, photoPath); 67 | } 68 | setResult(RESULT_OK, intent); 69 | finish(); 70 | return true; 71 | } 72 | return super.onOptionsItemSelected(item); 73 | } 74 | 75 | @Override 76 | protected void onDestroy() { 77 | super.onDestroy(); 78 | if (bitmap != null && !bitmap.isRecycled()) { 79 | bitmap.recycle(); 80 | } 81 | } 82 | 83 | @Override 84 | public void finish() { 85 | super.finish(); 86 | overridePendingTransition(0, R.anim.bottom_out); 87 | } 88 | } -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/ui/VideoPlayActivity.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.ui; 2 | 3 | import android.net.Uri; 4 | import android.os.Bundle; 5 | import android.text.TextUtils; 6 | import android.util.Log; 7 | import android.widget.MediaController; 8 | import android.widget.Toast; 9 | import android.widget.VideoView; 10 | 11 | import androidx.annotation.Nullable; 12 | 13 | import com.awen.photo.FrescoBaseActivity; 14 | import com.awen.photo.R; 15 | 16 | /** 17 | * 使用videoView进行视频的简单播放,通过intent接收“videoUrl”, 18 | * 单独显示一个本地视频或网络视频都可以通过intent传递进来 19 | */ 20 | public class VideoPlayActivity extends FrescoBaseActivity { 21 | 22 | @Override 23 | protected void onCreate(@Nullable Bundle arg0) { 24 | setOpenToolBar(false); 25 | super.onCreate(arg0); 26 | String videoUrl = getIntent().getStringExtra("videoUrl"); 27 | if (TextUtils.isEmpty(videoUrl)) { 28 | Log.e("VideoPlayActivity", "videoUrl must not null"); 29 | finish(); 30 | return; 31 | } 32 | setContentView(R.layout.activity_video_play); 33 | toolbar = findViewById(R.id.toolbar); 34 | toolbar.setTitle(""); 35 | setSupportActionBar(toolbar); 36 | VideoView videoView = findViewById(R.id.videoView); 37 | videoView.setVideoURI(Uri.parse(videoUrl)); 38 | MediaController mediaController = new MediaController(this); 39 | videoView.setMediaController(mediaController); 40 | mediaController.setMediaPlayer(videoView); 41 | videoView.requestFocus(); 42 | videoView.start(); 43 | } 44 | 45 | @Override 46 | public void finish() { 47 | super.finish(); 48 | overridePendingTransition(0, R.anim.image_pager_exit_animation); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/ui/VideoPlayLayout.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.ui; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.util.AttributeSet; 6 | import android.view.Gravity; 7 | import android.view.View; 8 | import android.widget.FrameLayout; 9 | import android.widget.ImageView; 10 | import androidx.annotation.NonNull; 11 | import androidx.annotation.Nullable; 12 | 13 | import com.awen.photo.FrescoImageLoader; 14 | import com.awen.photo.R; 15 | 16 | public class VideoPlayLayout extends FrameLayout { 17 | 18 | private Context context; 19 | private String videoPath; 20 | 21 | public VideoPlayLayout(@NonNull Context context) { 22 | super(context); 23 | init(context); 24 | } 25 | 26 | public VideoPlayLayout(@NonNull Context context, @Nullable AttributeSet attrs) { 27 | super(context, attrs); 28 | init(context); 29 | } 30 | 31 | public VideoPlayLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 32 | super(context, attrs, defStyleAttr); 33 | init(context); 34 | } 35 | 36 | private void init(Context context) { 37 | this.context = context; 38 | } 39 | 40 | private void addPlayButton() { 41 | ImageView imageView = new ImageView(context); 42 | imageView.setImageResource(R.mipmap.video_play_icon); 43 | imageView.setScaleType(ImageView.ScaleType.FIT_XY); 44 | LayoutParams params = new LayoutParams(200, 200); 45 | params.gravity = Gravity.CENTER; 46 | imageView.setLayoutParams(params); 47 | addView(imageView); 48 | imageView.setOnClickListener(new OnClickListener() { 49 | @Override 50 | public void onClick(View v) { 51 | FrescoImageLoader.startVideoActivity((Activity) context,videoPath); 52 | } 53 | }); 54 | } 55 | 56 | public void setData(View photoDraweeView, String videoPath) { 57 | this.videoPath = videoPath; 58 | addView(photoDraweeView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 59 | addPlayButton(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/util/AppPathUtil.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.util; 2 | 3 | import android.os.Environment; 4 | import android.text.TextUtils; 5 | 6 | import com.awen.photo.Awen; 7 | import com.awen.photo.R; 8 | 9 | import java.io.File; 10 | 11 | /** 12 | * Created by Awen 13 | */ 14 | public class AppPathUtil { 15 | 16 | /** 17 | * 裁剪头像 18 | * 19 | * @return 20 | */ 21 | public static String getClipPhotoPath() { 22 | return getPath("clip"); 23 | } 24 | 25 | /** 26 | * 保存大图到本地的路径地址 27 | * 28 | * @return String 29 | */ 30 | public static String getBigBitmapCachePath() { 31 | return getPath("Photo"); 32 | } 33 | 34 | private static String getPath(String str) { 35 | String path = null; 36 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 37 | path = Environment.getExternalStorageDirectory().getPath(); 38 | } 39 | if (TextUtils.isEmpty(path)) { 40 | path = Awen.getContext().getCacheDir().getPath(); 41 | } 42 | //地址如下:path/appname/appname_photo/ 43 | String app_root_name = Awen.getContext().getString(R.string.app_root_name); 44 | path = path + File.separator + app_root_name + File.separator + app_root_name + "_" + str + File.separator; 45 | exitesFolder(path); 46 | return path; 47 | } 48 | 49 | /** 50 | * 判断文件夹是否存在,不存在则创建 51 | * 52 | * @param path 53 | */ 54 | public static void exitesFolder(String path) { 55 | File file = new File(path); 56 | if (!file.exists()) { 57 | file.mkdirs(); 58 | } 59 | } 60 | 61 | public static String getFileName(String url) { 62 | String fileName = url.substring(url.lastIndexOf("/") + 1, url.length()); 63 | if (!fileName.endsWith(".jpg") && !fileName.endsWith(".png") && !fileName.endsWith(".jpeg") && !fileName.endsWith(".gif") && !fileName.endsWith(".webp")) { 64 | //防止有些图片没有后缀名 65 | fileName = fileName + ".jpg"; 66 | } 67 | return fileName; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/util/BitmapUtil.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.util; 2 | 3 | import android.graphics.BitmapFactory; 4 | import android.util.Log; 5 | 6 | import com.awen.photo.photopick.bean.Photo; 7 | 8 | /** 9 | * Created by Awen 10 | */ 11 | public class BitmapUtil { 12 | /** 13 | * 检查文件是否损坏 14 | * Check if the file is corrupted 15 | * 16 | * @return false为不损坏,true为损坏 17 | */ 18 | public static boolean checkImgCorrupted(String filePath) { 19 | BitmapFactory.Options options = new BitmapFactory.Options(); 20 | options.inJustDecodeBounds = true; 21 | BitmapFactory.decodeFile(filePath, options); 22 | return (options.mCancel || options.outWidth == -1 || options.outHeight == -1); 23 | } 24 | 25 | /** 26 | * 检查文件是否损坏,并且给photo添加上长宽 27 | * Check if the file is corrupted 28 | * 29 | * @return false为不损坏,true为损坏 30 | */ 31 | @Deprecated 32 | public static boolean checkImgCorrupted(Photo photo, String filePath) { 33 | BitmapFactory.Options options = new BitmapFactory.Options(); 34 | options.inJustDecodeBounds = true; 35 | BitmapFactory.decodeFile(filePath, options); 36 | 37 | if(options.mCancel || options.outWidth == -1 || options.outHeight == -1){ 38 | return true; 39 | }else { 40 | photo.setWidth(options.outWidth); 41 | photo.setHeight(options.outHeight); 42 | return false; 43 | } 44 | } 45 | 46 | public static int[] getImageSize(String filePath) { 47 | BitmapFactory.Options options = new BitmapFactory.Options(); 48 | options.inJustDecodeBounds = true; 49 | BitmapFactory.decodeFile(filePath, options); 50 | return new int[]{options.outWidth,options.outHeight}; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/util/FileSizeUtil.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.util; 2 | 3 | import java.text.DecimalFormat; 4 | 5 | /** 6 | * Created by Awen 7 | */ 8 | 9 | public class FileSizeUtil { 10 | 11 | private static DecimalFormat df = new DecimalFormat("#.00"); 12 | /** 13 | * 转换文件大小 14 | */ 15 | public static String formatFileSize(long fileS) { 16 | if (fileS == 0) { 17 | return "0B"; 18 | } 19 | String fileSizeString; 20 | if (fileS < 1024) { 21 | fileSizeString = df.format((double) fileS) + "B"; 22 | } else if (fileS < 1048576) { 23 | fileSizeString = df.format((double) fileS / 1024) + "KB"; 24 | } else if (fileS < 1073741824) { 25 | fileSizeString = df.format((double) fileS / 1048576) + "MB"; 26 | } else { 27 | fileSizeString = df.format((double) fileS / 1073741824) + "GB"; 28 | } 29 | return fileSizeString; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/util/PermissionUtil.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.util; 2 | 3 | import android.app.Activity; 4 | import android.content.DialogInterface; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.net.Uri; 8 | import android.os.Build; 9 | import android.provider.Settings; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.appcompat.app.AlertDialog; 13 | import androidx.core.app.ActivityCompat; 14 | 15 | import com.awen.photo.R; 16 | 17 | 18 | /** 19 | * Created by Awen 20 | */ 21 | public class PermissionUtil { 22 | 23 | public static final String TAG = "PermissionUtil"; 24 | public static final int REQUEST_CODE_ASK_PERMISSIONS = 100; 25 | 26 | public static boolean checkPermission(Activity activity, String permission) { 27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 28 | int storagePermission = ActivityCompat.checkSelfPermission(activity, permission); 29 | if (storagePermission != PackageManager.PERMISSION_GRANTED) { 30 | return false; 31 | } 32 | } 33 | return true; 34 | } 35 | 36 | 37 | public static void showPermissionDialog(final Activity activity, String permission) { 38 | if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { 39 | ActivityCompat.requestPermissions(activity, new String[]{permission}, 40 | PermissionUtil.REQUEST_CODE_ASK_PERMISSIONS); 41 | return; 42 | } 43 | ActivityCompat.requestPermissions(activity, new String[]{permission}, 44 | PermissionUtil.REQUEST_CODE_ASK_PERMISSIONS); 45 | } 46 | 47 | public static void showSystemSettingDialog(@NonNull final Activity activity, @NonNull String tips){ 48 | new AlertDialog.Builder(activity) 49 | .setMessage(tips) 50 | .setNegativeButton(R.string.cancel, null) 51 | .setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() { 52 | @Override 53 | public void onClick(DialogInterface dialog, int which) { 54 | startSystemSettingActivity(activity); 55 | } 56 | }) 57 | .show(); 58 | } 59 | 60 | public static void startSystemSettingActivity(@NonNull Activity activity){ 61 | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 62 | intent.setData(Uri.parse("package:" + activity.getPackageName())); 63 | activity.startActivity(intent); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/util/ViewUtil.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.util; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Point; 6 | import android.os.Build; 7 | import android.util.DisplayMetrics; 8 | import android.view.Display; 9 | import android.view.KeyCharacterMap; 10 | import android.view.KeyEvent; 11 | import android.view.ViewConfiguration; 12 | 13 | /** 14 | * Created by Awen 15 | */ 16 | 17 | public class ViewUtil { 18 | 19 | public static boolean isNavigationBarShow(Activity activity){ 20 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 21 | Display display = activity.getWindowManager().getDefaultDisplay(); 22 | Point size = new Point(); 23 | Point realSize = new Point(); 24 | display.getSize(size); 25 | display.getRealSize(realSize); 26 | return realSize.y!=size.y; 27 | }else { 28 | boolean menu = ViewConfiguration.get(activity).hasPermanentMenuKey(); 29 | boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK); 30 | return menu || back; 31 | } 32 | } 33 | 34 | public static int getNavigationBarHeight(Activity activity) { 35 | if (!isNavigationBarShow(activity)){ 36 | return 0; 37 | } 38 | return activity.getResources().getDimensionPixelSize(activity.getResources().getIdentifier("navigation_bar_height","dimen", "android")); 39 | } 40 | 41 | 42 | public static int getSceenHeight(Activity activity) { 43 | return activity.getWindowManager().getDefaultDisplay().getHeight()+getNavigationBarHeight(activity); 44 | } 45 | 46 | public static int getDisplayHeight(Context context){ 47 | return context.getResources().getDisplayMetrics().heightPixels; 48 | } 49 | 50 | public static int[] getDisplayWidthAndHeight(Context context){ 51 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 52 | return new int[]{displayMetrics.widthPixels,displayMetrics.heightPixels}; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/HackyViewPager.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.util.Log; 6 | import android.view.MotionEvent; 7 | 8 | import androidx.viewpager.widget.ViewPager; 9 | 10 | /** 11 | * Hacky fix for Issue #4 and 12 | * http://code.google.com/p/android/issues/detail?id=18990 13 | *

14 | * ScaleGestureDetector seems to mess up the touch events, which means that 15 | * ViewGroups which make use of onInterceptTouchEvent throw a lot of 16 | * IllegalArgumentException: pointerIndex out of range. 17 | *

18 | * There's not much I can do in my code for now, but we can mask the result by 19 | * just catching the problem and ignoring it. 20 | */ 21 | public class HackyViewPager extends ViewPager { 22 | 23 | private static final String TAG = "HackyViewPager"; 24 | 25 | public HackyViewPager(Context context) { 26 | super(context); 27 | } 28 | 29 | public HackyViewPager(Context context, AttributeSet attrs) { 30 | super(context, attrs); 31 | } 32 | 33 | @Override 34 | public boolean onInterceptTouchEvent(MotionEvent ev) { 35 | try { 36 | return super.onInterceptTouchEvent(ev); 37 | } catch (IllegalArgumentException e) { 38 | // 不理会 39 | Log.e(TAG, "hacky viewpager error1"); 40 | return false; 41 | } catch (ArrayIndexOutOfBoundsException e) { 42 | // 不理会 43 | Log.e(TAG, "hacky viewpager error2"); 44 | return false; 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/RoundProgressBar.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.ColorFilter; 5 | import android.graphics.Paint; 6 | import android.graphics.Rect; 7 | import android.graphics.RectF; 8 | import android.graphics.Typeface; 9 | import android.graphics.drawable.Drawable; 10 | import android.util.Log; 11 | 12 | import com.facebook.drawee.drawable.DrawableUtils; 13 | 14 | 15 | /** 16 | * 圆形进度条
17 | * @author Homk-M 18 | */ 19 | public class RoundProgressBar extends Drawable { 20 | private static final String TAG = "RoundProgressBar"; 21 | private Paint mRingBackgroundPaint; 22 | private int mRingBackgroundColor; 23 | // 画圆环的画笔 24 | private Paint mRingPaint; 25 | // 圆环颜色 26 | private int mRingColor; 27 | // 半径 28 | private float mRadius; 29 | // 圆环半径 30 | private float mRingRadius; 31 | // 圆环宽度 32 | private float mStrokeWidth; 33 | // 圆心x坐标 34 | private int mXCenter; 35 | // 圆心y坐标 36 | private int mYCenter; 37 | // 总进度 38 | private int mTotalProgress = 10000; 39 | // 当前进度 40 | private int mProgress; 41 | 42 | private Paint textPaint; 43 | //是否显示中间的百分比进度 44 | private boolean textIsDisplayable; 45 | private int textSize; 46 | private int textColor; 47 | 48 | public RoundProgressBar() { 49 | initAttrs(); 50 | } 51 | 52 | private void initAttrs() { 53 | textIsDisplayable = true; 54 | textSize = 24; 55 | textColor = 0xFFEBAC0E; 56 | mRadius = 60; 57 | mStrokeWidth = 16; 58 | mRingBackgroundColor = 0xFFdddad7; 59 | mRingColor = 0xFFEBAC0E; 60 | mRingRadius = mRadius + mStrokeWidth / 2; 61 | initVariable(); 62 | } 63 | 64 | private void initVariable() { 65 | mRingBackgroundPaint = new Paint(); 66 | mRingBackgroundPaint.setAntiAlias(true); 67 | mRingBackgroundPaint.setColor(mRingBackgroundColor); 68 | mRingBackgroundPaint.setStyle(Paint.Style.STROKE); 69 | mRingBackgroundPaint.setStrokeWidth(mStrokeWidth); 70 | 71 | mRingPaint = new Paint(); 72 | mRingPaint.setAntiAlias(true); 73 | mRingPaint.setColor(mRingColor); 74 | mRingPaint.setStyle(Paint.Style.STROKE); 75 | mRingPaint.setStrokeWidth(mStrokeWidth); 76 | 77 | textPaint = new Paint(); 78 | textPaint.setStrokeWidth(0); 79 | textPaint.setColor(textColor); 80 | textPaint.setTextSize(textSize); 81 | textPaint.setTypeface(Typeface.DEFAULT_BOLD); // 设置字体 82 | } 83 | 84 | @Override 85 | public void draw(Canvas canvas) { 86 | drawBar(canvas, mTotalProgress, mRingBackgroundPaint); 87 | drawBar(canvas, mProgress, mRingPaint); 88 | drawText(canvas, mProgress, textPaint); 89 | } 90 | 91 | private void drawBar(Canvas canvas, int level, Paint paint) { 92 | if (level > 0) { 93 | Rect bound = getBounds(); 94 | mXCenter = bound.centerX(); 95 | mYCenter = bound.centerY(); 96 | RectF oval = new RectF(); 97 | oval.left = (mXCenter - mRingRadius); 98 | oval.top = (mYCenter - mRingRadius); 99 | oval.right = mRingRadius * 2 + (mXCenter - mRingRadius); 100 | oval.bottom = mRingRadius * 2 + (mYCenter - mRingRadius); 101 | canvas.drawArc(oval, -90, ((float) level / mTotalProgress) * 360, false, paint); // 102 | } 103 | } 104 | 105 | /** 106 | * 画进度百分比 107 | */ 108 | private void drawText(Canvas canvas, int level, Paint paint) { 109 | if (level > 0 && textIsDisplayable) { 110 | Rect bound = getBounds(); 111 | mXCenter = bound.centerX(); 112 | mYCenter = bound.centerY(); 113 | int percent = (int) (((float) level / (float) mTotalProgress) * 100);// 中间的进度百分比,先转换成float在进行除法运算,不然都为0 114 | float textWidth = paint.measureText(percent + "%"); // 测量字体宽度,我们需要根据字体的宽度设置在圆环中间 115 | if (percent != 0) { 116 | canvas.drawText(percent + "%", mXCenter - textWidth / 2, mYCenter + textSize / 2, paint); // 画出进度百分比 117 | } 118 | // Log.e(TAG,"percent = " + percent); 119 | // Log.e(TAG,"x = " + (mXCenter - textWidth / 2)); 120 | } 121 | } 122 | 123 | @Override 124 | protected boolean onLevelChange(int level) { 125 | mProgress = level; 126 | if (level > 0 && level < 10000) { 127 | invalidateSelf(); 128 | return true; 129 | } else { 130 | return false; 131 | } 132 | } 133 | 134 | @Override 135 | public void setAlpha(int alpha) { 136 | mRingPaint.setAlpha(alpha); 137 | } 138 | 139 | @Override 140 | public void setColorFilter(ColorFilter cf) { 141 | mRingPaint.setColorFilter(cf); 142 | } 143 | 144 | @Override 145 | public int getOpacity() { 146 | return DrawableUtils.getOpacityFromColor(this.mRingPaint.getColor()); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/RoundProgressBarDrawable.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.ColorFilter; 5 | import android.graphics.Paint; 6 | import android.graphics.PixelFormat; 7 | import android.graphics.Rect; 8 | import android.graphics.RectF; 9 | import android.graphics.drawable.Drawable; 10 | 11 | import androidx.annotation.IntRange; 12 | import androidx.annotation.NonNull; 13 | import androidx.annotation.Nullable; 14 | 15 | /** 16 | * Created by Awen 17 | */ 18 | 19 | public class RoundProgressBarDrawable extends Drawable { 20 | private long maxValue = 10000; 21 | private long progress; 22 | private Paint circlePaint; 23 | private float circleWidth; 24 | private int circleProgressColor; 25 | private int circleBottomColor; 26 | private int circleBottomWidth; 27 | private int circleRadius; 28 | private int interval;//两个圆之间的间隔 29 | 30 | public RoundProgressBarDrawable() { 31 | this(70); 32 | } 33 | 34 | public RoundProgressBarDrawable(int circleRadius) { 35 | circlePaint = new Paint(); 36 | circlePaint.setAntiAlias(true); 37 | circlePaint.setStrokeWidth(10); 38 | circlePaint.setStrokeCap(Paint.Cap.ROUND); 39 | circleProgressColor = 0xdddddddd; 40 | circleBottomColor = 0xdddddddd; 41 | circleWidth = 8; 42 | this.circleRadius = circleRadius; 43 | interval = 5; 44 | circleBottomWidth = 2; 45 | progress = 1; 46 | } 47 | 48 | /* 49 | * 话空心圆 50 | */ 51 | private void drawCircle(Canvas canvas) { 52 | Rect bounds = getBounds(); 53 | int xPos = bounds.left + (bounds.width() >> 1); 54 | int yPos = bounds.bottom - (bounds.height() >> 1); 55 | circlePaint.setColor(circleBottomColor); 56 | circlePaint.setStrokeWidth(circleBottomWidth); 57 | circlePaint.setStyle(Paint.Style.STROKE); 58 | circlePaint.setShader(null); 59 | canvas.drawCircle(xPos, yPos, circleRadius + interval, circlePaint); 60 | } 61 | 62 | /* 63 | * 根据进度条画实心圆 64 | */ 65 | private void drawArc(Canvas canvas) { 66 | circlePaint.setStyle(Paint.Style.FILL); 67 | Rect bounds = getBounds(); 68 | int xPos = bounds.left + (bounds.width() >> 1); 69 | int yPos = bounds.bottom - (bounds.height() >> 1); 70 | RectF rectF = new RectF(xPos - circleRadius, yPos - circleRadius, xPos + circleRadius, yPos + circleRadius); 71 | float degree = (float) progress / (float) maxValue * 360; 72 | circlePaint.setStrokeWidth(circleWidth); 73 | circlePaint.setColor(circleProgressColor); 74 | canvas.drawArc(rectF, 270, degree, true, circlePaint); 75 | } 76 | 77 | @Override 78 | public void draw(@NonNull Canvas canvas) { 79 | if ((int) (((float) progress / (float) maxValue) * 100) == 100) { 80 | return; 81 | } 82 | drawCircle(canvas); 83 | drawArc(canvas); 84 | } 85 | 86 | @Override 87 | public void setAlpha(@IntRange(from = 0, to = 255) int i) { 88 | 89 | } 90 | 91 | @Override 92 | public void setColorFilter(@Nullable ColorFilter colorFilter) { 93 | 94 | } 95 | 96 | @Override 97 | public int getOpacity() { 98 | return PixelFormat.UNKNOWN; 99 | } 100 | 101 | @Override 102 | protected boolean onLevelChange(int level) { 103 | long origin = progress; 104 | progress = level; 105 | if (progress != 0 && origin != progress) { 106 | invalidateSelf(); 107 | return true; 108 | } else { 109 | return false; 110 | } 111 | } 112 | 113 | public RoundProgressBarDrawable setMaxValue(long value) { 114 | this.maxValue = value; 115 | return this; 116 | } 117 | 118 | public RoundProgressBarDrawable setCircleWidth(float circleWidth) { 119 | this.circleWidth = circleWidth; 120 | return this; 121 | } 122 | 123 | public RoundProgressBarDrawable setCircleBottomWidth(int circleBottomWidth) { 124 | this.circleBottomWidth = circleBottomWidth; 125 | return this; 126 | } 127 | 128 | public RoundProgressBarDrawable setCircleRadius(int circleRadius) { 129 | this.circleRadius = circleRadius; 130 | return this; 131 | } 132 | 133 | public RoundProgressBarDrawable setInterval(int interval) { 134 | this.interval = interval; 135 | return this; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/clipimage/ClipImageBorderView.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.clipimage; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Paint.Style; 8 | import android.util.AttributeSet; 9 | import android.util.TypedValue; 10 | import android.view.View; 11 | 12 | /** 13 | * 自定义裁剪头像view 14 | * 15 | * @author Homk-M 16 | * 17 | */ 18 | public class ClipImageBorderView extends View { 19 | /** 20 | * 水平方向与View的边距 21 | */ 22 | private int mHorizontalPadding; 23 | /** 24 | * 垂直方向与View的边距 25 | */ 26 | private int mVerticalPadding; 27 | /** 28 | * 绘制的矩形的宽度 29 | */ 30 | private int mWidth; 31 | /** 32 | * 边框的颜色,默认为白色 33 | */ 34 | private int mBorderColor = Color.parseColor("#FFFFFF"); 35 | /** 36 | * 边框的宽度 单位dp 37 | */ 38 | private int mBorderWidth = 1; 39 | 40 | private Paint mPaint; 41 | 42 | public ClipImageBorderView(Context context) { 43 | this(context, null); 44 | } 45 | 46 | public ClipImageBorderView(Context context, AttributeSet attrs) { 47 | this(context, attrs, 0); 48 | } 49 | 50 | public ClipImageBorderView(Context context, AttributeSet attrs, int defStyle) { 51 | super(context, attrs, defStyle); 52 | 53 | mBorderWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mBorderWidth, getResources().getDisplayMetrics()); 54 | mPaint = new Paint(); 55 | mPaint.setAntiAlias(true); 56 | } 57 | 58 | @Override 59 | protected void onDraw(Canvas canvas) { 60 | super.onDraw(canvas); 61 | // 计算矩形区域的宽度 62 | mWidth = getWidth() - 2 * mHorizontalPadding; 63 | // 计算距离屏幕垂直边界 的边距 64 | mVerticalPadding = (getHeight() - mWidth) / 2; 65 | mPaint.setColor(Color.parseColor("#aa000000")); 66 | mPaint.setStyle(Style.FILL); 67 | // 绘制左边1 68 | canvas.drawRect(0, 0, mHorizontalPadding, getHeight(), mPaint); 69 | // 绘制右边2 70 | canvas.drawRect(getWidth() - mHorizontalPadding, 0, getWidth(), getHeight(), mPaint); 71 | // 绘制上边3 72 | canvas.drawRect(mHorizontalPadding, 0, getWidth() - mHorizontalPadding, mVerticalPadding, mPaint); 73 | // 绘制下边4 74 | canvas.drawRect(mHorizontalPadding, getHeight() - mVerticalPadding, getWidth() - mHorizontalPadding, getHeight(), mPaint); 75 | // 绘制外边框 76 | mPaint.setColor(mBorderColor); 77 | mPaint.setStrokeWidth(mBorderWidth); 78 | mPaint.setStyle(Style.STROKE); 79 | canvas.drawRect(mHorizontalPadding, mVerticalPadding, getWidth() - mHorizontalPadding, getHeight() - mVerticalPadding, mPaint); 80 | 81 | } 82 | 83 | public void setHorizontalPadding(int mHorizontalPadding) { 84 | this.mHorizontalPadding = mHorizontalPadding; 85 | 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/clipimage/ClipImageLayout.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.clipimage; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.util.AttributeSet; 6 | import android.util.TypedValue; 7 | import android.widget.RelativeLayout; 8 | 9 | /** 10 | * 裁剪头像整个layout 11 | * 12 | * @author Homk-M 13 | * 14 | */ 15 | public class ClipImageLayout extends RelativeLayout { 16 | 17 | private ClipZoomImageView mZoomImageView; 18 | 19 | /** 20 | * 可以提取为自定义属性 21 | */ 22 | private int mHorizontalPadding = 20; 23 | 24 | public ClipImageLayout(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | 27 | } 28 | 29 | public void init(Context context, Bitmap bitmap) { 30 | mZoomImageView = new ClipZoomImageView(context); 31 | ClipImageBorderView mClipImageView = new ClipImageBorderView(context); 32 | 33 | android.view.ViewGroup.LayoutParams lp = new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, 34 | android.view.ViewGroup.LayoutParams.MATCH_PARENT); 35 | 36 | mZoomImageView.setImageBitmap(bitmap); 37 | 38 | this.addView(mZoomImageView, lp); 39 | this.addView(mClipImageView, lp); 40 | 41 | // 计算padding的px 42 | mHorizontalPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mHorizontalPadding, getResources() 43 | .getDisplayMetrics()); 44 | mZoomImageView.setHorizontalPadding(mHorizontalPadding); 45 | mClipImageView.setHorizontalPadding(mHorizontalPadding); 46 | } 47 | 48 | /** 49 | * 对外公布设置边距的方法,单位为dp 50 | * 51 | * @param mHorizontalPadding 52 | */ 53 | public void setHorizontalPadding(int mHorizontalPadding) { 54 | this.mHorizontalPadding = mHorizontalPadding; 55 | } 56 | 57 | /** 58 | * 裁切图片 59 | * 60 | * @return 61 | */ 62 | public Bitmap clip() { 63 | return mZoomImageView.clip(); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/DefaultOnDoubleTapListener.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.photodraweeview; 2 | 3 | import android.graphics.RectF; 4 | import android.view.GestureDetector; 5 | import android.view.MotionEvent; 6 | 7 | import com.facebook.drawee.generic.GenericDraweeHierarchy; 8 | import com.facebook.drawee.view.DraweeView; 9 | 10 | public class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener { 11 | 12 | private Attacher mAttacher; 13 | 14 | public DefaultOnDoubleTapListener(Attacher attacher) { 15 | setPhotoDraweeViewAttacher(attacher); 16 | } 17 | 18 | @Override public boolean onSingleTapConfirmed(MotionEvent e) { 19 | 20 | if (mAttacher == null) { 21 | return false; 22 | } 23 | DraweeView draweeView = mAttacher.getDraweeView(); 24 | if (draweeView == null) { 25 | return false; 26 | } 27 | 28 | if (mAttacher.getOnPhotoTapListener() != null) { 29 | final RectF displayRect = mAttacher.getDisplayRect(); 30 | 31 | if (null != displayRect) { 32 | final float x = e.getX(), y = e.getY(); 33 | if (displayRect.contains(x, y)) { 34 | float xResult = (x - displayRect.left) / displayRect.width(); 35 | float yResult = (y - displayRect.top) / displayRect.height(); 36 | mAttacher.getOnPhotoTapListener().onPhotoTap(draweeView, xResult, yResult); 37 | return true; 38 | } 39 | } 40 | } 41 | 42 | if (mAttacher.getOnViewTapListener() != null) { 43 | mAttacher.getOnViewTapListener().onViewTap(draweeView, e.getX(), e.getY()); 44 | return true; 45 | } 46 | 47 | return false; 48 | } 49 | 50 | @Override public boolean onDoubleTap(MotionEvent event) { 51 | if (mAttacher == null) { 52 | return false; 53 | } 54 | 55 | try { 56 | float scale = mAttacher.getScale(); 57 | float x = event.getX(); 58 | float y = event.getY(); 59 | 60 | if (scale < mAttacher.getMediumScale()) { 61 | mAttacher.setScale(mAttacher.getMediumScale(), x, y, true); 62 | } else if (scale >= mAttacher.getMediumScale() && scale < mAttacher.getMaximumScale()) { 63 | mAttacher.setScale(mAttacher.getMaximumScale(), x, y, true); 64 | } else { 65 | mAttacher.setScale(mAttacher.getMinimumScale(), x, y, true); 66 | } 67 | } catch (Exception e) { 68 | // Can sometimes happen when getX() and getY() is called 69 | } 70 | return true; 71 | } 72 | 73 | @Override public boolean onDoubleTapEvent(MotionEvent event) { 74 | return false; 75 | } 76 | 77 | public void setPhotoDraweeViewAttacher(Attacher attacher) { 78 | mAttacher = attacher; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/IAttacher.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.photodraweeview; 2 | 3 | import android.view.GestureDetector; 4 | import android.view.View; 5 | 6 | /** 7 | * **************************************************************************** 8 | * Copyright 2011, 2012 Chris Banes. 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * ***************************************************************************** 22 | */ 23 | 24 | public interface IAttacher { 25 | 26 | public static final float DEFAULT_MAX_SCALE = 3.0f; 27 | public static final float DEFAULT_MID_SCALE = 1.75f; 28 | public static final float DEFAULT_MIN_SCALE = 1.0f; 29 | public static final long ZOOM_DURATION = 200L; 30 | 31 | float getMinimumScale(); 32 | 33 | float getMediumScale(); 34 | 35 | float getMaximumScale(); 36 | 37 | void setMaximumScale(float maximumScale); 38 | 39 | void setMediumScale(float mediumScale); 40 | 41 | void setMinimumScale(float minimumScale); 42 | 43 | float getScale(); 44 | 45 | void setScale(float scale); 46 | 47 | void setScale(float scale, boolean animate); 48 | 49 | void setScale(float scale, float focalX, float focalY, boolean animate); 50 | 51 | void setZoomTransitionDuration(long duration); 52 | 53 | void setAllowParentInterceptOnEdge(boolean allow); 54 | 55 | void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener listener); 56 | 57 | void setOnScaleChangeListener(OnScaleChangeListener listener); 58 | 59 | void setOnLongClickListener(View.OnLongClickListener listener); 60 | 61 | void setOnPhotoTapListener(OnPhotoTapListener listener); 62 | 63 | void setOnViewTapListener(OnViewTapListener listener); 64 | 65 | OnPhotoTapListener getOnPhotoTapListener(); 66 | 67 | OnViewTapListener getOnViewTapListener(); 68 | 69 | void update(int imageInfoWidth, int imageInfoHeight); 70 | } 71 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnPhotoTapListener.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.photodraweeview; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * Interface definition for a callback to be invoked when the Photo is tapped with a single 7 | * tap. 8 | * 9 | * @author Chris Banes 10 | */ 11 | public interface OnPhotoTapListener { 12 | 13 | /** 14 | * A callback to receive where the user taps on a photo. You will only receive a callback if 15 | * the user taps on the actual photo, tapping on 'whitespace' will be ignored. 16 | * 17 | * @param view - View the user tapped. 18 | * @param x - where the user tapped from the of the Drawable, as percentage of the 19 | * Drawable width. 20 | * @param y - where the user tapped from the top of the Drawable, as percentage of the 21 | * Drawable height. 22 | */ 23 | void onPhotoTap(View view, float x, float y); 24 | } 25 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnScaleChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.photodraweeview; 2 | 3 | /** 4 | * Interface definition for callback to be invoked when attached ImageView scale changes 5 | * 6 | * @author Marek Sebera 7 | */ 8 | public interface OnScaleChangeListener { 9 | /** 10 | * Callback for when the scale changes 11 | * 12 | * @param scaleFactor the scale factor (<1 for zoom out, >1 for zoom in) 13 | * @param focusX focal point X position 14 | * @param focusY focal point Y position 15 | */ 16 | void onScaleChange(float scaleFactor, float focusX, float focusY); 17 | } 18 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnScaleDragGestureListener.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.photodraweeview; 2 | 3 | /** 4 | * **************************************************************************** 5 | * Copyright 2011, 2012 Chris Banes. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ***************************************************************************** 19 | */ 20 | 21 | public interface OnScaleDragGestureListener { 22 | void onDrag(float dx, float dy); 23 | 24 | void onFling(float startX, float startY, float velocityX, float velocityY); 25 | 26 | void onScale(float scaleFactor, float focusX, float focusY); 27 | 28 | void onScaleEnd(); 29 | } 30 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnTouchEventAndScaleChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.photodraweeview; 2 | 3 | import android.graphics.Matrix; 4 | import android.view.MotionEvent; 5 | 6 | /** 7 | * 解决多指缩放和下拉冲突的,该接口主要用于用于ScalePhotoView,因为PhotoDraweeView它的多指操作,原图缩小的时候,onTouch会被我这里中断 8 | * Created by Awen 9 | */ 10 | 11 | public interface OnTouchEventAndScaleChangeListener { 12 | 13 | void onPhotoTouchEvent(MotionEvent ev); 14 | 15 | void onPhotoScaleChange(float matrixScale); 16 | 17 | void onPhotoScaleEnd(float matrixScale); 18 | } 19 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnViewTapListener.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.photodraweeview; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * Interface definition for a callback to be invoked when the ImageView is tapped with a single 7 | * tap. 8 | * 9 | * @author Chris Banes 10 | */ 11 | public interface OnViewTapListener { 12 | /** 13 | * A callback to receive where the user taps on a ImageView. You will receive a callback if 14 | * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored. 15 | * 16 | * @param view - View the user tapped. 17 | * @param x - where the user tapped from the left of the View. 18 | * @param y - where the user tapped from the top of the View. 19 | */ 20 | void onViewTap(View view, float x, float y); 21 | } -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/PhotoDraweeView.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.photodraweeview; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.util.AttributeSet; 6 | import android.view.GestureDetector; 7 | import android.view.MotionEvent; 8 | 9 | import androidx.annotation.NonNull; 10 | 11 | import com.facebook.drawee.generic.GenericDraweeHierarchy; 12 | import com.facebook.drawee.view.SimpleDraweeView; 13 | 14 | /** 15 | * FaceBook的控件SimpleDraweeView对于图片的缩放不支持,这里修改下 16 | * Created by Awen 17 | */ 18 | public class PhotoDraweeView extends SimpleDraweeView implements IAttacher { 19 | 20 | private Attacher mAttacher; 21 | 22 | public PhotoDraweeView(Context context, GenericDraweeHierarchy hierarchy) { 23 | super(context, hierarchy); 24 | init(); 25 | } 26 | 27 | public PhotoDraweeView(Context context) { 28 | super(context); 29 | init(); 30 | } 31 | 32 | public PhotoDraweeView(Context context, AttributeSet attrs) { 33 | super(context, attrs); 34 | init(); 35 | } 36 | 37 | public PhotoDraweeView(Context context, AttributeSet attrs, int defStyle) { 38 | super(context, attrs, defStyle); 39 | init(); 40 | } 41 | 42 | protected void init() { 43 | if (mAttacher == null || mAttacher.getDraweeView() == null) { 44 | mAttacher = new Attacher(this); 45 | } 46 | } 47 | 48 | @Override public boolean onTouchEvent(MotionEvent event) { 49 | 50 | return super.onTouchEvent(event); 51 | } 52 | 53 | @Override protected void onDraw(@NonNull Canvas canvas) { 54 | int saveCount = canvas.save(); 55 | canvas.concat(mAttacher.getDrawMatrix()); 56 | super.onDraw(canvas); 57 | canvas.restoreToCount(saveCount); 58 | } 59 | 60 | @Override protected void onAttachedToWindow() { 61 | init(); 62 | super.onAttachedToWindow(); 63 | } 64 | 65 | @Override protected void onDetachedFromWindow() { 66 | mAttacher.onDetachedFromWindow(); 67 | super.onDetachedFromWindow(); 68 | } 69 | 70 | @Override public float getMinimumScale() { 71 | return mAttacher.getMinimumScale(); 72 | } 73 | 74 | @Override public float getMediumScale() { 75 | return mAttacher.getMediumScale(); 76 | } 77 | 78 | @Override public float getMaximumScale() { 79 | return mAttacher.getMaximumScale(); 80 | } 81 | 82 | @Override public void setMinimumScale(float minimumScale) { 83 | mAttacher.setMinimumScale(minimumScale); 84 | } 85 | 86 | @Override public void setMediumScale(float mediumScale) { 87 | mAttacher.setMediumScale(mediumScale); 88 | } 89 | 90 | @Override public void setMaximumScale(float maximumScale) { 91 | mAttacher.setMaximumScale(maximumScale); 92 | } 93 | 94 | @Override public float getScale() { 95 | return mAttacher.getScale(); 96 | } 97 | 98 | @Override public void setScale(float scale) { 99 | mAttacher.setScale(scale); 100 | } 101 | 102 | @Override public void setScale(float scale, boolean animate) { 103 | mAttacher.setScale(scale, animate); 104 | } 105 | 106 | @Override public void setScale(float scale, float focalX, float focalY, boolean animate) { 107 | mAttacher.setScale(scale, focalX, focalY, animate); 108 | } 109 | 110 | @Override public void setZoomTransitionDuration(long duration) { 111 | mAttacher.setZoomTransitionDuration(duration); 112 | } 113 | 114 | @Override public void setAllowParentInterceptOnEdge(boolean allow) { 115 | mAttacher.setAllowParentInterceptOnEdge(allow); 116 | } 117 | 118 | @Override public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener listener) { 119 | mAttacher.setOnDoubleTapListener(listener); 120 | } 121 | 122 | @Override public void setOnScaleChangeListener(OnScaleChangeListener listener) { 123 | mAttacher.setOnScaleChangeListener(listener); 124 | } 125 | 126 | @Override public void setOnLongClickListener(OnLongClickListener listener) { 127 | mAttacher.setOnLongClickListener(listener); 128 | } 129 | 130 | @Override public void setOnPhotoTapListener(OnPhotoTapListener listener) { 131 | mAttacher.setOnPhotoTapListener(listener); 132 | } 133 | 134 | @Override public void setOnViewTapListener(OnViewTapListener listener) { 135 | mAttacher.setOnViewTapListener(listener); 136 | } 137 | 138 | @Override public OnPhotoTapListener getOnPhotoTapListener() { 139 | return mAttacher.getOnPhotoTapListener(); 140 | } 141 | 142 | @Override public OnViewTapListener getOnViewTapListener() { 143 | return mAttacher.getOnViewTapListener(); 144 | } 145 | 146 | @Override public void update(int imageInfoWidth, int imageInfoHeight) { 147 | mAttacher.update(imageInfoWidth, imageInfoHeight); 148 | } 149 | 150 | public Attacher getmAttacher() { 151 | return mAttacher; 152 | } 153 | 154 | public void setOnTouchEventAndScaleChangeListener(OnTouchEventAndScaleChangeListener onTouchEventAndScaleChangeListener) { 155 | mAttacher.setOnTouchEventAndScaleChangeListener(onTouchEventAndScaleChangeListener); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/ScaleDragDetector.java: -------------------------------------------------------------------------------- 1 | package com.awen.photo.photopick.widget.photodraweeview; 2 | 3 | import android.content.Context; 4 | import androidx.core.view.MotionEventCompat; 5 | 6 | import android.view.MotionEvent; 7 | import android.view.ScaleGestureDetector; 8 | import android.view.VelocityTracker; 9 | import android.view.ViewConfiguration; 10 | 11 | /** 12 | * **************************************************************************** 13 | * Copyright 2011, 2012 Chris Banes. 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | * ***************************************************************************** 27 | */ 28 | public class ScaleDragDetector implements ScaleGestureDetector.OnScaleGestureListener { 29 | 30 | private static final int INVALID_POINTER_ID = -1; 31 | 32 | private final float mTouchSlop; 33 | private final float mMinimumVelocity; 34 | private final ScaleGestureDetector mScaleDetector; 35 | private final OnScaleDragGestureListener mScaleDragGestureListener; 36 | 37 | private VelocityTracker mVelocityTracker; 38 | private boolean mIsDragging; 39 | float mLastTouchX; 40 | float mLastTouchY; 41 | private int mActivePointerId = INVALID_POINTER_ID; 42 | private int mActivePointerIndex = 0; 43 | 44 | public ScaleDragDetector(Context context, OnScaleDragGestureListener scaleDragGestureListener) { 45 | mScaleDetector = new ScaleGestureDetector(context, this); 46 | mScaleDragGestureListener = scaleDragGestureListener; 47 | 48 | final ViewConfiguration configuration = ViewConfiguration.get(context); 49 | mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); 50 | mTouchSlop = configuration.getScaledTouchSlop(); 51 | } 52 | 53 | @Override public boolean onScale(ScaleGestureDetector detector) { 54 | float scaleFactor = detector.getScaleFactor(); 55 | 56 | if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) { 57 | return false; 58 | } 59 | 60 | mScaleDragGestureListener.onScale(scaleFactor, detector.getFocusX(), detector.getFocusY()); 61 | return true; 62 | } 63 | 64 | @Override public boolean onScaleBegin(ScaleGestureDetector detector) { 65 | return true; 66 | } 67 | 68 | @Override public void onScaleEnd(ScaleGestureDetector detector) { 69 | mScaleDragGestureListener.onScaleEnd(); 70 | } 71 | 72 | public boolean isScaling() { 73 | return mScaleDetector.isInProgress(); 74 | } 75 | 76 | public boolean isDragging() { 77 | return mIsDragging; 78 | } 79 | 80 | private float getActiveX(MotionEvent ev) { 81 | try { 82 | return MotionEventCompat.getX(ev, mActivePointerIndex); 83 | } catch (Exception e) { 84 | return ev.getX(); 85 | } 86 | } 87 | 88 | private float getActiveY(MotionEvent ev) { 89 | try { 90 | return MotionEventCompat.getY(ev, mActivePointerIndex); 91 | } catch (Exception e) { 92 | return ev.getY(); 93 | } 94 | } 95 | 96 | public boolean onTouchEvent(MotionEvent ev) { 97 | mScaleDetector.onTouchEvent(ev); 98 | final int action = MotionEventCompat.getActionMasked(ev); 99 | onTouchActivePointer(action, ev); 100 | onTouchDragEvent(action, ev); 101 | return true; 102 | } 103 | 104 | private void onTouchActivePointer(int action, MotionEvent ev) { 105 | switch (action) { 106 | case MotionEvent.ACTION_DOWN: 107 | mActivePointerId = ev.getPointerId(0); 108 | break; 109 | case MotionEvent.ACTION_CANCEL: 110 | case MotionEvent.ACTION_UP: 111 | mActivePointerId = INVALID_POINTER_ID; 112 | break; 113 | case MotionEvent.ACTION_POINTER_UP: 114 | final int pointerIndex = MotionEventCompat.getActionIndex(ev); 115 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 116 | if (pointerId == mActivePointerId) { 117 | final int newPointerIndex = (pointerIndex == 0) ? 1 : 0; 118 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 119 | mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex); 120 | mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex); 121 | } 122 | break; 123 | } 124 | mActivePointerIndex = MotionEventCompat.findPointerIndex(ev, 125 | mActivePointerId != INVALID_POINTER_ID ? mActivePointerId : 0); 126 | } 127 | 128 | private void onTouchDragEvent(int action, MotionEvent ev) { 129 | switch (action) { 130 | case MotionEvent.ACTION_DOWN: { 131 | mVelocityTracker = VelocityTracker.obtain(); 132 | if (null != mVelocityTracker) { 133 | mVelocityTracker.addMovement(ev); 134 | } 135 | 136 | mLastTouchX = getActiveX(ev); 137 | mLastTouchY = getActiveY(ev); 138 | mIsDragging = false; 139 | break; 140 | } 141 | 142 | case MotionEvent.ACTION_MOVE: { 143 | final float x = getActiveX(ev); 144 | final float y = getActiveY(ev); 145 | final float dx = x - mLastTouchX, dy = y - mLastTouchY; 146 | 147 | if (!mIsDragging) { 148 | mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop; 149 | } 150 | 151 | if (mIsDragging) { 152 | mScaleDragGestureListener.onDrag(dx, dy); 153 | mLastTouchX = x; 154 | mLastTouchY = y; 155 | 156 | if (null != mVelocityTracker) { 157 | mVelocityTracker.addMovement(ev); 158 | } 159 | } 160 | break; 161 | } 162 | 163 | case MotionEvent.ACTION_CANCEL: { 164 | if (null != mVelocityTracker) { 165 | mVelocityTracker.recycle(); 166 | mVelocityTracker = null; 167 | } 168 | break; 169 | } 170 | 171 | case MotionEvent.ACTION_UP: { 172 | if (mIsDragging) { 173 | if (null != mVelocityTracker) { 174 | mLastTouchX = getActiveX(ev); 175 | mLastTouchY = getActiveY(ev); 176 | 177 | mVelocityTracker.addMovement(ev); 178 | mVelocityTracker.computeCurrentVelocity(1000); 179 | 180 | final float vX = mVelocityTracker.getXVelocity(), vY = 181 | mVelocityTracker.getYVelocity(); 182 | 183 | if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) { 184 | mScaleDragGestureListener.onFling(mLastTouchX, mLastTouchY, -vX, -vY); 185 | } 186 | } 187 | } 188 | if (null != mVelocityTracker) { 189 | mVelocityTracker.recycle(); 190 | mVelocityTracker = null; 191 | } 192 | break; 193 | } 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/anim/bottom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/anim/bottom_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/anim/image_pager_enter_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/anim/image_pager_exit_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/layout/activity_clipicture.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/layout/activity_photo_detail_pager.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | 25 | 26 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/layout/activity_photo_pick.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 38 | 39 | 44 | 45 | 55 | 56 | 60 | 61 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/layout/activity_photo_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 28 | 29 | 38 | 39 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/layout/activity_video_play.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 23 | 24 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/layout/item_photo_gallery.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 22 | 23 | 31 | 32 | 37 | 38 | 44 | 45 | 46 | 54 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/layout/item_photo_pick.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 24 | 25 | 30 | 31 | 37 | 38 | 46 | 47 | 48 | 55 | 56 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/layout/toolbar_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/menu/menu_ok.xml: -------------------------------------------------------------------------------- 1 | 2 |

4 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/menu/menu_pick.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-xhdpi/failure_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/failure_image.jpg -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-xhdpi/gif_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/gif_icon.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-xhdpi/select_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/select_icon.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-xhdpi/take_photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/take_photo.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-xhdpi/video_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/video_icon.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-xhdpi/video_play_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/video_play_icon.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values-v21/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25dp 4 | 71dp 5 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values-v26/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values-v27/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values-v29/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #80000000 8 | #00000000 9 | #D9D9D9 10 | #80ffffff 11 | #00000000 12 | 13 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 0dp 7 | 48dp 8 | 9 | 4dp 10 | 2dp 11 | 5dp 12 | 8dp 13 | 16dp 14 | 22dp 15 | 24dp 16 | 28dp 17 | 32dp 18 | 64dp 19 | 70dp 20 | 1dp 21 | 2dp 22 | 4dp 23 | 6dp 24 | 8dp 25 | 12dp 26 | 16dp 27 | 22dp 28 | 24dp 29 | 28dp 30 | 32dp 31 | 64dp 32 | 33 | 34 | 10sp 35 | 12sp 36 | 14sp 37 | 15sp 38 | 16sp 39 | 18sp 40 | 22sp 41 | 26sp 42 | 40sp 43 | 6sp 44 | 45 | 40dp 46 | 16dp 47 | 20dp 48 | 24dp 49 | 32dp 50 | 40dp 51 | 48dp 52 | 56dp 53 | 54 | 55 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PhotoPickLibrary 3 | 4 | %s张 5 | %s个 6 | SD卡不存在,无法使用此功能 7 | 所有图片 8 | 所有视频 9 | 图片和视频 10 | 图片 11 | 视频 12 | 裁剪 13 | 确定 14 | 预览 15 | 保存图片 16 | 保存失败 17 | 已保存->图库查看,图片地址: \n %s 18 | 相册 19 | 加载中… 20 | 找不到图片 21 | 找不到视频 22 | 取消 23 | 选择 24 | 发送 25 | 发送(%s/%s) 26 | 原图(%s) 27 | 视频(%s) 28 | 29 | 30 | 缺少(相机)权限,无法使用该功能,点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限 31 | 缺少(麦克风)权限,无法使用该功能,点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限 32 | 缺少(麦克风或相机)权限,无法使用该功能,点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限 33 | 缺少(读取位置信息)权限,无法使用该功能,点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限 34 | 本App需要使用(存储)和(读取本机识别码)权限,您是否同意?点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限 35 | 本App需要使用(存储)权限,您是否同意?点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限 36 | 设置 37 | 38 | -------------------------------------------------------------------------------- /photoLibrary/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 18 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /pictrue/304079-052c8fd0c9d22efd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/304079-052c8fd0c9d22efd.gif -------------------------------------------------------------------------------- /pictrue/304079-8d726553c6c0b6ba.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/304079-8d726553c6c0b6ba.gif -------------------------------------------------------------------------------- /pictrue/304079-e4c819f695ed83c0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/304079-e4c819f695ed83c0.gif -------------------------------------------------------------------------------- /pictrue/WechatIMG20.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/WechatIMG20.jpeg -------------------------------------------------------------------------------- /pictrue/WechatIMG21.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/WechatIMG21.jpeg -------------------------------------------------------------------------------- /pictrue/device-2017-10-25-033458.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/device-2017-10-25-033458.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':photoLibrary', ':simaple' 2 | -------------------------------------------------------------------------------- /simaple/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /simaple/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'kotlin-kapt' 5 | 6 | android { 7 | compileSdkVersion rootProject.ext.android.compileSdkVersion 8 | buildToolsVersion rootProject.ext.android.buildToolsVersion 9 | 10 | defaultConfig { 11 | applicationId "com.simaple" 12 | minSdkVersion 16 13 | targetSdkVersion rootProject.ext.android.targetSdkVersion 14 | versionCode 1 15 | versionName "1.0" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 28 | implementation project(':photoLibrary') 29 | implementation "androidx.appcompat:appcompat:1.2.0" 30 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4' 31 | //android6.0权限工具类 32 | implementation 'com.lovedise:permissiongen:0.1.1' 33 | } 34 | -------------------------------------------------------------------------------- /simaple/keystore/keystore密码: -------------------------------------------------------------------------------- 1 | keystore密码:123456 2 | alias:photo 3 | password:123456 -------------------------------------------------------------------------------- /simaple/keystore/photo.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/keystore/photo.jks -------------------------------------------------------------------------------- /simaple/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Awen/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | #保持 Parcelable 不被混淆 19 | -keep class * implements android.os.Parcelable { 20 | public static final android.os.Parcelable$Creator *; 21 | } 22 | # app javabean classes 23 | -keep class com.awen.photo.photopick.bean.** { *; } 24 | 25 | ##---------------6.0权限 start---------------------------------- 26 | -keepattributes Annotation 27 | -keepclassmembers class ** { 28 | @kr.co.namee.permissiongen.PermissionSuccess public *; 29 | @kr.co.namee.permissiongen.PermissionFail public *; 30 | } 31 | ##---------------6.0权限 end---------------------------------- 32 | 33 | ##---------------fresco start---------------------------------- 34 | # Keep our interfaces so they can be used by other ProGuard rules. 35 | # See http://sourceforge.net/p/proguard/bugs/466/ 36 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 37 | 38 | # Do not strip any method/class that is annotated with @DoNotStrip 39 | -keep @com.facebook.common.internal.DoNotStrip class * 40 | -keepclassmembers class * { 41 | @com.facebook.common.internal.DoNotStrip *; 42 | } 43 | 44 | # Keep native methods 45 | -keepclassmembers class * { 46 | native ; 47 | } 48 | ##---------------fresco end---------------------------------- 49 | -------------------------------------------------------------------------------- /simaple/simaple-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/simaple-release.apk -------------------------------------------------------------------------------- /simaple/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /simaple/src/main/assets/test.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/src/main/assets/test.jpeg -------------------------------------------------------------------------------- /simaple/src/main/java/com/simaple/App.java: -------------------------------------------------------------------------------- 1 | package com.simaple; 2 | 3 | import android.app.Application; 4 | 5 | import com.awen.photo.FrescoImageLoader; 6 | 7 | /** 8 | * Created by Awen 9 | */ 10 | 11 | public class App extends Application { 12 | 13 | @Override 14 | public void onCreate() { 15 | super.onCreate(); 16 | FrescoImageLoader.init(this); 17 | //下面是配置toolbar颜色和存储图片地址的 18 | // FrescoImageLoader.init(this,android.R.color.black); 19 | // FrescoImageLoader.init(this,android.R.color.holo_blue_light,"/storage/xxxx/xxx"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /simaple/src/main/java/com/simaple/CustomInsetsRelativeLayout.java: -------------------------------------------------------------------------------- 1 | package com.simaple; 2 | 3 | import android.content.Context; 4 | import android.graphics.Rect; 5 | import android.os.Build; 6 | import android.util.AttributeSet; 7 | import android.widget.RelativeLayout; 8 | 9 | /** 10 | * 解决activity设置了状态栏透明,又设置了android:windowSoftInputMode="adjustResize"的时候输入框被输入法挡住的情况
11 | * 这个作为layout的根布局(root),并设置android:fitsSystemWindows="true" 12 | * Created by Awen 13 | */ 14 | public class CustomInsetsRelativeLayout extends RelativeLayout { 15 | private int[] mInsets = new int[4]; 16 | 17 | public CustomInsetsRelativeLayout(Context context) { 18 | super(context); 19 | } 20 | 21 | public CustomInsetsRelativeLayout(Context context, AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | public CustomInsetsRelativeLayout(Context context, AttributeSet attrs, int defStyle) { 26 | super(context, attrs, defStyle); 27 | } 28 | 29 | public final int[] getInsets() { 30 | return mInsets; 31 | } 32 | 33 | @Override 34 | protected final boolean fitSystemWindows(Rect insets) { 35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 36 | // Intentionally do not modify the bottom inset. For some reason, 37 | // if the bottom inset is modified, window resizing stops working. 38 | // TODO: Figure out why. 39 | 40 | mInsets[0] = insets.left; 41 | mInsets[1] = insets.top; 42 | mInsets[2] = insets.right; 43 | 44 | insets.left = 0; 45 | insets.top = 0; 46 | insets.right = 0; 47 | } 48 | 49 | return super.fitSystemWindows(insets); 50 | } 51 | } -------------------------------------------------------------------------------- /simaple/src/main/java/com/simaple/ImageProvider.java: -------------------------------------------------------------------------------- 1 | package com.simaple; 2 | 3 | import com.awen.photo.FrescoImageLoader; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * Created by Awen 10 | */ 11 | public class ImageProvider { 12 | 13 | public static List getImageUrls(){ 14 | List list = new ArrayList<>(); 15 | list.add(FrescoImageLoader.getResUrl(R.mipmap.resx));//长图 16 | list.add(FrescoImageLoader.getAssetUrl("test.jpeg")); 17 | // //如果加载本地图片,可按照下面的来 18 | // list.add(FrescoImageLoader.getFileUrl("/storage/emulated/0/Pictures/Screenshots/Screenshot_20170919-203120.png")); 19 | // list.add(FrescoImageLoader.getFileUrl("/storage/emulated/0/tencent/MicroMsg/WeiXin/mmexport1505817817979.jpg")); 20 | //网络长图,包括横向和纵向的长图 21 | list.add("https://user-images.githubusercontent.com/39898537/61259085-3449aa80-a7ab-11e9-90bb-0359971e3d15.jpg"); 22 | list.add("https://user-images.githubusercontent.com/39898537/61259086-34e24100-a7ab-11e9-9643-2ad2f50dee6f.jpg"); 23 | list.add("https://user-images.githubusercontent.com/39898537/61259087-34e24100-a7ab-11e9-9c6c-6b2d8ffa43a1.jpg"); 24 | list.add("https://wx2.sinaimg.cn/mw690/005MctNqgy1fx674gpkbvj30gf4k2wzp.jpg"); 25 | list.add("https://wx2.sinaimg.cn/mw690/0062Xesrgy1fx8uu4ltdyj30j635g47x.jpg"); 26 | list.add("https://wx1.sinaimg.cn/mw690/0062Xesrgy1fx8uu5d856j30j64v0h7z.jpg"); 27 | list.add("https://wx3.sinaimg.cn/mw690/0062Xesrgy1fx8uu55neej30j63sq4le.jpg"); 28 | list.add("https://wx1.sinaimg.cn/mw690/0062Xesrgy1fx8uu5p4stj30j66d6b29.jpg"); 29 | list.add("https://wx3.sinaimg.cn/mw690/0062Xesrgy1fx8uu5tkicj30j65fu4qp.jpg"); 30 | list.add("https://raw.githubusercontent.com/Awent/PhotoPick-Master/master/pictrue/WechatIMG20.jpeg"); 31 | list.add("https://raw.githubusercontent.com/Awent/PhotoPick-Master/master/pictrue/WechatIMG21.jpeg"); 32 | //网络图片 33 | list.add("https://wx1.sinaimg.cn/mw690/7325792bly1fx9oma87k1j21900u04jf.jpg"); 34 | list.add("https://wx3.sinaimg.cn/mw690/7325792bly1fx9oma3jhpj21900u04h0.jpg"); 35 | list.add("https://wx2.sinaimg.cn/mw690/7325792bly1fx9oylai59j22040u0hdu.jpg"); 36 | list.add("https://wx4.sinaimg.cn/mw690/0061VhPpgy1fx9x54op3oj30u00pc1kx.jpg"); 37 | list.add("https://wx3.sinaimg.cn/mw690/006cZ2iWgy1fskddvgmwoj30mq0vu7ik.jpg"); 38 | list.add("https://wx3.sinaimg.cn/mw690/006l0mbogy1fi68udt62wj30u010h79j.jpg"); 39 | list.add("https://wx4.sinaimg.cn/mw690/006l0mbogy1fi68ud4uwwj30u00zrtdj.jpg"); 40 | list.add("https://wx1.sinaimg.cn/mw690/006DQg3tly1fuvkrwjforg30b40alb29.gif"); 41 | list.add("https://wx2.sinaimg.cn/mw690/006DQg3tly1fwhen1vuudg30go09e7wi.gif"); 42 | list.add("https://wx1.sinaimg.cn/mw690/006DQg3tly1fuvkrxsntcg30g409xx6q.gif"); 43 | list.add("https://wx1.sinaimg.cn/mw690/006DQg3tly1fuvks859e4g30dw0atqv8.gif"); 44 | return list; 45 | } 46 | 47 | /** 48 | * 大图 49 | * @return 50 | */ 51 | public static List getBigImgUrls(){ 52 | List list = new ArrayList<>(); 53 | list.add("https://wx3.sinaimg.cn/mw690/0061VhPpgy1fx8w3jn6o8j30u00qd774.jpg"); 54 | list.add("https://wx4.sinaimg.cn/mw690/006mQAf4ly1fx8yoea4zuj30vy1bzk0x.jpg"); 55 | list.add("https://wx1.sinaimg.cn/mw690/006mQAf4ly1fx8yoipc10j30vy1bzwnt.jpg"); 56 | return list; 57 | } 58 | 59 | /** 60 | * 小图,这里随便找两张小图的 61 | * @return 62 | */ 63 | public static List getSmallImgUrls(){ 64 | List list = new ArrayList<>(); 65 | list.add("https://wx3.sinaimg.cn/mw690/006qDXTKgy1fx8q6x78hkj30c80953yt.jpg"); 66 | list.add("https://wx3.sinaimg.cn/mw690/006qDXTKgy1fx8q6x78hkj30c80953yt.jpg"); 67 | list.add("https://wx3.sinaimg.cn/mw690/006qDXTKgy1fx8q6x78hkj30c80953yt.jpg"); 68 | return list; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /simaple/src/main/java/com/simaple/MyPhotoBean.java: -------------------------------------------------------------------------------- 1 | package com.simaple; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by Awen 8 | */ 9 | 10 | public class MyPhotoBean implements Parcelable { 11 | 12 | private int id; 13 | private String content; 14 | 15 | public MyPhotoBean(){} 16 | 17 | protected MyPhotoBean(Parcel in) { 18 | id = in.readInt(); 19 | content = in.readString(); 20 | } 21 | 22 | public int getId() { 23 | return id; 24 | } 25 | 26 | public void setId(int id) { 27 | this.id = id; 28 | } 29 | 30 | public String getContent() { 31 | return content; 32 | } 33 | 34 | public void setContent(String content) { 35 | this.content = content; 36 | } 37 | 38 | @Override 39 | public int describeContents() { 40 | return 0; 41 | } 42 | 43 | @Override 44 | public void writeToParcel(Parcel dest, int flags) { 45 | dest.writeInt(id); 46 | dest.writeString(content); 47 | } 48 | 49 | public static final Creator CREATOR = new Creator() { 50 | @Override 51 | public MyPhotoBean createFromParcel(Parcel in) { 52 | return new MyPhotoBean(in); 53 | } 54 | 55 | @Override 56 | public MyPhotoBean[] newArray(int size) { 57 | return new MyPhotoBean[size]; 58 | } 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /simaple/src/main/java/com/simaple/UserBean.kt: -------------------------------------------------------------------------------- 1 | package com.simaple 2 | 3 | class UserBean { 4 | var code = 0 5 | var msg: String? = null 6 | var list: List? = null 7 | 8 | class User { 9 | var avatar: String? = null 10 | var smallAvatar: String? = null 11 | var name: String? = null 12 | var age = 0 13 | } 14 | } -------------------------------------------------------------------------------- /simaple/src/main/java/com/simaple/activity/CustomPhotoPageActivity.kt: -------------------------------------------------------------------------------- 1 | package com.simaple.activity 2 | 3 | import android.Manifest 4 | import android.view.View 5 | import android.widget.TextView 6 | import android.widget.Toast 7 | import androidx.appcompat.app.AlertDialog 8 | import com.awen.photo.photopick.controller.PhotoPagerConfig 9 | import com.awen.photo.photopick.ui.PhotoPagerActivity 10 | import com.simaple.R 11 | import kr.co.namee.permissiongen.PermissionFail 12 | import kr.co.namee.permissiongen.PermissionGen 13 | import kr.co.namee.permissiongen.PermissionSuccess 14 | 15 | /** 16 | * 自定义view实现下标和长按功能等,如果需要传递参数进来,参考 17 | * @see MyPhotoPagerActivity 18 | * 19 | * 如果自定义activity开启了下滑关闭功能,记得当前自定义activity的Theme要引用{@style/PhoAppTheme.Transparent} 20 | * 如下: 21 | * 24 | */ 25 | class CustomPhotoPageActivity : PhotoPagerActivity() { 26 | companion object { 27 | const val requestSDCode = 100 28 | } 29 | 30 | private lateinit var indicator: TextView 31 | private var size: Int = 0 32 | private val userId: Long by lazy { 33 | bundle.getLong("user_id")//通过bundle获取传递进来的参数信息 34 | } 35 | 36 | override fun setCustomView(layoutId: Int) { 37 | super.setCustomView(R.layout.activity_kt_custom_page)//设置自定义view 38 | } 39 | 40 | override fun init() { 41 | super.init() 42 | setIndicatorVisibility(false)//需要主动隐藏默认的下标 43 | size = photoPagerBean.bigImgUrls.size 44 | indicator = customView.findViewById(R.id.indicator)//通过customView来获取自定义view的控件 45 | indicator.text = "1/$size" 46 | 47 | //保存图片回调 48 | setOnPhotoSaveCallback { 49 | AlertDialog.Builder(this) 50 | .setTitle("提示") 51 | .setMessage(if (it.isNullOrEmpty()) "保存失败" else "保存成功,保存地址:$it") 52 | .show() 53 | } 54 | 55 | } 56 | 57 | override fun onStart() { 58 | super.onStart() 59 | AlertDialog.Builder(this) 60 | .setTitle("提示") 61 | .setMessage("1、可长按保存图片\n\np:前两张为res和asset图片,会保存失败\n\n2、这是继承PhotoPagerActivity的自定义界面,在这里只展示自定义下标、长按保存功能、下滑关闭界面功能,更多功能需要自己去实现") 62 | .show() 63 | } 64 | 65 | override fun onPageSelected(position: Int) { 66 | super.onPageSelected(position) 67 | indicator.text = "${currentPosition + 1}/$size" 68 | } 69 | 70 | override fun onLongClick(view: View?): Boolean { 71 | val items = arrayOf("获取当前用户id", "获取当前图片地址", "发给朋友", "收藏", "保存图片", "举报", "取消") 72 | AlertDialog.Builder(this) 73 | .setItems(items) { dialog, which -> 74 | when (which) { 75 | 0 -> toast("通过bundle传过来的用户userId = $userId") 76 | 1 -> toast(currentImageUrl) 77 | 4 -> {//保存图片,注意这里要自己先获取写sd卡权限,具体的使用哪个库来获取权限,这里自己进行修改 78 | //以下操作会回调这两个方法:#requestPermissionSDSuccess(), #requestPermissionSDFailed() 79 | PermissionGen.needPermission(this, requestSDCode, Manifest.permission.WRITE_EXTERNAL_STORAGE) 80 | } 81 | else 82 | -> toast(items[which]) 83 | } 84 | }.show() 85 | return true 86 | } 87 | 88 | private fun toast(s: String) { 89 | Toast.makeText(this, s, Toast.LENGTH_LONG).show(); 90 | } 91 | 92 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { 93 | //转移权限请求由PermissionGen处理 94 | PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults) 95 | } 96 | 97 | @PermissionSuccess(requestCode = requestSDCode) 98 | fun requestPermissionSDSuccess() { 99 | //调用startPermissionSDSuccess此方法可以保存图片 100 | startPermissionSDSuccess() 101 | } 102 | 103 | @PermissionFail(requestCode = requestSDCode) 104 | fun requestPermissionSDFailed() { 105 | startPermissionSDFaild() 106 | } 107 | 108 | } -------------------------------------------------------------------------------- /simaple/src/main/java/com/simaple/activity/MyPhotoPagerActivity.java: -------------------------------------------------------------------------------- 1 | package com.simaple.activity; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | import android.widget.Button; 6 | import android.widget.EditText; 7 | import android.widget.TextView; 8 | import android.widget.Toast; 9 | 10 | import androidx.annotation.LayoutRes; 11 | 12 | import com.awen.photo.photopick.ui.PhotoPagerActivity; 13 | import com.simaple.MyPhotoBean; 14 | import com.simaple.R; 15 | 16 | import java.util.ArrayList; 17 | 18 | /** 19 | * 自定义图片浏览界面 20 | * Created by Awen 21 | */ 22 | 23 | public class MyPhotoPagerActivity extends PhotoPagerActivity { 24 | 25 | private TextView title; 26 | private EditText et; 27 | private ArrayList list; 28 | 29 | @Override 30 | protected void setCustomView(@LayoutRes int layoutId) { 31 | super.setCustomView(R.layout.activity_custom_pager); 32 | } 33 | 34 | @Override 35 | protected void init() { 36 | super.init(); 37 | et = (EditText) getCustomView().findViewById(R.id.et); 38 | title = (TextView) getCustomView().findViewById(R.id.textView); 39 | // title.setText(currentPosition + "/" + photoPagerBean.getBigImgUrls().size()); 40 | Button button = (Button) getCustomView().findViewById(R.id.button); 41 | button.setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View view) { 44 | Toast.makeText(MyPhotoPagerActivity.this,"我是自定义的图片浏览器",Toast.LENGTH_LONG).show(); 45 | } 46 | }); 47 | setIndicatorVisibility(false);//设置Indicator不可见 48 | 49 | getCustomView().findViewById(R.id.back).setOnClickListener(new View.OnClickListener() { 50 | @Override 51 | public void onClick(View view) { 52 | onBackPressed(); 53 | } 54 | }); 55 | 56 | //获取MainActivity传递过来的数据 57 | Bundle bundle = getBundle(); 58 | if(bundle != null) { 59 | list = bundle.getParcelableArrayList("test_bundle"); 60 | if (list != null && !list.isEmpty()){ 61 | //you can do something 62 | } 63 | } 64 | } 65 | 66 | @Override 67 | public void onPageSelected(int position) { 68 | super.onPageSelected(position); 69 | title.setText(currentPosition + "/" + photoPagerBean.getBigImgUrls().size()); 70 | Toast.makeText(MyPhotoPagerActivity.this,"当前位置的图片地址 = " + photoPagerBean.getBigImgUrls().get(position),Toast.LENGTH_LONG).show(); 71 | if (list != null && !list.isEmpty()){ 72 | et.setText(list.get(position).getContent()); 73 | } 74 | } 75 | 76 | @Override 77 | protected boolean onSingleClick() { 78 | Toast.makeText(MyPhotoPagerActivity.this,"单击图片",Toast.LENGTH_LONG).show(); 79 | return true; 80 | } 81 | 82 | @Override 83 | public boolean onLongClick(View view) { 84 | Toast.makeText(MyPhotoPagerActivity.this,"长按图片",Toast.LENGTH_LONG).show(); 85 | //saveImage();//保存图片到图库 86 | return true; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /simaple/src/main/res/layout/activity_custom_pager.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 20 | 21 | 27 | 28 | 37 | 38 | 39 | 45 | 46 |