├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── MultiImageSelector.iml ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── gradle.properties ├── library.iml ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yongchun │ │ └── library │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── yongchun │ │ │ └── library │ │ │ ├── adapter │ │ │ ├── ImageFolderAdapter.java │ │ │ └── ImageListAdapter.java │ │ │ ├── model │ │ │ ├── LocalMedia.java │ │ │ └── LocalMediaFolder.java │ │ │ ├── utils │ │ │ ├── CropUtil.java │ │ │ ├── FileUtils.java │ │ │ ├── GridSpacingItemDecoration.java │ │ │ ├── LocalMediaLoader.java │ │ │ └── ScreenUtils.java │ │ │ ├── view │ │ │ ├── FolderWindow.java │ │ │ ├── ImageCropActivity.java │ │ │ ├── ImagePreviewActivity.java │ │ │ ├── ImagePreviewFragment.java │ │ │ └── ImageSelectorActivity.java │ │ │ └── widget │ │ │ ├── PreviewViewPager.java │ │ │ └── SquareRelativeLayout.java │ └── res │ │ ├── anim │ │ ├── down_out.xml │ │ ├── fade_in.xml │ │ ├── fade_out.xml │ │ └── up_in.xml │ │ ├── color │ │ └── white_text_selector.xml │ │ ├── drawable │ │ ├── btn_round_green_disable.xml │ │ ├── btn_round_green_enable.xml │ │ ├── btn_round_green_selector.xml │ │ ├── check_green.xml │ │ ├── checkbox_checktor.xml │ │ ├── checkbox_selector.xml │ │ ├── image_placeholder.xml │ │ ├── item_divider.xml │ │ └── radio_button.xml │ │ ├── layout │ │ ├── activity_image_crop.xml │ │ ├── activity_image_preview.xml │ │ ├── activity_imageselector.xml │ │ ├── fragment_image_preview.xml │ │ ├── item_camera.xml │ │ ├── item_folder.xml │ │ ├── item_picture.xml │ │ └── window_folder.xml │ │ ├── mipmap-xhdpi │ │ ├── ic_back.png │ │ └── ic_camera.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_arrow.png │ │ ├── ic_check.png │ │ ├── ic_checked.png │ │ ├── ic_placeholder.png │ │ └── ic_radio_button.png │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── yongchun │ └── library │ └── ExampleUnitTest.java ├── sample ├── .gitignore ├── Example-release.apk ├── build.gradle ├── proguard-rules.pro ├── sample.iml └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yongchun │ │ └── multiimageselector │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── yongchun │ │ │ └── multiimageselector │ │ │ ├── MainActivity.java │ │ │ └── SelectResultActivity.java │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_result.xml │ │ ├── item_result.xml │ │ └── layout_number.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_minus.png │ │ └── ic_plus.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── yongchun │ └── multiimageselector │ └── ExampleUnitTest.java ├── screenshot ├── Screenshot1.jpg ├── Screenshot2.jpg ├── Screenshot3.jpg ├── Screenshot4.jpg └── Screenshot5.jpg └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | MultiImageSelector -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Encapsulation issues 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | 77 | 78 | 79 | 80 | 81 | 82 | 1.8 83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 | 1.8 95 | 96 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /MultiImageSelector.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImageSelector 2 | Image selector library for Android. Support single choice、multi-choice、cropping image and preview image. 3 | 4 | ![](https://raw.githubusercontent.com/ioneday/ImageSelector/master/screenshot/Screenshot1.jpg) 5 | ![](https://raw.githubusercontent.com/ioneday/ImageSelector/master/screenshot/Screenshot2.jpg) 6 | ![](https://raw.githubusercontent.com/ioneday/ImageSelector/master/screenshot/Screenshot3.jpg) 7 | 8 | ![](https://raw.githubusercontent.com/ioneday/ImageSelector/master/screenshot/Screenshot4.jpg) 9 | ![](https://raw.githubusercontent.com/ioneday/ImageSelector/master/screenshot/Screenshot5.jpg) 10 | 11 | ## Quick start 12 | 13 | 1) Add Library module as a dependency in your build.gradle file. 14 | 15 | or 16 | 17 | ```xml 18 | dependencies { 19 | compile 'com.android.support:recyclerview-v7:22.2.1' 20 | compile 'com.github.bumptech.glide:glide:3.6.1' 21 | compile 'com.commit451:PhotoView:1.2.4' 22 | compile 'com.isseiaoki:simplecropview:1.0.13' 23 | compile 'com.yongchun:com.yongchun.imageselector:1.1.0' 24 | } 25 | ``` 26 | 27 | 2) Declare permission in your AndroidManifest.xml 28 | 29 | ```xml 30 | 31 | 32 | ``` 33 | ```java 34 | 35 | 36 | 37 | ``` 38 | 39 | 3) Call ImageSelectorActivity in your code 40 | 41 | ```java 42 | ImageSelectorActivity.start(MainActivity.this, maxSelectNum, mode, isShow,isPreview,isCrop); 43 | ``` 44 | same this 45 | 46 | ```java 47 | public static void start(Activity activity, int maxSelectNum, int mode, boolean isShow, boolean enablePreview, boolean enableCrop) { 48 | Intent intent = new Intent(activity, ImageSelectorActivity.class); 49 | intent.putExtra(EXTRA_MAX_SELECT_NUM, maxSelectNum); 50 | intent.putExtra(EXTRA_SELECT_MODE, mode); 51 | intent.putExtra(EXTRA_SHOW_CAMERA, isShow); 52 | intent.putExtra(EXTRA_ENABLE_PREVIEW, enablePreview); 53 | intent.putExtra(EXTRA_ENABLE_CROP, enableCrop); 54 | activity.startActivityForResult(intent, REQUEST_IMAGE); 55 | } 56 | ``` 57 | 4) Receive result in your onActivityResult Method 58 | 59 | ``` java 60 | @Override 61 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 62 | if(resultCode == RESULT_OK && requestCode == ImageSelectorActivity.REQUEST_IMAGE){ 63 | ArrayList images = (ArrayList) data.getSerializableExtra(ImageSelectorActivity.REQUEST_OUTPUT); 64 | // do something 65 | } 66 | } 67 | ``` 68 | 69 | ## Thanks 70 | 71 | * [Glide](https://github.com/bumptech/glide) 72 | 73 | * [PhotoView](https://github.com/chrisbanes/PhotoView) 74 | 75 | * [simplecropview](https://github.com/IsseiAoki/SimpleCropView) 76 | 77 | ###License 78 | >The MIT License (MIT) 79 | 80 | >Copyright (c) 2015 Dee 81 | 82 | >Permission is hereby granted, free of charge, to any person obtaining a copy 83 | of this software and associated documentation files (the "Software"), to deal 84 | in the Software without restriction, including without limitation the rights 85 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 86 | copies of the Software, and to permit persons to whom the Software is 87 | furnished to do so, subject to the following conditions: 88 | 89 | >The above copyright notice and this permission notice shall be included in all 90 | copies or substantial portions of the Software. 91 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.5.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioneday/ImageSelector/c922b7ba9b50a5983d4e26ba88441225a58e967e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Nov 19 10:13:17 CST 2015 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-2.4-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 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2' 8 | classpath "org.jfrog.buildinfo:build-info-extractor-gradle:3.1.1" 9 | } 10 | } 11 | 12 | 13 | android { 14 | compileSdkVersion 22 15 | buildToolsVersion "22.0.1" 16 | 17 | defaultConfig { 18 | minSdkVersion 15 19 | targetSdkVersion 22 20 | versionCode 1 21 | versionName "1.0" 22 | } 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | sourceSets { 30 | main { 31 | manifest.srcFile 'src/main/AndroidManifest.xml' 32 | } 33 | } 34 | } 35 | 36 | dependencies { 37 | compile fileTree(include: ['*.jar'], dir: 'libs') 38 | testCompile 'junit:junit:4.12' 39 | compile 'com.android.support:appcompat-v7:22.2.1' 40 | compile 'com.android.support:recyclerview-v7:22.2.1' 41 | compile 'com.github.bumptech.glide:glide:3.6.1' 42 | compile 'com.commit451:PhotoView:1.2.4' 43 | compile 'com.isseiaoki:simplecropview:1.0.13' 44 | } 45 | -------------------------------------------------------------------------------- /library/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 | 20 | PROJ_GROUP=com.yongchun 21 | PROJ_VERSION=1.1.0 22 | PROJ_NAME=ImageSelector 23 | PROJ_WEBSITEURL=https://github.com/ioneday/ImageSelector 24 | PROJ_ISSUETRACKERURL= 25 | PROJ_VCSURL=https://github.com/ioneday/ImageSelector.git 26 | PROJ_DESCRIPTION=Mulitple ImageSelector 27 | PROJ_ARTIFACTID=com.yongchun.imageselector 28 | 29 | DEVELOPER_ID=dee 30 | DEVELOPER_NAME=LiYongChun 31 | DEVELOPER_EMAIL=heiyongchun@gmail.com 32 | -------------------------------------------------------------------------------- /library/library.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /library/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/dzq/android-sdk-macosx/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 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/yongchun/library/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 11 | 14 | 17 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/adapter/ImageFolderAdapter.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 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 com.bumptech.glide.Glide; 12 | import com.yongchun.library.R; 13 | import com.yongchun.library.model.LocalMedia; 14 | import com.yongchun.library.model.LocalMediaFolder; 15 | 16 | import java.io.File; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * Created by dee on 15/11/20. 22 | */ 23 | public class ImageFolderAdapter extends RecyclerView.Adapter{ 24 | private Context context; 25 | private List folders = new ArrayList<>(); 26 | private int checkedIndex = 0; 27 | 28 | private OnItemClickListener onItemClickListener; 29 | public ImageFolderAdapter(Context context) { 30 | this.context = context; 31 | } 32 | 33 | public void bindFolder(List folders){ 34 | this.folders = folders; 35 | notifyDataSetChanged(); 36 | } 37 | 38 | @Override 39 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 40 | View itemView = LayoutInflater.from(context).inflate(R.layout.item_folder,parent,false); 41 | return new ViewHolder(itemView); 42 | } 43 | 44 | @Override 45 | public void onBindViewHolder(ViewHolder holder, final int position) { 46 | final LocalMediaFolder folder = folders.get(position); 47 | Glide.with(context) 48 | .load(new File(folder.getFirstImagePath())) 49 | .placeholder(R.mipmap.ic_placeholder) 50 | .error(R.mipmap.ic_placeholder) 51 | .centerCrop() 52 | .into(holder.firstImage); 53 | holder.folderName.setText(folder.getName()); 54 | holder.imageNum.setText(context.getString(R.string.num_postfix,folder.getImageNum())); 55 | 56 | holder.isSelected.setVisibility(checkedIndex == position ? View.VISIBLE : View.GONE); 57 | 58 | holder.contentView.setOnClickListener(new View.OnClickListener() { 59 | @Override 60 | public void onClick(View v) { 61 | if (onItemClickListener != null) { 62 | checkedIndex = position; 63 | notifyDataSetChanged(); 64 | onItemClickListener.onItemClick(folder.getName(),folder.getImages()); 65 | } 66 | } 67 | }); 68 | } 69 | 70 | @Override 71 | public int getItemCount() { 72 | return folders.size(); 73 | } 74 | 75 | class ViewHolder extends RecyclerView.ViewHolder{ 76 | ImageView firstImage; 77 | TextView folderName; 78 | TextView imageNum; 79 | ImageView isSelected; 80 | 81 | View contentView; 82 | public ViewHolder(View itemView) { 83 | super(itemView); 84 | contentView = itemView; 85 | firstImage = (ImageView) itemView.findViewById(R.id.first_image); 86 | folderName = (TextView) itemView.findViewById(R.id.folder_name); 87 | imageNum = (TextView) itemView.findViewById(R.id.image_num); 88 | isSelected = (ImageView) itemView.findViewById(R.id.is_selected); 89 | } 90 | } 91 | public void setOnItemClickListener(OnItemClickListener onItemClickListener){ 92 | this.onItemClickListener = onItemClickListener; 93 | } 94 | public interface OnItemClickListener{ 95 | void onItemClick(String folderName,List images); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/adapter/ImageListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.adapter; 2 | 3 | import android.content.Context; 4 | import android.graphics.PorterDuff; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.Toast; 11 | 12 | import com.bumptech.glide.Glide; 13 | import com.yongchun.library.R; 14 | import com.yongchun.library.model.LocalMedia; 15 | import com.yongchun.library.view.ImageSelectorActivity; 16 | 17 | import java.io.File; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * Created by dee on 15/11/19. 23 | */ 24 | public class ImageListAdapter extends RecyclerView.Adapter { 25 | public static final int TYPE_CAMERA = 1; 26 | public static final int TYPE_PICTURE = 2; 27 | 28 | private Context context; 29 | private boolean showCamera = true; 30 | private boolean enablePreview = true; 31 | private int maxSelectNum; 32 | private int selectMode = ImageSelectorActivity.MODE_MULTIPLE; 33 | 34 | private List images = new ArrayList(); 35 | private List selectImages = new ArrayList(); 36 | 37 | private OnImageSelectChangedListener imageSelectChangedListener; 38 | 39 | public ImageListAdapter(Context context, int maxSelectNum, int mode, boolean showCamera, boolean enablePreview) { 40 | this.context = context; 41 | this.selectMode = mode; 42 | this.maxSelectNum = maxSelectNum; 43 | this.showCamera = showCamera; 44 | this.enablePreview = enablePreview; 45 | } 46 | 47 | public void bindImages(List images) { 48 | this.images = images; 49 | notifyDataSetChanged(); 50 | } 51 | 52 | public void bindSelectImages(List images) { 53 | this.selectImages = images; 54 | notifyDataSetChanged(); 55 | if (imageSelectChangedListener != null) { 56 | imageSelectChangedListener.onChange(selectImages); 57 | } 58 | } 59 | 60 | @Override 61 | public int getItemViewType(int position) { 62 | if (showCamera && position == 0) { 63 | return TYPE_CAMERA; 64 | } else { 65 | return TYPE_PICTURE; 66 | } 67 | } 68 | 69 | @Override 70 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 71 | if (viewType == TYPE_CAMERA) { 72 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_camera, parent, false); 73 | return new HeaderViewHolder(view); 74 | } else { 75 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_picture, parent, false); 76 | return new ViewHolder(view); 77 | } 78 | } 79 | 80 | @Override 81 | public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { 82 | if (getItemViewType(position) == TYPE_CAMERA) { 83 | HeaderViewHolder headerHolder = (HeaderViewHolder) holder; 84 | headerHolder.headerView.setOnClickListener(new View.OnClickListener() { 85 | @Override 86 | public void onClick(View v) { 87 | if (imageSelectChangedListener != null) { 88 | imageSelectChangedListener.onTakePhoto(); 89 | } 90 | } 91 | }); 92 | } else { 93 | final ViewHolder contentHolder = (ViewHolder) holder; 94 | final LocalMedia image = images.get(showCamera ? position - 1 : position); 95 | 96 | Glide.with(context) 97 | .load(new File(image.getPath())) 98 | .centerCrop() 99 | .thumbnail(0.5f) 100 | .placeholder(R.drawable.image_placeholder) 101 | .error(R.drawable.image_placeholder) 102 | .dontAnimate() 103 | .into(contentHolder.picture); 104 | 105 | if (selectMode == ImageSelectorActivity.MODE_SINGLE) { 106 | contentHolder.check.setVisibility(View.GONE); 107 | } 108 | 109 | selectImage(contentHolder, isSelected(image)); 110 | 111 | if (enablePreview) { 112 | contentHolder.check.setOnClickListener(new View.OnClickListener() { 113 | @Override 114 | public void onClick(View v) { 115 | changeCheckboxState(contentHolder, image); 116 | } 117 | }); 118 | } 119 | 120 | contentHolder.contentView.setOnClickListener(new View.OnClickListener() { 121 | @Override 122 | public void onClick(View v) { 123 | if ((selectMode == ImageSelectorActivity.MODE_SINGLE || enablePreview) && imageSelectChangedListener != null) { 124 | imageSelectChangedListener.onPictureClick(image, showCamera ? position - 1 : position); 125 | } else { 126 | changeCheckboxState(contentHolder, image); 127 | } 128 | } 129 | }); 130 | } 131 | } 132 | 133 | @Override 134 | public int getItemCount() { 135 | return showCamera ? images.size() + 1 : images.size(); 136 | } 137 | 138 | private void changeCheckboxState(ViewHolder contentHolder, LocalMedia image) { 139 | boolean isChecked = contentHolder.check.isSelected(); 140 | if (selectImages.size() >= maxSelectNum && !isChecked) { 141 | Toast.makeText(context, context.getString(R.string.message_max_num, maxSelectNum), Toast.LENGTH_LONG).show(); 142 | return; 143 | } 144 | if (isChecked) { 145 | for (LocalMedia media : selectImages) { 146 | if (media.getPath().equals(image.getPath())) { 147 | selectImages.remove(media); 148 | break; 149 | } 150 | } 151 | } else { 152 | selectImages.add(image); 153 | } 154 | selectImage(contentHolder, !isChecked); 155 | if (imageSelectChangedListener != null) { 156 | imageSelectChangedListener.onChange(selectImages); 157 | } 158 | } 159 | 160 | public List getSelectedImages() { 161 | return selectImages; 162 | } 163 | 164 | public List getImages() { 165 | return images; 166 | } 167 | 168 | public boolean isSelected(LocalMedia image) { 169 | for (LocalMedia media : selectImages) { 170 | if (media.getPath().equals(image.getPath())) { 171 | return true; 172 | } 173 | } 174 | return false; 175 | } 176 | 177 | public void selectImage(ViewHolder holder, boolean isChecked) { 178 | holder.check.setSelected(isChecked); 179 | if (isChecked) { 180 | holder.picture.setColorFilter(context.getResources().getColor(R.color.image_overlay2), PorterDuff.Mode.SRC_ATOP); 181 | } else { 182 | holder.picture.setColorFilter(context.getResources().getColor(R.color.image_overlay), PorterDuff.Mode.SRC_ATOP); 183 | } 184 | } 185 | 186 | static class HeaderViewHolder extends RecyclerView.ViewHolder { 187 | View headerView; 188 | 189 | public HeaderViewHolder(View itemView) { 190 | super(itemView); 191 | headerView = itemView; 192 | } 193 | } 194 | 195 | static class ViewHolder extends RecyclerView.ViewHolder { 196 | ImageView picture; 197 | ImageView check; 198 | 199 | View contentView; 200 | 201 | public ViewHolder(View itemView) { 202 | super(itemView); 203 | contentView = itemView; 204 | picture = (ImageView) itemView.findViewById(R.id.picture); 205 | check = (ImageView) itemView.findViewById(R.id.check); 206 | } 207 | 208 | } 209 | 210 | public interface OnImageSelectChangedListener { 211 | void onChange(List selectImages); 212 | 213 | void onTakePhoto(); 214 | 215 | void onPictureClick(LocalMedia media, int position); 216 | } 217 | 218 | public void setOnImageSelectChangedListener(OnImageSelectChangedListener imageSelectChangedListener) { 219 | this.imageSelectChangedListener = imageSelectChangedListener; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/model/LocalMedia.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by dee on 2015/8/5. 7 | */ 8 | public class LocalMedia implements Serializable { 9 | private String path; 10 | private long duration; 11 | private long lastUpdateAt; 12 | 13 | 14 | public LocalMedia(String path, long lastUpdateAt, long duration) { 15 | this.path = path; 16 | this.duration = duration; 17 | this.lastUpdateAt = lastUpdateAt; 18 | } 19 | 20 | public LocalMedia(String path) { 21 | this.path = path; 22 | } 23 | 24 | public String getPath() { 25 | return path; 26 | } 27 | 28 | public void setPath(String path) { 29 | this.path = path; 30 | } 31 | 32 | public long getLastUpdateAt() { 33 | return lastUpdateAt; 34 | } 35 | 36 | public void setLastUpdateAt(long lastUpdateAt) { 37 | this.lastUpdateAt = lastUpdateAt; 38 | } 39 | 40 | public long getDuration() { 41 | return duration; 42 | } 43 | public void setDuration(long duration) { 44 | this.duration = duration; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/model/LocalMediaFolder.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * Created by dee on 2015/8/5. 9 | */ 10 | public class LocalMediaFolder implements Serializable { 11 | private String name; 12 | private String path; 13 | private String firstImagePath; 14 | private int imageNum; 15 | private List images = new ArrayList(); 16 | 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public String getPath() { 27 | return path; 28 | } 29 | 30 | public void setPath(String path) { 31 | this.path = path; 32 | } 33 | 34 | public String getFirstImagePath() { 35 | return firstImagePath; 36 | } 37 | 38 | public void setFirstImagePath(String firstImagePath) { 39 | this.firstImagePath = firstImagePath; 40 | } 41 | 42 | public int getImageNum() { 43 | return imageNum; 44 | } 45 | 46 | public void setImageNum(int imageNum) { 47 | this.imageNum = imageNum; 48 | } 49 | 50 | public List getImages() { 51 | return images; 52 | } 53 | 54 | public void setImages(List images) { 55 | this.images = images; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/utils/CropUtil.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.utils; 2 | 3 | import android.content.ContentResolver; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.media.ExifInterface; 7 | import android.net.Uri; 8 | import android.os.ParcelFileDescriptor; 9 | import android.provider.MediaStore; 10 | import android.support.annotation.Nullable; 11 | import android.text.TextUtils; 12 | 13 | import java.io.Closeable; 14 | import java.io.File; 15 | import java.io.FileDescriptor; 16 | import java.io.FileInputStream; 17 | import java.io.FileOutputStream; 18 | import java.io.IOException; 19 | 20 | /* 21 | * Modified from original in AOSP. 22 | */ 23 | public class CropUtil { 24 | 25 | private static final String SCHEME_FILE = "file"; 26 | private static final String SCHEME_CONTENT = "content"; 27 | 28 | public static void closeSilently(@Nullable Closeable c) { 29 | if (c == null) return; 30 | try { 31 | c.close(); 32 | } catch (Throwable t) { 33 | // Do nothing 34 | } 35 | } 36 | 37 | public static int getExifRotation(File imageFile) { 38 | if (imageFile == null) return 0; 39 | try { 40 | ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath()); 41 | // We only recognize a subset of orientation tag values 42 | switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) { 43 | case ExifInterface.ORIENTATION_ROTATE_90: 44 | return 90; 45 | case ExifInterface.ORIENTATION_ROTATE_180: 46 | return 180; 47 | case ExifInterface.ORIENTATION_ROTATE_270: 48 | return 270; 49 | default: 50 | return ExifInterface.ORIENTATION_UNDEFINED; 51 | } 52 | } catch (IOException e) { 53 | return 0; 54 | } 55 | } 56 | 57 | public static boolean copyExifRotation(File sourceFile, File destFile) { 58 | if (sourceFile == null || destFile == null) return false; 59 | try { 60 | ExifInterface exifSource = new ExifInterface(sourceFile.getAbsolutePath()); 61 | ExifInterface exifDest = new ExifInterface(destFile.getAbsolutePath()); 62 | exifDest.setAttribute(ExifInterface.TAG_ORIENTATION, exifSource.getAttribute(ExifInterface.TAG_ORIENTATION)); 63 | exifDest.saveAttributes(); 64 | return true; 65 | } catch (IOException e) { 66 | return false; 67 | } 68 | } 69 | 70 | @Nullable 71 | public static File getFromMediaUri(Context context, ContentResolver resolver, Uri uri) { 72 | if (uri == null) return null; 73 | 74 | if (SCHEME_FILE.equals(uri.getScheme())) { 75 | return new File(uri.getPath()); 76 | } else if (SCHEME_CONTENT.equals(uri.getScheme())) { 77 | final String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME}; 78 | Cursor cursor = null; 79 | try { 80 | cursor = resolver.query(uri, filePathColumn, null, null, null); 81 | if (cursor != null && cursor.moveToFirst()) { 82 | final int columnIndex = (uri.toString().startsWith("content://com.google.android.gallery3d")) ? 83 | cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) : 84 | cursor.getColumnIndex(MediaStore.MediaColumns.DATA); 85 | // Picasa images on API 13+ 86 | if (columnIndex != -1) { 87 | String filePath = cursor.getString(columnIndex); 88 | if (!TextUtils.isEmpty(filePath)) { 89 | return new File(filePath); 90 | } 91 | } 92 | } 93 | } catch (IllegalArgumentException e) { 94 | // Google Drive images 95 | return getFromMediaUriPfd(context, resolver, uri); 96 | } catch (SecurityException ignored) { 97 | // Nothing we can do 98 | } finally { 99 | if (cursor != null) cursor.close(); 100 | } 101 | } 102 | return null; 103 | } 104 | 105 | private static String getTempFilename(Context context) throws IOException { 106 | File outputDir = context.getCacheDir(); 107 | File outputFile = File.createTempFile("image", "tmp", outputDir); 108 | return outputFile.getAbsolutePath(); 109 | } 110 | 111 | @Nullable 112 | private static File getFromMediaUriPfd(Context context, ContentResolver resolver, Uri uri) { 113 | if (uri == null) return null; 114 | 115 | FileInputStream input = null; 116 | FileOutputStream output = null; 117 | try { 118 | ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r"); 119 | FileDescriptor fd = pfd.getFileDescriptor(); 120 | input = new FileInputStream(fd); 121 | 122 | String tempFilename = getTempFilename(context); 123 | output = new FileOutputStream(tempFilename); 124 | 125 | int read; 126 | byte[] bytes = new byte[4096]; 127 | while ((read = input.read(bytes)) != -1) { 128 | output.write(bytes, 0, read); 129 | } 130 | return new File(tempFilename); 131 | } catch (IOException ignored) { 132 | // Nothing we can do 133 | } finally { 134 | closeSilently(input); 135 | closeSilently(output); 136 | } 137 | return null; 138 | } 139 | 140 | // public static void startBackgroundJob(MonitoredActivity activity, 141 | // String title, String message, Runnable job, Handler handler) { 142 | // // Make the progress dialog uncancelable, so that we can guarantee 143 | // // the thread will be done before the activity getting destroyed 144 | // ProgressDialog dialog = ProgressDialog.show( 145 | // activity, title, message, true, false); 146 | // new Thread(new BackgroundJob(activity, job, dialog, handler)).start(); 147 | // } 148 | 149 | // private static class BackgroundJob extends MonitoredActivity.LifeCycleAdapter implements Runnable { 150 | // 151 | // private final MonitoredActivity activity; 152 | // private final ProgressDialog dialog; 153 | // private final Runnable job; 154 | // private final Handler handler; 155 | // private final Runnable cleanupRunner = new Runnable() { 156 | // public void run() { 157 | // activity.removeLifeCycleListener(BackgroundJob.this); 158 | // if (dialog.getWindow() != null) dialog.dismiss(); 159 | // } 160 | // }; 161 | // 162 | // public BackgroundJob(MonitoredActivity activity, Runnable job, 163 | // ProgressDialog dialog, Handler handler) { 164 | // this.activity = activity; 165 | // this.dialog = dialog; 166 | // this.job = job; 167 | // this.activity.addLifeCycleListener(this); 168 | // this.handler = handler; 169 | // } 170 | // 171 | // public void run() { 172 | // try { 173 | // job.run(); 174 | // } finally { 175 | // handler.post(cleanupRunner); 176 | // } 177 | // } 178 | // 179 | // @Override 180 | // public void onActivityDestroyed(MonitoredActivity activity) { 181 | // // We get here only when the onDestroyed being called before 182 | // // the cleanupRunner. So, run it now and remove it from the queue 183 | // cleanupRunner.run(); 184 | // handler.removeCallbacks(cleanupRunner); 185 | // } 186 | // 187 | // @Override 188 | // public void onActivityStopped(MonitoredActivity activity) { 189 | // dialog.hide(); 190 | // } 191 | // 192 | // @Override 193 | // public void onActivityStarted(MonitoredActivity activity) { 194 | // dialog.show(); 195 | // } 196 | // } 197 | } 198 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.utils; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | 6 | import java.io.File; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | import java.util.Locale; 10 | 11 | /** 12 | * Created by dee on 15/11/20. 13 | */ 14 | public class FileUtils { 15 | public static final String POSTFIX = ".JPEG"; 16 | public static final String APP_NAME = "ImageSelector"; 17 | public static final String CAMERA_PATH = "/" + APP_NAME + "/CameraImage/"; 18 | public static final String CROP_PATH = "/" + APP_NAME + "/CropImage/"; 19 | 20 | public static File createCameraFile(Context context) { 21 | return createMediaFile(context,CAMERA_PATH); 22 | } 23 | public static File createCropFile(Context context) { 24 | return createMediaFile(context,CROP_PATH); 25 | } 26 | 27 | private static File createMediaFile(Context context,String parentPath){ 28 | String state = Environment.getExternalStorageState(); 29 | File rootDir = state.equals(Environment.MEDIA_MOUNTED)?Environment.getExternalStorageDirectory():context.getCacheDir(); 30 | 31 | File folderDir = new File(rootDir.getAbsolutePath() + parentPath); 32 | if (!folderDir.exists() && folderDir.mkdirs()){ 33 | 34 | } 35 | 36 | String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()); 37 | String fileName = APP_NAME + "_" + timeStamp + ""; 38 | File tmpFile = new File(folderDir, fileName + POSTFIX); 39 | return tmpFile; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/utils/GridSpacingItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.utils; 2 | 3 | import android.graphics.Rect; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | 7 | /** 8 | * Created by dee on 2015/8/18. 9 | */ 10 | public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { 11 | 12 | private int spanCount; 13 | private int spacing; 14 | private boolean includeEdge; 15 | 16 | public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) { 17 | this.spanCount = spanCount; 18 | this.spacing = spacing; 19 | this.includeEdge = includeEdge; 20 | } 21 | 22 | @Override 23 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 24 | int position = parent.getChildAdapterPosition(view); 25 | int column = position % spanCount; 26 | if (includeEdge) { 27 | outRect.left = spacing - column * spacing / spanCount; 28 | outRect.right = (column + 1) * spacing / spanCount; 29 | if (position < spanCount) { 30 | outRect.top = spacing; 31 | } 32 | outRect.bottom = spacing; 33 | } else { 34 | outRect.left = column * spacing / spanCount; 35 | outRect.right = spacing - (column + 1) * spacing / spanCount; 36 | // if (position >= spanCount) { 37 | // outRect.top = spacing; 38 | // } 39 | if (position < spanCount) { 40 | outRect.top = spacing; 41 | } 42 | outRect.bottom = spacing; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/utils/LocalMediaLoader.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.utils; 2 | 3 | import android.database.Cursor; 4 | import android.os.Bundle; 5 | import android.provider.MediaStore; 6 | import android.support.v4.app.FragmentActivity; 7 | import android.support.v4.app.LoaderManager; 8 | import android.support.v4.content.CursorLoader; 9 | import android.support.v4.content.Loader; 10 | 11 | import com.yongchun.library.model.LocalMedia; 12 | import com.yongchun.library.model.LocalMediaFolder; 13 | 14 | import java.io.File; 15 | import java.io.FilenameFilter; 16 | import java.util.ArrayList; 17 | import java.util.Collections; 18 | import java.util.Comparator; 19 | import java.util.HashSet; 20 | import java.util.List; 21 | 22 | /** 23 | * Created by dee on 15/11/19. 24 | */ 25 | public class LocalMediaLoader { 26 | // load type 27 | public static final int TYPE_IMAGE = 1; 28 | public static final int TYPE_VIDEO = 2; 29 | 30 | private final static String[] IMAGE_PROJECTION = { 31 | MediaStore.Images.Media.DATA, 32 | MediaStore.Images.Media.DISPLAY_NAME, 33 | MediaStore.Images.Media.DATE_ADDED, 34 | MediaStore.Images.Media._ID}; 35 | 36 | private final static String[] VIDEO_PROJECTION = { 37 | MediaStore.Video.Media.DATA, 38 | MediaStore.Video.Media.DISPLAY_NAME, 39 | MediaStore.Video.Media.DATE_ADDED, 40 | MediaStore.Video.Media._ID, 41 | MediaStore.Video.Media.DURATION}; 42 | 43 | private int type = TYPE_IMAGE; 44 | private FragmentActivity activity; 45 | 46 | public LocalMediaLoader(FragmentActivity activity, int type) { 47 | this.activity = activity; 48 | this.type = type; 49 | } 50 | 51 | HashSet mDirPaths = new HashSet(); 52 | 53 | public void loadAllImage(final LocalMediaLoadListener imageLoadListener) { 54 | activity.getSupportLoaderManager().initLoader(type, null, new LoaderManager.LoaderCallbacks() { 55 | @Override 56 | public Loader onCreateLoader(int id, Bundle args) { 57 | CursorLoader cursorLoader = null; 58 | if (id == TYPE_IMAGE) { 59 | cursorLoader = new CursorLoader( 60 | activity, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 61 | IMAGE_PROJECTION, MediaStore.Images.Media.MIME_TYPE + "=? or " 62 | + MediaStore.Images.Media.MIME_TYPE + "=?", 63 | new String[]{"image/jpeg", "image/png"}, IMAGE_PROJECTION[2] + " DESC"); 64 | } else if (id == TYPE_VIDEO) { 65 | cursorLoader = new CursorLoader( 66 | activity, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, 67 | VIDEO_PROJECTION, null, null, VIDEO_PROJECTION[2] + " DESC"); 68 | } 69 | return cursorLoader; 70 | } 71 | 72 | @Override 73 | public void onLoadFinished(Loader loader, Cursor data) { 74 | ArrayList imageFolders = new ArrayList(); 75 | LocalMediaFolder allImageFolder = new LocalMediaFolder(); 76 | List allImages = new ArrayList(); 77 | 78 | while (data != null && data.moveToNext()) { 79 | // 获取图片的路径 80 | String path = data.getString(data 81 | .getColumnIndex(MediaStore.Images.Media.DATA)); 82 | File file = new File(path); 83 | if (!file.exists()) 84 | continue; 85 | // 获取该图片的目录路径名 86 | File parentFile = file.getParentFile(); 87 | if (parentFile == null || !parentFile.exists()) 88 | continue; 89 | 90 | String dirPath = parentFile.getAbsolutePath(); 91 | // 利用一个HashSet防止多次扫描同一个文件夹 92 | if (mDirPaths.contains(dirPath)) { 93 | continue; 94 | } else { 95 | mDirPaths.add(dirPath); 96 | } 97 | 98 | if (parentFile.list() == null) 99 | continue; 100 | LocalMediaFolder localMediaFolder = getImageFolder(path, imageFolders); 101 | 102 | File[] files = parentFile.listFiles(new FilenameFilter() { 103 | @Override 104 | public boolean accept(File dir, String filename) { 105 | if (filename.endsWith(".jpg") 106 | || filename.endsWith(".png") 107 | || filename.endsWith(".jpeg")) 108 | return true; 109 | return false; 110 | } 111 | }); 112 | ArrayList images = new ArrayList<>(); 113 | for (int i = 0; i < files.length; i++) { 114 | File f = files[i]; 115 | LocalMedia localMedia = new LocalMedia(f.getAbsolutePath()); 116 | allImages.add(localMedia); 117 | images.add(localMedia); 118 | } 119 | if (images.size() > 0) { 120 | localMediaFolder.setImages(images); 121 | localMediaFolder.setImageNum(localMediaFolder.getImages().size()); 122 | imageFolders.add(localMediaFolder); 123 | } 124 | } 125 | 126 | allImageFolder.setImages(allImages); 127 | allImageFolder.setImageNum(allImageFolder.getImages().size()); 128 | allImageFolder.setFirstImagePath(allImages.get(0).getPath()); 129 | allImageFolder.setName(activity.getString(com.yongchun.library.R.string.all_image)); 130 | imageFolders.add(allImageFolder); 131 | sortFolder(imageFolders); 132 | imageLoadListener.loadComplete(imageFolders); 133 | if (data != null) data.close(); 134 | } 135 | 136 | @Override 137 | public void onLoaderReset(Loader loader) { 138 | } 139 | }); 140 | } 141 | 142 | private void sortFolder(List imageFolders) { 143 | // 文件夹按图片数量排序 144 | Collections.sort(imageFolders, new Comparator() { 145 | @Override 146 | public int compare(LocalMediaFolder lhs, LocalMediaFolder rhs) { 147 | if (lhs.getImages() == null || rhs.getImages() == null) { 148 | return 0; 149 | } 150 | int lsize = lhs.getImageNum(); 151 | int rsize = rhs.getImageNum(); 152 | return lsize == rsize ? 0 : (lsize < rsize ? 1 : -1); 153 | } 154 | }); 155 | } 156 | 157 | private LocalMediaFolder getImageFolder(String path, List imageFolders) { 158 | File imageFile = new File(path); 159 | File folderFile = imageFile.getParentFile(); 160 | 161 | for (LocalMediaFolder folder : imageFolders) { 162 | if (folder.getName().equals(folderFile.getName())) { 163 | return folder; 164 | } 165 | } 166 | LocalMediaFolder newFolder = new LocalMediaFolder(); 167 | newFolder.setName(folderFile.getName()); 168 | newFolder.setPath(folderFile.getAbsolutePath()); 169 | newFolder.setFirstImagePath(path); 170 | return newFolder; 171 | } 172 | 173 | public interface LocalMediaLoadListener { 174 | void loadComplete(List folders); 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/utils/ScreenUtils.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.util.DisplayMetrics; 6 | 7 | import java.lang.reflect.Field; 8 | 9 | /** 10 | * Created by dee on 15/11/19. 11 | */ 12 | public class ScreenUtils { 13 | /** 14 | * dp2px 15 | */ 16 | public static int dip2px(Context context, float dpValue) { 17 | final float scale = context.getResources().getDisplayMetrics().density; 18 | return (int) (dpValue * scale + 0.5f); 19 | } 20 | public static int getScreenWidth(Context context) { 21 | DisplayMetrics localDisplayMetrics = new DisplayMetrics(); 22 | ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics); 23 | return localDisplayMetrics.widthPixels; 24 | } 25 | public static int getScreenHeight(Context context) { 26 | DisplayMetrics localDisplayMetrics = new DisplayMetrics(); 27 | ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics); 28 | return localDisplayMetrics.heightPixels - getStatusBarHeight(context); 29 | } 30 | public static int getStatusBarHeight(Context context){ 31 | Class c = null; 32 | Object obj = null; 33 | Field field = null; 34 | int x = 0, statusBarHeight = 0; 35 | try { 36 | c = Class.forName("com.android.internal.R$dimen"); 37 | obj = c.newInstance(); 38 | field = c.getField("status_bar_height"); 39 | x = Integer.parseInt(field.get(obj).toString()); 40 | statusBarHeight = context.getResources().getDimensionPixelSize(x); 41 | } catch (Exception e1) { 42 | e1.printStackTrace(); 43 | } 44 | return statusBarHeight; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/view/FolderWindow.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.ColorDrawable; 8 | import android.graphics.drawable.Drawable; 9 | import android.support.v7.widget.LinearLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.animation.Animation; 14 | import android.view.animation.AnimationUtils; 15 | import android.widget.PopupWindow; 16 | 17 | import com.yongchun.library.R; 18 | import com.yongchun.library.adapter.ImageFolderAdapter; 19 | import com.yongchun.library.model.LocalMediaFolder; 20 | import com.yongchun.library.utils.ScreenUtils; 21 | 22 | import java.lang.reflect.Method; 23 | import java.util.List; 24 | 25 | /** 26 | * Created by dee on 15/11/20. 27 | */ 28 | public class FolderWindow extends PopupWindow { 29 | private Context context; 30 | private View window; 31 | private RecyclerView recyclerView; 32 | private ImageFolderAdapter adapter; 33 | 34 | private boolean isDismiss = false; 35 | public FolderWindow(Context context) { 36 | this.context = context; 37 | window = LayoutInflater.from(context).inflate(R.layout.window_folder, null); 38 | this.setContentView(window); 39 | this.setWidth(ScreenUtils.getScreenWidth(context)); 40 | this.setHeight(ScreenUtils.getScreenHeight(context) - ScreenUtils.dip2px(context, 96)); 41 | this.setAnimationStyle(R.style.WindowStyle); 42 | this.setFocusable(true); 43 | this.setOutsideTouchable(true); 44 | this.update(); 45 | this.setBackgroundDrawable(new ColorDrawable(Color.argb(153, 0, 0, 0))); 46 | 47 | initView(); 48 | registerListener(); 49 | setPopupWindowTouchModal(this, false); 50 | } 51 | 52 | public void initView() { 53 | adapter = new ImageFolderAdapter(context); 54 | 55 | recyclerView = (RecyclerView) window.findViewById(R.id.folder_list); 56 | recyclerView.addItemDecoration(new ItemDivider()); 57 | recyclerView.setLayoutManager(new LinearLayoutManager(context)); 58 | recyclerView.setAdapter(adapter); 59 | } 60 | 61 | public void registerListener() { 62 | 63 | } 64 | public void bindFolder(List folders){ 65 | adapter.bindFolder(folders); 66 | } 67 | @Override 68 | public void showAsDropDown(View anchor) { 69 | super.showAsDropDown(anchor); 70 | Animation animation = AnimationUtils.loadAnimation(context, R.anim.up_in); 71 | recyclerView.startAnimation(animation); 72 | } 73 | public void setOnItemClickListener(ImageFolderAdapter.OnItemClickListener onItemClickListener){ 74 | adapter.setOnItemClickListener(onItemClickListener); 75 | } 76 | @Override 77 | public void dismiss() { 78 | if(isDismiss){ 79 | return; 80 | } 81 | isDismiss = true; 82 | Animation animation = AnimationUtils.loadAnimation(context, R.anim.down_out); 83 | recyclerView.startAnimation(animation); 84 | dismiss(); 85 | animation.setAnimationListener(new Animation.AnimationListener() { 86 | @Override 87 | public void onAnimationStart(Animation animation) { 88 | 89 | } 90 | 91 | @Override 92 | public void onAnimationEnd(Animation animation) { 93 | isDismiss = false; 94 | FolderWindow.super.dismiss(); 95 | } 96 | @Override 97 | public void onAnimationRepeat(Animation animation) { 98 | 99 | } 100 | }); 101 | } 102 | 103 | public static void setPopupWindowTouchModal(PopupWindow popupWindow,boolean touchModal) { 104 | if (null == popupWindow) { 105 | return; 106 | } 107 | Method method; 108 | try { 109 | method = PopupWindow.class.getDeclaredMethod("setTouchModal",boolean.class); 110 | method.setAccessible(true); 111 | method.invoke(popupWindow, touchModal); 112 | } catch (Exception e) { 113 | e.printStackTrace(); 114 | } 115 | } 116 | public class ItemDivider extends RecyclerView.ItemDecoration { 117 | private Drawable mDrawable; 118 | public ItemDivider() { 119 | mDrawable = context.getResources().getDrawable(R.drawable.item_divider); 120 | } 121 | @Override 122 | public void onDrawOver(Canvas c, RecyclerView parent) { 123 | final int left = ScreenUtils.dip2px(parent.getContext(),16); 124 | final int right = parent.getWidth() - left; 125 | 126 | final int childCount = parent.getChildCount(); 127 | for (int i = 0; i < childCount - 1; i++) { 128 | final View child = parent.getChildAt(i); 129 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); 130 | final int top = child.getBottom() + params.bottomMargin; 131 | final int bottom = top + mDrawable.getIntrinsicHeight(); 132 | mDrawable.setBounds(left, top, right, bottom); 133 | mDrawable.draw(c); 134 | } 135 | } 136 | 137 | @Override 138 | public void getItemOffsets(Rect outRect, int position, RecyclerView parent) { 139 | outRect.set(0, 0, 0, mDrawable.getIntrinsicWidth()); 140 | } 141 | 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/view/ImageCropActivity.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.view; 2 | 3 | import android.app.Activity; 4 | import android.app.ProgressDialog; 5 | import android.content.Intent; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.graphics.Matrix; 9 | import android.net.Uri; 10 | import android.opengl.GLES10; 11 | import android.os.Bundle; 12 | import android.os.Handler; 13 | import android.support.v7.app.AppCompatActivity; 14 | import android.support.v7.widget.Toolbar; 15 | import android.view.View; 16 | import android.widget.TextView; 17 | 18 | import com.isseiaoki.simplecropview.CropImageView; 19 | import com.yongchun.library.R; 20 | import com.yongchun.library.utils.CropUtil; 21 | import com.yongchun.library.utils.FileUtils; 22 | 23 | import java.io.File; 24 | import java.io.IOException; 25 | import java.io.InputStream; 26 | import java.io.OutputStream; 27 | 28 | /** 29 | * Created by dee on 15/11/26. 30 | */ 31 | public class ImageCropActivity extends AppCompatActivity { 32 | public static final String EXTRA_PATH = "extraPath"; 33 | public static final String OUTPUT_PATH = "outputPath"; 34 | public static final int REQUEST_CROP = 69; 35 | private static final int SIZE_DEFAULT = 2048; 36 | private static final int SIZE_LIMIT = 4096; 37 | 38 | private Toolbar toolbar; 39 | private TextView doneText; 40 | private CropImageView cropImageView; 41 | 42 | 43 | private Uri sourceUri; 44 | private Uri saveUri; 45 | 46 | private final Handler handler = new Handler(); 47 | 48 | public static void startCrop(Activity activity, String path) { 49 | Intent intent = new Intent(activity, ImageCropActivity.class); 50 | intent.putExtra(EXTRA_PATH, path); 51 | activity.startActivityForResult(intent, REQUEST_CROP); 52 | } 53 | 54 | @Override 55 | protected void onCreate(Bundle savedInstanceState) { 56 | super.onCreate(savedInstanceState); 57 | setContentView(R.layout.activity_image_crop); 58 | String path = getIntent().getStringExtra(EXTRA_PATH); 59 | sourceUri = Uri.fromFile(new File(path)); 60 | 61 | initView(); 62 | registerListener(); 63 | } 64 | 65 | public void initView() { 66 | toolbar = (Toolbar) findViewById(R.id.toolbar); 67 | toolbar.setTitle(""); 68 | setSupportActionBar(toolbar); 69 | toolbar.setNavigationIcon(R.mipmap.ic_back); 70 | 71 | doneText = (TextView) findViewById(R.id.done_text); 72 | cropImageView = (CropImageView) findViewById(R.id.cropImageView); 73 | cropImageView.setHandleSizeInDp(10); 74 | 75 | 76 | int exifRotation = CropUtil.getExifRotation(CropUtil.getFromMediaUri(this, getContentResolver(), sourceUri)); 77 | 78 | InputStream is = null; 79 | try { 80 | int sampleSize = calculateBitmapSampleSize(sourceUri); 81 | is = getContentResolver().openInputStream(sourceUri); 82 | BitmapFactory.Options option = new BitmapFactory.Options(); 83 | option.inSampleSize = sampleSize; 84 | Bitmap sizeBitmap = BitmapFactory.decodeStream(is, null, option); 85 | if(sizeBitmap==null)return; 86 | Matrix matrix = getRotateMatrix(sizeBitmap, exifRotation % 360); 87 | Bitmap rotated = Bitmap.createBitmap(sizeBitmap, 0, 0, sizeBitmap.getWidth(), sizeBitmap.getHeight(), matrix, true); 88 | cropImageView.setImageBitmap(rotated); 89 | } catch (IOException e) { 90 | e.printStackTrace(); 91 | } catch (OutOfMemoryError e) { 92 | e.printStackTrace(); 93 | } finally { 94 | CropUtil.closeSilently(is); 95 | } 96 | } 97 | 98 | 99 | public void registerListener() { 100 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 101 | @Override 102 | public void onClick(View v) { 103 | finish(); 104 | } 105 | }); 106 | doneText.setOnClickListener(new View.OnClickListener() { 107 | @Override 108 | public void onClick(View v) { 109 | ProgressDialog.show( 110 | ImageCropActivity.this, null, getString(R.string.save_ing), true, false); 111 | saveUri = Uri.fromFile(FileUtils.createCropFile(ImageCropActivity.this)); 112 | saveOutput(cropImageView.getCroppedBitmap()); 113 | } 114 | }); 115 | } 116 | 117 | public Matrix getRotateMatrix(Bitmap bitmap, int rotation) { 118 | Matrix matrix = new Matrix(); 119 | if (bitmap != null && rotation != 0) { 120 | int cx = bitmap.getWidth() / 2; 121 | int cy = bitmap.getHeight() / 2; 122 | matrix.preTranslate(-cx, -cy); 123 | matrix.postRotate(rotation); 124 | matrix.postTranslate(bitmap.getWidth() / 2, bitmap.getHeight() / 2); 125 | } 126 | return matrix; 127 | } 128 | 129 | private int calculateBitmapSampleSize(Uri bitmapUri) throws IOException { 130 | InputStream is = null; 131 | BitmapFactory.Options options = new BitmapFactory.Options(); 132 | options.inJustDecodeBounds = true; 133 | try { 134 | is = getContentResolver().openInputStream(bitmapUri); 135 | BitmapFactory.decodeStream(is, null, options); // Just get image size 136 | } finally { 137 | CropUtil.closeSilently(is); 138 | } 139 | 140 | int maxSize = getMaxImageSize(); 141 | int sampleSize = 1; 142 | while (options.outHeight / sampleSize > maxSize || options.outWidth / sampleSize > maxSize) { 143 | sampleSize = sampleSize << 1; 144 | } 145 | return sampleSize; 146 | } 147 | 148 | private int getMaxImageSize() { 149 | int textureLimit = getMaxTextureSize(); 150 | if (textureLimit == 0) { 151 | return SIZE_DEFAULT; 152 | } else { 153 | return Math.min(textureLimit, SIZE_LIMIT); 154 | } 155 | } 156 | 157 | private int getMaxTextureSize() { 158 | int[] maxSize = new int[1]; 159 | GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0); 160 | return maxSize[0]; 161 | } 162 | 163 | private void saveOutput(Bitmap croppedImage) { 164 | if (saveUri != null) { 165 | OutputStream outputStream = null; 166 | try { 167 | outputStream = getContentResolver().openOutputStream(saveUri); 168 | if (outputStream != null) { 169 | croppedImage.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); 170 | } 171 | } catch (IOException e) { 172 | e.printStackTrace(); 173 | } finally { 174 | CropUtil.closeSilently(outputStream); 175 | } 176 | setResult(RESULT_OK, new Intent().putExtra(OUTPUT_PATH, saveUri.getPath())); 177 | } 178 | final Bitmap b = croppedImage; 179 | handler.post(new Runnable() { 180 | public void run() { 181 | b.recycle(); 182 | } 183 | }); 184 | finish(); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/view/ImagePreviewActivity.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.view; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentManager; 8 | import android.support.v4.app.FragmentPagerAdapter; 9 | import android.support.v4.view.ViewPager; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.support.v7.widget.Toolbar; 12 | import android.view.View; 13 | import android.view.WindowManager; 14 | import android.widget.CheckBox; 15 | import android.widget.LinearLayout; 16 | import android.widget.RelativeLayout; 17 | import android.widget.TextView; 18 | import android.widget.Toast; 19 | 20 | import com.yongchun.library.R; 21 | import com.yongchun.library.model.LocalMedia; 22 | import com.yongchun.library.widget.PreviewViewPager; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | /** 28 | * Created by dee on 15/11/24. 29 | */ 30 | public class ImagePreviewActivity extends AppCompatActivity { 31 | public static final int REQUEST_PREVIEW = 68; 32 | public static final String EXTRA_PREVIEW_LIST = "previewList"; 33 | public static final String EXTRA_PREVIEW_SELECT_LIST = "previewSelectList"; 34 | public static final String EXTRA_MAX_SELECT_NUM = "maxSelectNum"; 35 | public static final String EXTRA_POSITION = "position"; 36 | 37 | public static final String OUTPUT_LIST = "outputList"; 38 | public static final String OUTPUT_ISDONE = "isDone"; 39 | 40 | private LinearLayout barLayout; 41 | private RelativeLayout selectBarLayout; 42 | private Toolbar toolbar; 43 | private TextView doneText; 44 | private CheckBox checkboxSelect; 45 | private PreviewViewPager viewPager; 46 | 47 | 48 | private int position; 49 | private int maxSelectNum; 50 | private List images = new ArrayList<>(); 51 | private List selectImages = new ArrayList<>(); 52 | 53 | 54 | private boolean isShowBar = true; 55 | 56 | 57 | public static void startPreview(Activity context, List images, List selectImages, int maxSelectNum, int position) { 58 | Intent intent = new Intent(context, ImagePreviewActivity.class); 59 | intent.putExtra(EXTRA_PREVIEW_LIST, (ArrayList) images); 60 | intent.putExtra(EXTRA_PREVIEW_SELECT_LIST, (ArrayList) selectImages); 61 | intent.putExtra(EXTRA_POSITION, position); 62 | intent.putExtra(EXTRA_MAX_SELECT_NUM, maxSelectNum); 63 | context.startActivityForResult(intent, REQUEST_PREVIEW); 64 | } 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 70 | setContentView(R.layout.activity_image_preview); 71 | initView(); 72 | registerListener(); 73 | } 74 | 75 | public void initView() { 76 | images = (List) getIntent().getSerializableExtra(EXTRA_PREVIEW_LIST); 77 | selectImages = (List) getIntent().getSerializableExtra(EXTRA_PREVIEW_SELECT_LIST); 78 | maxSelectNum = getIntent().getIntExtra(EXTRA_MAX_SELECT_NUM, 9); 79 | position = getIntent().getIntExtra(EXTRA_POSITION, 1); 80 | 81 | barLayout = (LinearLayout) findViewById(R.id.bar_layout); 82 | selectBarLayout = (RelativeLayout) findViewById(R.id.select_bar_layout); 83 | 84 | toolbar = (Toolbar) findViewById(R.id.toolbar); 85 | toolbar.setTitle((position + 1) + "/" + images.size()); 86 | setSupportActionBar(toolbar); 87 | toolbar.setNavigationIcon(R.mipmap.ic_back); 88 | 89 | 90 | doneText = (TextView) findViewById(R.id.done_text); 91 | onSelectNumChange(); 92 | 93 | checkboxSelect = (CheckBox) findViewById(R.id.checkbox_select); 94 | onImageSwitch(position); 95 | 96 | 97 | viewPager = (PreviewViewPager) findViewById(R.id.preview_pager); 98 | viewPager.setAdapter(new SimpleFragmentAdapter(getSupportFragmentManager())); 99 | viewPager.setCurrentItem(position); 100 | } 101 | 102 | public void registerListener() { 103 | viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 104 | @Override 105 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 106 | } 107 | 108 | @Override 109 | public void onPageSelected(int position) { 110 | toolbar.setTitle(position + 1 + "/" + images.size()); 111 | onImageSwitch(position); 112 | } 113 | 114 | @Override 115 | public void onPageScrollStateChanged(int state) { 116 | } 117 | }); 118 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 119 | @Override 120 | public void onClick(View v) { 121 | onDoneClick(false); 122 | } 123 | }); 124 | checkboxSelect.setOnClickListener(new View.OnClickListener() { 125 | @Override 126 | public void onClick(View v) { 127 | boolean isChecked = checkboxSelect.isChecked(); 128 | if (selectImages.size() >= maxSelectNum && isChecked) { 129 | Toast.makeText(ImagePreviewActivity.this, getString(R.string.message_max_num, maxSelectNum), Toast.LENGTH_LONG).show(); 130 | checkboxSelect.setChecked(false); 131 | return; 132 | } 133 | LocalMedia image = images.get(viewPager.getCurrentItem()); 134 | if (isChecked) { 135 | selectImages.add(image); 136 | } else { 137 | for (LocalMedia media : selectImages) { 138 | if (media.getPath().equals(image.getPath())) { 139 | selectImages.remove(media); 140 | break; 141 | } 142 | } 143 | } 144 | onSelectNumChange(); 145 | } 146 | }); 147 | doneText.setOnClickListener(new View.OnClickListener() { 148 | @Override 149 | public void onClick(View v) { 150 | onDoneClick(true); 151 | } 152 | }); 153 | } 154 | 155 | public class SimpleFragmentAdapter extends FragmentPagerAdapter { 156 | public SimpleFragmentAdapter(FragmentManager fm) { 157 | super(fm); 158 | } 159 | 160 | @Override 161 | public Fragment getItem(int position) { 162 | return ImagePreviewFragment.getInstance(images.get(position).getPath()); 163 | } 164 | 165 | @Override 166 | public int getCount() { 167 | return images.size(); 168 | } 169 | } 170 | 171 | public void onSelectNumChange() { 172 | boolean enable = selectImages.size() != 0; 173 | doneText.setEnabled(enable); 174 | if (enable) { 175 | doneText.setText(getString(R.string.done_num, selectImages.size(), maxSelectNum)); 176 | } else { 177 | doneText.setText(R.string.done); 178 | } 179 | } 180 | 181 | public void onImageSwitch(int position) { 182 | checkboxSelect.setChecked(isSelected(images.get(position))); 183 | } 184 | 185 | public boolean isSelected(LocalMedia image) { 186 | for (LocalMedia media : selectImages) { 187 | if (media.getPath().equals(image.getPath())) { 188 | return true; 189 | } 190 | } 191 | return false; 192 | } 193 | 194 | private void hideStatusBar() { 195 | WindowManager.LayoutParams attrs = getWindow().getAttributes(); 196 | attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; 197 | getWindow().setAttributes(attrs); 198 | } 199 | 200 | private void showStatusBar() { 201 | WindowManager.LayoutParams attrs = getWindow().getAttributes(); 202 | attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN; 203 | getWindow().setAttributes(attrs); 204 | } 205 | 206 | public void switchBarVisibility() { 207 | barLayout.setVisibility(isShowBar ? View.GONE : View.VISIBLE); 208 | toolbar.setVisibility(isShowBar ? View.GONE : View.VISIBLE); 209 | selectBarLayout.setVisibility(isShowBar ? View.GONE : View.VISIBLE); 210 | if (isShowBar) { 211 | hideStatusBar(); 212 | } else { 213 | showStatusBar(); 214 | } 215 | isShowBar = !isShowBar; 216 | } 217 | public void onDoneClick(boolean isDone){ 218 | Intent intent = new Intent(); 219 | intent.putExtra(OUTPUT_LIST,(ArrayList)selectImages); 220 | intent.putExtra(OUTPUT_ISDONE,isDone); 221 | setResult(RESULT_OK,intent); 222 | finish(); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/view/ImagePreviewFragment.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.view; 2 | 3 | import android.graphics.Bitmap; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ImageView; 11 | 12 | import com.bumptech.glide.Glide; 13 | import com.bumptech.glide.request.animation.GlideAnimation; 14 | import com.bumptech.glide.request.target.SimpleTarget; 15 | import com.yongchun.library.R; 16 | 17 | import java.io.File; 18 | 19 | import uk.co.senab.photoview.PhotoViewAttacher; 20 | 21 | /** 22 | * Created by dee on 15/11/25. 23 | */ 24 | public class ImagePreviewFragment extends Fragment { 25 | public static final String PATH = "path"; 26 | 27 | public static ImagePreviewFragment getInstance(String path) { 28 | ImagePreviewFragment fragment = new ImagePreviewFragment(); 29 | Bundle bundle = new Bundle(); 30 | bundle.putString(PATH, path); 31 | fragment.setArguments(bundle); 32 | return fragment; 33 | } 34 | 35 | @Nullable 36 | @Override 37 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 38 | View contentView = inflater.inflate(R.layout.fragment_image_preview, container, false); 39 | final ImageView imageView = (ImageView) contentView.findViewById(R.id.preview_image); 40 | final PhotoViewAttacher mAttacher = new PhotoViewAttacher(imageView); 41 | Glide.with(container.getContext()) 42 | .load(new File(getArguments().getString(PATH))) 43 | .asBitmap() 44 | .into(new SimpleTarget(480, 800) { 45 | @Override 46 | public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 47 | imageView.setImageBitmap(resource); 48 | mAttacher.update(); 49 | } 50 | }); 51 | mAttacher.setOnViewTapListener(new PhotoViewAttacher.OnViewTapListener() { 52 | @Override 53 | public void onViewTap(View view, float x, float y) { 54 | ImagePreviewActivity activity = (ImagePreviewActivity) getActivity(); 55 | activity.switchBarVisibility(); 56 | } 57 | }); 58 | return contentView; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/view/ImageSelectorActivity.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.view; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import android.provider.MediaStore; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.GridLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.support.v7.widget.Toolbar; 12 | import android.view.View; 13 | import android.widget.LinearLayout; 14 | import android.widget.TextView; 15 | 16 | import com.yongchun.library.R; 17 | import com.yongchun.library.adapter.ImageFolderAdapter; 18 | import com.yongchun.library.adapter.ImageListAdapter; 19 | import com.yongchun.library.model.LocalMedia; 20 | import com.yongchun.library.model.LocalMediaFolder; 21 | import com.yongchun.library.utils.FileUtils; 22 | import com.yongchun.library.utils.GridSpacingItemDecoration; 23 | import com.yongchun.library.utils.LocalMediaLoader; 24 | import com.yongchun.library.utils.ScreenUtils; 25 | 26 | import java.io.File; 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | /** 31 | * Created by dee on 15/11/19. 32 | */ 33 | public class ImageSelectorActivity extends AppCompatActivity { 34 | public final static int REQUEST_IMAGE = 66; 35 | public final static int REQUEST_CAMERA = 67; 36 | 37 | public final static String BUNDLE_CAMERA_PATH = "CameraPath"; 38 | 39 | public final static String REQUEST_OUTPUT = "outputList"; 40 | 41 | public final static String EXTRA_SELECT_MODE = "SelectMode"; 42 | public final static String EXTRA_SHOW_CAMERA = "ShowCamera"; 43 | public final static String EXTRA_ENABLE_PREVIEW = "EnablePreview"; 44 | public final static String EXTRA_ENABLE_CROP = "EnableCrop"; 45 | public final static String EXTRA_MAX_SELECT_NUM = "MaxSelectNum"; 46 | 47 | public final static int MODE_MULTIPLE = 1; 48 | public final static int MODE_SINGLE = 2; 49 | 50 | private int maxSelectNum = 9; 51 | private int selectMode = MODE_MULTIPLE; 52 | private boolean showCamera = true; 53 | private boolean enablePreview = true; 54 | private boolean enableCrop = false; 55 | 56 | private int spanCount = 3; 57 | 58 | private Toolbar toolbar; 59 | private TextView doneText; 60 | 61 | private TextView previewText; 62 | 63 | private RecyclerView recyclerView; 64 | private ImageListAdapter imageAdapter; 65 | 66 | private LinearLayout folderLayout; 67 | private TextView folderName; 68 | private FolderWindow folderWindow; 69 | 70 | private String cameraPath; 71 | 72 | public static void start(Activity activity, int maxSelectNum, int mode, boolean isShow, boolean enablePreview, boolean enableCrop) { 73 | Intent intent = new Intent(activity, ImageSelectorActivity.class); 74 | intent.putExtra(EXTRA_MAX_SELECT_NUM, maxSelectNum); 75 | intent.putExtra(EXTRA_SELECT_MODE, mode); 76 | intent.putExtra(EXTRA_SHOW_CAMERA, isShow); 77 | intent.putExtra(EXTRA_ENABLE_PREVIEW, enablePreview); 78 | intent.putExtra(EXTRA_ENABLE_CROP, enableCrop); 79 | activity.startActivityForResult(intent, REQUEST_IMAGE); 80 | } 81 | 82 | @Override 83 | protected void onCreate(Bundle savedInstanceState) { 84 | super.onCreate(savedInstanceState); 85 | setContentView(R.layout.activity_imageselector); 86 | 87 | maxSelectNum = getIntent().getIntExtra(EXTRA_MAX_SELECT_NUM, 9); 88 | selectMode = getIntent().getIntExtra(EXTRA_SELECT_MODE, MODE_MULTIPLE); 89 | showCamera = getIntent().getBooleanExtra(EXTRA_SHOW_CAMERA, true); 90 | enablePreview = getIntent().getBooleanExtra(EXTRA_ENABLE_PREVIEW, true); 91 | enableCrop = getIntent().getBooleanExtra(EXTRA_ENABLE_CROP, false); 92 | 93 | if (selectMode == MODE_MULTIPLE) { 94 | enableCrop = false; 95 | } else { 96 | enablePreview = false; 97 | } 98 | if (savedInstanceState != null) { 99 | cameraPath = savedInstanceState.getString(BUNDLE_CAMERA_PATH); 100 | } 101 | initView(); 102 | registerListener(); 103 | new LocalMediaLoader(this, LocalMediaLoader.TYPE_IMAGE).loadAllImage(new LocalMediaLoader.LocalMediaLoadListener() { 104 | 105 | @Override 106 | public void loadComplete(List folders) { 107 | folderWindow.bindFolder(folders); 108 | imageAdapter.bindImages(folders.get(0).getImages()); 109 | } 110 | }); 111 | } 112 | 113 | public void initView() { 114 | folderWindow = new FolderWindow(this); 115 | 116 | toolbar = (Toolbar) findViewById(R.id.toolbar); 117 | toolbar.setTitle(R.string.picture); 118 | setSupportActionBar(toolbar); 119 | toolbar.setNavigationIcon(R.mipmap.ic_back); 120 | 121 | doneText = (TextView) findViewById(R.id.done_text); 122 | doneText.setVisibility(selectMode == MODE_MULTIPLE ? View.VISIBLE : View.GONE); 123 | 124 | previewText = (TextView) findViewById(R.id.preview_text); 125 | previewText.setVisibility(enablePreview ? View.VISIBLE : View.GONE); 126 | 127 | folderLayout = (LinearLayout) findViewById(R.id.folder_layout); 128 | folderName = (TextView) findViewById(R.id.folder_name); 129 | 130 | recyclerView = (RecyclerView) findViewById(R.id.folder_list); 131 | recyclerView.setHasFixedSize(true); 132 | recyclerView.addItemDecoration(new GridSpacingItemDecoration(spanCount, ScreenUtils.dip2px(this, 2), false)); 133 | recyclerView.setLayoutManager(new GridLayoutManager(this, spanCount)); 134 | 135 | imageAdapter = new ImageListAdapter(this, maxSelectNum, selectMode, showCamera,enablePreview); 136 | recyclerView.setAdapter(imageAdapter); 137 | 138 | } 139 | 140 | public void registerListener() { 141 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 142 | @Override 143 | public void onClick(View v) { 144 | finish(); 145 | } 146 | }); 147 | folderLayout.setOnClickListener(new View.OnClickListener() { 148 | @Override 149 | public void onClick(View v) { 150 | if (folderWindow.isShowing()) { 151 | folderWindow.dismiss(); 152 | } else { 153 | folderWindow.showAsDropDown(toolbar); 154 | } 155 | } 156 | }); 157 | imageAdapter.setOnImageSelectChangedListener(new ImageListAdapter.OnImageSelectChangedListener() { 158 | @Override 159 | public void onChange(List selectImages) { 160 | boolean enable = selectImages.size() != 0; 161 | doneText.setEnabled(enable ? true : false); 162 | previewText.setEnabled(enable ? true : false); 163 | if (enable) { 164 | doneText.setText(getString(R.string.done_num, selectImages.size(), maxSelectNum)); 165 | previewText.setText(getString(R.string.preview_num, selectImages.size())); 166 | } else { 167 | doneText.setText(R.string.done); 168 | previewText.setText(R.string.preview); 169 | } 170 | } 171 | 172 | @Override 173 | public void onTakePhoto() { 174 | startCamera(); 175 | } 176 | 177 | @Override 178 | public void onPictureClick(LocalMedia media, int position) { 179 | if (enablePreview) { 180 | startPreview(imageAdapter.getImages(), position); 181 | } else if (enableCrop) { 182 | startCrop(media.getPath()); 183 | } else { 184 | onSelectDone(media.getPath()); 185 | } 186 | } 187 | }); 188 | doneText.setOnClickListener(new View.OnClickListener() { 189 | @Override 190 | public void onClick(View v) { 191 | onSelectDone(imageAdapter.getSelectedImages()); 192 | } 193 | }); 194 | folderWindow.setOnItemClickListener(new ImageFolderAdapter.OnItemClickListener() { 195 | @Override 196 | public void onItemClick(String name, List images) { 197 | folderWindow.dismiss(); 198 | imageAdapter.bindImages(images); 199 | folderName.setText(name); 200 | } 201 | }); 202 | previewText.setOnClickListener(new View.OnClickListener() { 203 | @Override 204 | public void onClick(View v) { 205 | startPreview(imageAdapter.getSelectedImages(), 0); 206 | } 207 | }); 208 | } 209 | 210 | 211 | @Override 212 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 213 | if (resultCode == RESULT_OK) { 214 | // on take photo success 215 | if (requestCode == REQUEST_CAMERA) { 216 | sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(cameraPath)))); 217 | if (enableCrop) { 218 | startCrop(cameraPath); 219 | } else { 220 | onSelectDone(cameraPath); 221 | } 222 | } 223 | //on preview select change 224 | else if (requestCode == ImagePreviewActivity.REQUEST_PREVIEW) { 225 | boolean isDone = data.getBooleanExtra(ImagePreviewActivity.OUTPUT_ISDONE, false); 226 | List images = (List) data.getSerializableExtra(ImagePreviewActivity.OUTPUT_LIST); 227 | if (isDone) { 228 | onSelectDone(images); 229 | }else{ 230 | imageAdapter.bindSelectImages(images); 231 | } 232 | } 233 | // on crop success 234 | else if (requestCode == ImageCropActivity.REQUEST_CROP) { 235 | String path = data.getStringExtra(ImageCropActivity.OUTPUT_PATH); 236 | onSelectDone(path); 237 | } 238 | } 239 | } 240 | 241 | @Override 242 | protected void onSaveInstanceState(Bundle outState) { 243 | outState.putString(BUNDLE_CAMERA_PATH, cameraPath); 244 | } 245 | 246 | /** 247 | * start to camera、preview、crop 248 | */ 249 | public void startCamera() { 250 | Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 251 | if (cameraIntent.resolveActivity(getPackageManager()) != null) { 252 | File cameraFile = FileUtils.createCameraFile(this); 253 | cameraPath = cameraFile.getAbsolutePath(); 254 | cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cameraFile)); 255 | startActivityForResult(cameraIntent, REQUEST_CAMERA); 256 | } 257 | } 258 | 259 | public void startPreview(List previewImages, int position) { 260 | ImagePreviewActivity.startPreview(this, previewImages, imageAdapter.getSelectedImages(), maxSelectNum, position); 261 | } 262 | 263 | public void startCrop(String path) { 264 | ImageCropActivity.startCrop(this, path); 265 | } 266 | 267 | /** 268 | * on select done 269 | * 270 | * @param medias 271 | */ 272 | public void onSelectDone(List medias) { 273 | ArrayList images = new ArrayList<>(); 274 | for (LocalMedia media : medias) { 275 | images.add(media.getPath()); 276 | } 277 | onResult(images); 278 | } 279 | 280 | public void onSelectDone(String path) { 281 | ArrayList images = new ArrayList<>(); 282 | images.add(path); 283 | onResult(images); 284 | } 285 | 286 | public void onResult(ArrayList images) { 287 | setResult(RESULT_OK, new Intent().putStringArrayListExtra(REQUEST_OUTPUT, images)); 288 | finish(); 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/widget/PreviewViewPager.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.MotionEvent; 6 | 7 | /** 8 | * Created by dee on 15/11/24. 9 | */ 10 | public class PreviewViewPager extends android.support.v4.view.ViewPager { 11 | 12 | public PreviewViewPager(Context context) { 13 | super(context); 14 | } 15 | 16 | public PreviewViewPager(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | @Override 21 | public boolean onTouchEvent(MotionEvent ev) { 22 | try { 23 | return super.onTouchEvent(ev); 24 | } catch (IllegalArgumentException ex) { 25 | ex.printStackTrace(); 26 | } 27 | return false; 28 | } 29 | 30 | @Override 31 | public boolean onInterceptTouchEvent(MotionEvent ev) { 32 | try { 33 | return super.onInterceptTouchEvent(ev); 34 | } catch (IllegalArgumentException ex) { 35 | ex.printStackTrace(); 36 | } 37 | return false; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /library/src/main/java/com/yongchun/library/widget/SquareRelativeLayout.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library.widget; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.util.AttributeSet; 7 | import android.widget.RelativeLayout; 8 | 9 | /** 10 | * Created by dee on 15/11/19. 11 | */ 12 | public class SquareRelativeLayout extends RelativeLayout { 13 | 14 | public SquareRelativeLayout(Context context) { 15 | super(context); 16 | } 17 | 18 | public SquareRelativeLayout(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 23 | super(context, attrs, defStyleAttr); 24 | } 25 | 26 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 27 | public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 28 | super(context, attrs, defStyleAttr, defStyleRes); 29 | } 30 | 31 | @Override 32 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 33 | // Set a square layout. 34 | super.onMeasure(widthMeasureSpec, widthMeasureSpec); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /library/src/main/res/anim/down_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/anim/up_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/color/white_text_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/btn_round_green_disable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/btn_round_green_enable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/btn_round_green_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/check_green.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/checkbox_checktor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/checkbox_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/image_placeholder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/item_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/radio_button.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /library/src/main/res/layout/activity_image_crop.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 27 | 28 | 29 | 34 | -------------------------------------------------------------------------------- /library/src/main/res/layout/activity_image_preview.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 17 | 18 | 22 | 23 | 29 | 30 | 42 | 43 | 44 | 45 | 52 | 53 | 54 | 66 | 67 | -------------------------------------------------------------------------------- /library/src/main/res/layout/activity_imageselector.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 26 | 27 | 28 | 34 | 35 | 41 | 42 | 50 | 51 | 52 | 59 | 60 | 66 | 67 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /library/src/main/res/layout/fragment_image_preview.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /library/src/main/res/layout/item_camera.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 14 | 22 | -------------------------------------------------------------------------------- /library/src/main/res/layout/item_folder.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 24 | 25 | 32 | 33 | 40 | 41 | 42 | 47 | -------------------------------------------------------------------------------- /library/src/main/res/layout/item_picture.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 17 | -------------------------------------------------------------------------------- /library/src/main/res/layout/window_folder.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 | -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xhdpi/ic_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioneday/ImageSelector/c922b7ba9b50a5983d4e26ba88441225a58e967e/library/src/main/res/mipmap-xhdpi/ic_back.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xhdpi/ic_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioneday/ImageSelector/c922b7ba9b50a5983d4e26ba88441225a58e967e/library/src/main/res/mipmap-xhdpi/ic_camera.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xxhdpi/ic_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioneday/ImageSelector/c922b7ba9b50a5983d4e26ba88441225a58e967e/library/src/main/res/mipmap-xxhdpi/ic_arrow.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xxhdpi/ic_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioneday/ImageSelector/c922b7ba9b50a5983d4e26ba88441225a58e967e/library/src/main/res/mipmap-xxhdpi/ic_check.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xxhdpi/ic_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioneday/ImageSelector/c922b7ba9b50a5983d4e26ba88441225a58e967e/library/src/main/res/mipmap-xxhdpi/ic_checked.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xxhdpi/ic_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioneday/ImageSelector/c922b7ba9b50a5983d4e26ba88441225a58e967e/library/src/main/res/mipmap-xxhdpi/ic_placeholder.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xxhdpi/ic_radio_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioneday/ImageSelector/c922b7ba9b50a5983d4e26ba88441225a58e967e/library/src/main/res/mipmap-xxhdpi/ic_radio_button.png -------------------------------------------------------------------------------- /library/src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 所有图片 4 | 图片 5 | 拍摄照片 6 | 预览 7 | 预览(%1$s) 8 | 选择 9 | 完成 10 | 使用 11 | 完成(%1$s/%2$s) 12 | %1$s张 13 | 你最多可以选择%1$s张图片 14 | 保存图片中… 15 | -------------------------------------------------------------------------------- /library/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | #ADFFFFFF 5 | #000000 6 | #333333 7 | #43c117 8 | #AD43c117 9 | #393a3e 10 | #dd393a3e 11 | #999999 12 | #dcdcdc 13 | #353535 14 | #20000000 15 | #80000000 16 | -------------------------------------------------------------------------------- /library/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2dp 4 | 48dp 5 | 32dp 6 | 8dp 7 | 16dp 8 | 25dp 9 | 74dp 10 | 52dp 11 | 12 | 17sp 13 | 15sp 14 | 13sp 15 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Library 3 | Images 4 | All Images 5 | Take Photo 6 | Preview 7 | Preview(%1$s) 8 | Select 9 | Done 10 | OK 11 | %1$s 12 | Done(%1$s/%2$s) 13 | Up to %1$s photos can be selected 14 | Saving picture… 15 | 16 | -------------------------------------------------------------------------------- /library/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 20 | 21 | 25 | 26 | -------------------------------------------------------------------------------- /library/src/test/java/com/yongchun/library/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.library; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/Example-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioneday/ImageSelector/c922b7ba9b50a5983d4e26ba88441225a58e967e/sample/Example-release.apk -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.yongchun.multiimageselector" 9 | minSdkVersion 15 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:22.2.1' 26 | compile project(':library') 27 | } 28 | -------------------------------------------------------------------------------- /sample/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/dzq/android-sdk-macosx/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 | -------------------------------------------------------------------------------- /sample/sample.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /sample/src/androidTest/java/com/yongchun/multiimageselector/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.multiimageselector; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sample/src/main/java/com/yongchun/multiimageselector/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.multiimageselector; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.Button; 8 | import android.widget.EditText; 9 | import android.widget.ImageButton; 10 | import android.widget.RadioGroup; 11 | 12 | import com.yongchun.library.view.ImageSelectorActivity; 13 | 14 | import java.util.ArrayList; 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | private ImageButton minus; 18 | private ImageButton plus; 19 | private EditText selectNumText; 20 | 21 | private RadioGroup selectMode; 22 | private RadioGroup showCamera; 23 | private RadioGroup enablePreview; 24 | private RadioGroup enableCrop; 25 | 26 | private Button selectPicture; 27 | 28 | private int maxSelectNum = 9; 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_main); 33 | 34 | initView(); 35 | registerListener(); 36 | 37 | } 38 | public void initView(){ 39 | minus = (ImageButton) findViewById(R.id.minus); 40 | plus = (ImageButton) findViewById(R.id.plus); 41 | selectNumText = (EditText) findViewById(R.id.select_num); 42 | 43 | selectMode = (RadioGroup) findViewById(R.id.select_mode); 44 | showCamera = (RadioGroup) findViewById(R.id.show_camera); 45 | enablePreview = (RadioGroup) findViewById(R.id.enable_preview); 46 | enableCrop = (RadioGroup) findViewById(R.id.enable_crop); 47 | 48 | selectPicture = (Button) findViewById(R.id.select_picture); 49 | } 50 | public void registerListener(){ 51 | selectMode.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { 52 | @Override 53 | public void onCheckedChanged(RadioGroup group, int checkedId) { 54 | if(checkedId == R.id.mode_single){ 55 | enableCrop.check(R.id.crop_enable); 56 | findViewById(R.id.crop_enable).setEnabled(true); 57 | 58 | enablePreview.check(R.id.preview_disable); 59 | findViewById(R.id.preview_enable).setEnabled(false); 60 | }else{ 61 | enableCrop.check(R.id.crop_disable); 62 | findViewById(R.id.crop_enable).setEnabled(false); 63 | 64 | enablePreview.check(R.id.preview_enable); 65 | findViewById(R.id.preview_enable).setEnabled(true); 66 | } 67 | } 68 | }); 69 | minus.setOnClickListener(new View.OnClickListener() { 70 | @Override 71 | public void onClick(View v) { 72 | maxSelectNum --; 73 | selectNumText.setText(maxSelectNum+""); 74 | } 75 | }); 76 | plus.setOnClickListener(new View.OnClickListener() { 77 | @Override 78 | public void onClick(View v) { 79 | maxSelectNum ++; 80 | selectNumText.setText(maxSelectNum+""); 81 | } 82 | }); 83 | selectPicture.setOnClickListener(new View.OnClickListener() { 84 | @Override 85 | public void onClick(View v) { 86 | int mode = selectMode.getCheckedRadioButtonId()==R.id.mode_multiple?ImageSelectorActivity.MODE_MULTIPLE:ImageSelectorActivity.MODE_SINGLE; 87 | boolean isShow = showCamera.getCheckedRadioButtonId()==R.id.camera_yes?true:false; 88 | boolean isPreview = enablePreview.getCheckedRadioButtonId()==R.id.preview_enable?true:false; 89 | boolean isCrop = enableCrop.getCheckedRadioButtonId()==R.id.crop_enable?true:false; 90 | 91 | ImageSelectorActivity.start(MainActivity.this, maxSelectNum, mode, isShow,isPreview,isCrop); 92 | } 93 | }); 94 | } 95 | 96 | @Override 97 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 98 | if(resultCode == RESULT_OK && requestCode == ImageSelectorActivity.REQUEST_IMAGE){ 99 | ArrayList images = (ArrayList) data.getSerializableExtra(ImageSelectorActivity.REQUEST_OUTPUT); 100 | startActivity(new Intent(this,SelectResultActivity.class).putExtra(SelectResultActivity.EXTRA_IMAGES,images)); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /sample/src/main/java/com/yongchun/multiimageselector/SelectResultActivity.java: -------------------------------------------------------------------------------- 1 | package com.yongchun.multiimageselector; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.support.v7.widget.GridLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ImageView; 11 | 12 | import com.bumptech.glide.Glide; 13 | 14 | import java.io.File; 15 | import java.util.ArrayList; 16 | 17 | /** 18 | * Created by dee on 15/11/27. 19 | */ 20 | public class SelectResultActivity extends AppCompatActivity { 21 | public static final String EXTRA_IMAGES = "extraImages"; 22 | private RecyclerView resultRecyclerView; 23 | private ImageView singleImageView; 24 | 25 | private ArrayList images = new ArrayList<>(); 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_result); 31 | 32 | initView(); 33 | } 34 | 35 | public void initView() { 36 | images = (ArrayList) getIntent().getSerializableExtra(EXTRA_IMAGES); 37 | singleImageView = (ImageView) findViewById(R.id.single_image); 38 | 39 | resultRecyclerView = (RecyclerView) findViewById(R.id.result_recycler); 40 | resultRecyclerView.setLayoutManager(new GridLayoutManager(this, 3)); 41 | 42 | if (images.size() == 1) { 43 | resultRecyclerView.setVisibility(View.GONE); 44 | Glide.with(SelectResultActivity.this) 45 | .load(new File(images.get(0))) 46 | .into(singleImageView); 47 | } else { 48 | singleImageView.setVisibility(View.GONE); 49 | resultRecyclerView.setAdapter(new GridAdapter()); 50 | } 51 | 52 | } 53 | 54 | private class GridAdapter extends RecyclerView.Adapter { 55 | 56 | @Override 57 | public GridAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 58 | View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_result, parent, false); 59 | return new ViewHolder(itemView); 60 | } 61 | 62 | @Override 63 | public void onBindViewHolder(GridAdapter.ViewHolder holder, int position) { 64 | Glide.with(SelectResultActivity.this) 65 | .load(new File(images.get(position))) 66 | .centerCrop() 67 | .into(holder.imageView); 68 | } 69 | 70 | @Override 71 | public int getItemCount() { 72 | return images.size(); 73 | } 74 | 75 | class ViewHolder extends RecyclerView.ViewHolder { 76 | ImageView imageView; 77 | 78 | public ViewHolder(View itemView) { 79 | super(itemView); 80 | imageView = (ImageView) itemView.findViewById(R.id.image); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 19 | 20 | 21 | 22 | 28 | 29 | 37 | 38 | 44 | 45 | 52 | 53 | 54 | 60 | 61 | 69 | 70 | 76 | 77 | 84 | 85 | 86 | 92 | 93 | 101 | 102 | 108 | 109 | 116 | 117 | 118 | 124 | 125 | 133 | 134 | 141 | 142 | 149 | 150 | 151 |