├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── imnjh │ │ └── imagepicker │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── imnjh │ │ │ └── imagepickersample │ │ │ ├── MainActivity.java │ │ │ ├── ScaleTypeFillCenterInside.java │ │ │ ├── SingleFileLimitInterceptor.java │ │ │ ├── adapter │ │ │ └── PickAdapter.java │ │ │ ├── app │ │ │ └── PickerApplication.java │ │ │ ├── cache │ │ │ ├── Cache.java │ │ │ ├── CacheManager.java │ │ │ ├── InnerCache.java │ │ │ └── LocalCache.java │ │ │ ├── imageloader │ │ │ ├── FrescoImageLoader.java │ │ │ └── GlideImageLoader.java │ │ │ └── widget │ │ │ └── ExpandGridView.java │ └── res │ │ ├── drawable │ │ └── image_bg.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── image_item.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── imnjh │ └── imagepicker │ └── ExampleUnitTest.java ├── build.gradle ├── imagepicker ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── imnjh │ │ └── imagepicker │ │ ├── CapturePhotoHelper.java │ │ ├── FileChooseInterceptor.java │ │ ├── ImageLoader.java │ │ ├── PhotoLoadListener.java │ │ ├── PickerAction.java │ │ ├── PickerConfig.java │ │ ├── SImagePicker.java │ │ ├── activity │ │ ├── BasePickerActivity.java │ │ ├── CaptureConfirmActivity.java │ │ ├── CaptureTempActivity.java │ │ ├── CropImageActivity.java │ │ ├── PhotoPickerActivity.java │ │ └── PickerPreviewActivity.java │ │ ├── adapter │ │ ├── AlbumAdapter.java │ │ ├── BaseRecycleCursorAdapter.java │ │ ├── CommonHeaderFooterAdapter.java │ │ └── PhotoAdapter.java │ │ ├── control │ │ ├── AlbumController.java │ │ ├── BaseLoaderController.java │ │ └── PhotoController.java │ │ ├── loader │ │ ├── AlbumLoader.java │ │ └── PhotoLoader.java │ │ ├── model │ │ ├── Album.java │ │ └── Photo.java │ │ ├── util │ │ ├── CollectionUtils.java │ │ ├── DeviceCompat.java │ │ ├── FileUtil.java │ │ ├── ImageUtil.java │ │ ├── LogUtils.java │ │ ├── SystemUtil.java │ │ └── UriUtil.java │ │ └── widget │ │ ├── CheckBox.java │ │ ├── ClipImageBorderView.java │ │ ├── ClipImageLayout.java │ │ ├── ClipZoomImageView.java │ │ ├── GridInsetDecoration.java │ │ ├── PickerBottomLayout.java │ │ ├── PicturePreviewPageView.java │ │ ├── PreviewViewPager.java │ │ ├── SquareRelativeLayout.java │ │ └── subsamplingview │ │ ├── ImageSource.java │ │ ├── ImageViewState.java │ │ ├── OnImageEventListener.java │ │ ├── SubsamplingScaleImageView.java │ │ └── decoder │ │ ├── CompatDecoderFactory.java │ │ ├── DecoderFactory.java │ │ ├── ImageDecoder.java │ │ ├── ImageRegionDecoder.java │ │ ├── SkiaImageDecoder.java │ │ └── SkiaImageRegionDecoder.java │ └── res │ ├── anim │ └── slide_in_bottom.xml │ ├── drawable-v21 │ └── bg_toolbar_spinner.xml │ ├── drawable-xxhdpi │ ├── bg_spinner_default.9.png │ ├── ic_album_tick_blue.png │ ├── ic_attachment_camera.png │ ├── ic_general_back.png │ ├── ic_general_cancel_left.png │ ├── ic_maillist_mailbox_arrow.png │ ├── ic_takephoto_again.png │ ├── ic_takephoto_ok.png │ └── ic_takephoto_quit.png │ ├── drawable │ ├── bg_toolbar_spinner.xml │ ├── bs_selectable.xml │ └── ic_arrow_down_white.xml │ ├── layout │ ├── activity_capture_temp.xml │ ├── activity_crop_image.xml │ ├── activity_photo_confirm.xml │ ├── activity_photo_picker.xml │ ├── activity_picker_preview.xml │ ├── album_list_item.xml │ ├── common_toolbar_spinner.xml │ ├── item_album_selected.xml │ ├── item_picker_capture.xml │ ├── picker_bottom_layout.xml │ └── picker_photo_item.xml │ ├── values-v19 │ └── styles.xml │ ├── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── provider_paths.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | ## http://stackoverflow.com/questions/16736856/what-should-be-in-my-gitignore-for-an-android-studio-project 2 | ## https://raw.githubusercontent.com/github/gitignore/master/Android.gitignore 3 | 4 | gradle.properties 5 | 6 | # Built application files 7 | *.apk 8 | *.ap_ 9 | 10 | # Files for the Dalvik VM 11 | *.dex 12 | 13 | # Java class files 14 | *.class 15 | 16 | # Generated files 17 | bin/ 18 | gen/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Windows thumbnail db 36 | Thumbs.db 37 | 38 | # OSX files 39 | .DS_Store 40 | 41 | # Eclipse project files 42 | .classpath 43 | .project 44 | .metadata/ 45 | 46 | # Android Studio 47 | *.iml 48 | .idea 49 | #.idea/workspace.xml - remove # and delete .idea if it better suit your needs. 50 | 51 | # Gradle files 52 | .gradle/ 53 | .gradle 54 | build/ 55 | 56 | #NDK 57 | obj/ 58 | 59 | 60 | # Optional - for older project format, add this section to your gitignore file: 61 | /*/out 62 | /*/*/build 63 | /*/*/production 64 | *.iws 65 | *.ipr 66 | *~ 67 | *.swp 68 | 69 | 70 | # Update: Since Android Studio 1.0, new projects are created with this gitignore file: 71 | /local.properties 72 | /.idea/workspace.xml 73 | /.idea/libraries 74 | /build 75 | 76 | 77 | # Intellij IDEA (see https://intellij-support.jetbrains.com/entries/23393067) 78 | .idea/workspace.xml 79 | .idea/tasks.xml 80 | .idea/datasources.xml 81 | .idea/dataSources.ids 82 | 83 | gradle/ 84 | gradlew 85 | gradlew.bat 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImagePicker 2 | 3 | [![](https://jitpack.io/v/martin90s/ImagePicker.svg)](https://jitpack.io/#martin90s/ImagePicker) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-SImagePicker-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/5129) 4 | 5 | 6 | ## 效果图 7 | ### 第一张头像模式,第二张选择多张图片(包括动画和顺畅的跳转),第三张是分片加载超大图(19.5M,10000*5000px) 8 | 9 | ![image](https://github.com/martin90s/ScreenShot/blob/master/choose_avatar.gif) 10 | ![image](https://github.com/martin90s/ScreenShot/blob/master/choose_image.gif) 11 | ![image](https://github.com/martin90s/ScreenShot/blob/master/huge_iamge.gif) 12 | 13 | 项目介绍请参考 14 | * [Android-如何开发一个功能强大的图片选择器](http://www.jianshu.com/p/81aeb7e2eaa9) 15 | 16 | 致谢 17 | * [subsampling-scale-image-view](https://github.com/davemorrissey/subsampling-scale-image-view) 18 | * y总的页面切换动画 19 | 20 | 21 | # 如何添加 22 | ### Gradle 23 | #### 1.在Project的build.gradle 中添加仓库地址 24 | ``` gradle 25 | // JitPack仓库地址 26 | maven { url "https://jitpack.io" } 27 | ``` 28 | 29 | 示例: 30 | ``` gradle 31 | allprojects { 32 | repositories { 33 | jcenter() 34 | // JitPack仓库地址 35 | maven { url "https://jitpack.io" } 36 | } 37 | } 38 | ``` 39 | #### 2.在app目录下的build.gradle中添加依赖 40 | ```gradle 41 | //SImagePicker 42 | compile 'com.github.martin90s:ImagePicker:v1.3.2' 43 | ``` 44 | 45 | # 如何使用 46 | #### 1.首先初始化(推荐在Application的oncreate中调用) 47 | ```java 48 | SImagePicker.init(new PickerConfig.Builder().setAppContext(this) 49 | .setImageLoader(new FrescoImageLoader()) 50 | .setToolbaseColor(getColor(R.color.colorPrimary)) 51 | .build()); 52 | ``` 53 | #### 2.在需要选择图片的地方调用 54 | ```java 55 | SImagePicker 56 | .from(MainActivity.this) 57 | .maxCount(9) 58 | .rowCount(3) 59 | .pickMode(SImagePicker.MODE_IMAGE) 60 | .fileInterceptor(new SingleFileLimitInterceptor()) 61 | .forResult(REQUEST_CODE_IMAGE); 62 | ``` 63 | 64 | 65 | # 可配置项 66 | #### 1.全局配置(即初始化时传入的PickerConfig,此配置作用于SImagePicker整个使用过程) 67 | | 配置参数 | 参数含义 | 68 | | ------------ | ------------ | 69 | | setImageLoader(ImageLoader) | 使用的图片加载器。demo工程中实现了Fresco和Glide两种ImageLoader,可以参考 | 70 | | setToolbarColor(int) | Picker的主色调,默认值是App的primaryColor | 71 | | setAppContext(Context) | Picker内部用到的Context,传入ApplicationContext即可 | 72 | 73 | #### 2.单次配置(即每次调用SImagePicker时传入的参数,此参数只对这次调用生效) 74 | | 配置参数 | 参数含义 | 75 | | ------------ | ------------ | 76 | | from(Activity or Fragment) | 调用图片选择器可从Activity或者Fragment进入,最后的结果会在onActivityResult()返回,现在返回的结果有两个值,用户选择的图片的路径列表data.getStringArrayListExtra(PhotoPickerActivity.EXTRA_RESULT_SELECTION);用户是否选择了原图data.getBooleanExtra(PhotoPickerActivity.EXTRA_RESULT_ORIGINAL, false); 77 | | 78 | | maxCount(int) | 此次选择允许的最大选择数量,默认是1.比如发朋友圈最多选择9张图就传9 | 79 | | rowCount(int) | 图片列表单排展示多少张图 | 80 | | setSelected(List) | 默认已经被选中的图片 | 81 | | pickMode(int) | 选图的模式,现在有头像模式和普通模式两种,头像模式选中图片后默认会跳到图片裁剪页面且默认只能选择一张 | 82 | | cropFilePath(String) | 头像模式下裁剪图片存放地址 | 83 | | showCamera(boolen) | 是否要展示拍照入口 | 84 | | pickText(int) | Picker里右下角展示的文字信息(比如配置选择,发送,完成) | 85 | | fileInterceptor(FileChooseInterceptor) | 图片过滤器,比如用户选择的单张图片大小有限制,即可写在这个拦截器中,当用户选择过大图片时可以提示并且过滤 | 86 | |forResult(int requestCode) | 打开图片选择器,并且传入requestCode 87 | 88 | ##获取结果 89 | ### 在调用图片选择器的Fragment或者Activity中 90 | ```java 91 | @Override 92 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 93 | super.onActivityResult(requestCode, resultCode, data); 94 | if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_IMAGE) { 95 | final ArrayList pathList = 96 | data.getStringArrayListExtra(PhotoPickerActivity.EXTRA_RESULT_SELECTION); 97 | final boolean original = 98 | data.getBooleanExtra(PhotoPickerActivity.EXTRA_RESULT_ORIGINAL, false); 99 | } 100 | } 101 | ``` 102 | 103 | ##TODO 104 | * 增加一个普通模式。由于现在预览为了支持超大图所以选择了SubsamplingView。后续增加一个不支持超大图的模式,会更加流畅 105 | * Glide下由于glide的缓存策略,跳转动画第一次播放会闪一下,下版修复此问题 106 | 107 | ##联系方式 108 | * 邮箱地址: martinhi2016@gmail.com 109 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | 7 | defaultConfig { 8 | applicationId "com.imnjh.imagepickersample" 9 | minSdkVersion rootProject.ext.minSdk 10 | targetSdkVersion rootProject.ext.targetSdk 11 | versionCode 1 12 | versionName "1.0" 13 | ndk { 14 | abiFilters "armeabi" 15 | } 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | compile project(':imagepicker') 27 | compile 'com.github.bumptech.glide:glide:3.7.0' 28 | compile 'com.facebook.fresco:fresco:0.10.0+' 29 | compile 'com.facebook.fresco:imagepipeline-okhttp3:0.10.0+' 30 | } -------------------------------------------------------------------------------- /app/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/niejunhong/Develop/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/imnjh/imagepicker/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepicker; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.imnjh.imagepicker", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample; 2 | 3 | import java.util.ArrayList; 4 | 5 | import android.app.Activity; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.View; 10 | import android.widget.Button; 11 | import android.widget.CheckBox; 12 | import android.widget.GridView; 13 | import android.widget.TextView; 14 | 15 | import com.imnjh.imagepicker.SImagePicker; 16 | import com.imnjh.imagepicker.activity.PhotoPickerActivity; 17 | import com.imnjh.imagepickersample.adapter.PickAdapter; 18 | import com.imnjh.imagepickersample.cache.CacheManager; 19 | 20 | public class MainActivity extends AppCompatActivity { 21 | 22 | public static final String AVATAR_FILE_NAME = "avatar.png"; 23 | public static final int REQUEST_CODE_AVATAR = 100; 24 | public static final int REQUEST_CODE_IMAGE = 101; 25 | 26 | private Button pickHeadBtn; 27 | private Button pickImageBtn; 28 | private Button pickImageWithLimitBtn; 29 | private GridView gridView; 30 | private PickAdapter pickAdapter; 31 | private TextView originalView; 32 | private CheckBox showCamera; 33 | 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_main); 39 | initUI(); 40 | } 41 | 42 | private void initUI() { 43 | pickAdapter = new PickAdapter(this); 44 | gridView = (GridView) findViewById(R.id.image_grid); 45 | gridView.setAdapter(pickAdapter); 46 | showCamera = (CheckBox) findViewById(R.id.show_camera); 47 | originalView = (TextView) findViewById(R.id.original); 48 | pickHeadBtn = (Button) findViewById(R.id.pick_head_icon); 49 | pickImageBtn = (Button) findViewById(R.id.pick_image); 50 | pickImageWithLimitBtn = (Button) findViewById(R.id.pick_image_with_limit); 51 | pickHeadBtn.setOnClickListener(new View.OnClickListener() { 52 | @Override 53 | public void onClick(View view) { 54 | SImagePicker 55 | .from(MainActivity.this) 56 | .pickMode(SImagePicker.MODE_AVATAR) 57 | .showCamera(showCamera.isChecked()) 58 | .cropFilePath( 59 | CacheManager.getInstance().getImageInnerCache() 60 | .getAbsolutePath(AVATAR_FILE_NAME)) 61 | .forResult(REQUEST_CODE_AVATAR); 62 | } 63 | }); 64 | pickImageBtn.setOnClickListener(new View.OnClickListener() { 65 | @Override 66 | public void onClick(View v) { 67 | SImagePicker 68 | .from(MainActivity.this) 69 | .maxCount(9) 70 | .rowCount(3) 71 | .showCamera(showCamera.isChecked()) 72 | .pickMode(SImagePicker.MODE_IMAGE) 73 | .forResult(REQUEST_CODE_IMAGE); 74 | } 75 | }); 76 | pickImageWithLimitBtn.setOnClickListener(new View.OnClickListener() { 77 | @Override 78 | public void onClick(View v) { 79 | SImagePicker 80 | .from(MainActivity.this) 81 | .maxCount(9) 82 | .rowCount(3) 83 | .pickMode(SImagePicker.MODE_IMAGE) 84 | .fileInterceptor(new SingleFileLimitInterceptor()) 85 | .forResult(REQUEST_CODE_IMAGE); 86 | } 87 | }); 88 | } 89 | 90 | @Override 91 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 92 | super.onActivityResult(requestCode, resultCode, data); 93 | if (resultCode == Activity.RESULT_OK) { 94 | final ArrayList pathList = 95 | data.getStringArrayListExtra(PhotoPickerActivity.EXTRA_RESULT_SELECTION); 96 | final boolean original = 97 | data.getBooleanExtra(PhotoPickerActivity.EXTRA_RESULT_ORIGINAL, false); 98 | pickAdapter.setNewData(pathList); 99 | originalView.setText("原图:" + original); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/ScaleTypeFillCenterInside.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample; 2 | 3 | import android.graphics.Matrix; 4 | import android.graphics.Rect; 5 | 6 | import com.facebook.drawee.drawable.ScalingUtils; 7 | 8 | /** 9 | * Created on 6/6/16. 10 | * 11 | * @author yingyi.xu@rush.im (Yingyi Xu) 12 | */ 13 | public class ScaleTypeFillCenterInside extends ScalingUtils.AbstractScaleType { 14 | 15 | public static final ScalingUtils.ScaleType INSTANCE = new ScaleTypeFillCenterInside(); 16 | 17 | @Override 18 | public void getTransformImpl( 19 | Matrix outTransform, 20 | Rect parentRect, 21 | int childWidth, 22 | int childHeight, 23 | float focusX, 24 | float focusY, 25 | float scaleX, 26 | float scaleY) { 27 | float scale = Math.min(scaleX, scaleY); 28 | float dx = parentRect.left + (parentRect.width() - childWidth * scale) * 0.5f; 29 | float dy = parentRect.top + (parentRect.height() - childHeight * scale) * 0.5f; 30 | outTransform.setScale(scale, scale); 31 | outTransform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/SingleFileLimitInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | 6 | import android.app.Activity; 7 | import android.app.AlertDialog; 8 | import android.content.Context; 9 | import android.content.DialogInterface; 10 | import android.os.Parcel; 11 | 12 | import com.imnjh.imagepicker.FileChooseInterceptor; 13 | import com.imnjh.imagepicker.PickerAction; 14 | 15 | /** 16 | * Created by Martin on 2017/1/18. 17 | */ 18 | 19 | public class SingleFileLimitInterceptor implements FileChooseInterceptor { 20 | 21 | private static final long MAX_FILE_SIZE_ORIGINAL = 200 * 1024; // 200K 22 | 23 | public SingleFileLimitInterceptor() {} 24 | 25 | @Override 26 | public boolean onFileChosen(Context context, ArrayList selectedPic, 27 | boolean original, 28 | int resultCode, PickerAction action) { 29 | if (resultCode != Activity.RESULT_OK) { 30 | return true; 31 | } 32 | if (original) { 33 | ArrayList confirmedFiles = new ArrayList<>(); 34 | for (String filePath : selectedPic) { 35 | File file = new File(filePath); 36 | if (file.exists()) { 37 | if (file.length() <= MAX_FILE_SIZE_ORIGINAL) { 38 | confirmedFiles.add(filePath); 39 | } 40 | } 41 | } 42 | if (confirmedFiles.size() < selectedPic.size()) { 43 | showSingleFileLimitDialog(context, original, resultCode, action, confirmedFiles); 44 | return false; 45 | } 46 | } 47 | return true; 48 | } 49 | 50 | private void showSingleFileLimitDialog(Context context, final boolean original, 51 | final int resultCode, 52 | final PickerAction action, final ArrayList confirmedFiles) { 53 | new AlertDialog.Builder(context) 54 | .setMessage(R.string.general_max_per_image) 55 | .setPositiveButton( 56 | R.string.general_ok, new DialogInterface.OnClickListener() { 57 | @Override 58 | public void onClick(DialogInterface dialog, int which) { 59 | action.proceedResultAndFinish(confirmedFiles, original, resultCode); 60 | } 61 | }) 62 | .setNegativeButton(R.string.general_cancel, new DialogInterface.OnClickListener() { 63 | @Override 64 | public void onClick(DialogInterface dialog, int which) {} 65 | }) 66 | .show(); 67 | } 68 | 69 | @Override 70 | public int describeContents() { 71 | return 0; 72 | } 73 | 74 | @Override 75 | public void writeToParcel(Parcel dest, int flags) { 76 | 77 | } 78 | 79 | protected SingleFileLimitInterceptor(Parcel in) {} 80 | 81 | public static final Creator CREATOR = 82 | new Creator() { 83 | @Override 84 | public SingleFileLimitInterceptor createFromParcel(Parcel source) { 85 | return new SingleFileLimitInterceptor(source); 86 | } 87 | 88 | @Override 89 | public SingleFileLimitInterceptor[] newArray(int size) { 90 | return new SingleFileLimitInterceptor[size]; 91 | } 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/adapter/PickAdapter.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample.adapter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import android.content.Context; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.BaseAdapter; 11 | import android.widget.ImageView; 12 | 13 | import com.bumptech.glide.Glide; 14 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 15 | import com.imnjh.imagepickersample.R; 16 | import com.imnjh.imagepickersample.app.PickerApplication; 17 | 18 | /** 19 | * Created by Martin on 2017/1/18. 20 | */ 21 | 22 | public class PickAdapter extends BaseAdapter { 23 | 24 | private LayoutInflater layoutInflater; 25 | private List data = new ArrayList<>(); 26 | 27 | public PickAdapter(Context context) { 28 | layoutInflater = LayoutInflater.from(context); 29 | } 30 | 31 | public void setNewData(List data) { 32 | this.data.clear(); 33 | if (data != null) { 34 | this.data.addAll(data); 35 | } 36 | notifyDataSetChanged(); 37 | } 38 | 39 | 40 | @Override 41 | public int getCount() { 42 | return data.size(); 43 | } 44 | 45 | @Override 46 | public Object getItem(int position) { 47 | return data.get(position); 48 | } 49 | 50 | @Override 51 | public long getItemId(int position) { 52 | return position; 53 | } 54 | 55 | @Override 56 | public View getView(int position, View convertView, ViewGroup parent) { 57 | ViewHolder holder; 58 | if (convertView == null) { 59 | convertView = layoutInflater.inflate(R.layout.image_item, null, false); 60 | holder = new ViewHolder(); 61 | holder.imageView = (ImageView) convertView.findViewById(R.id.image); 62 | convertView.setTag(holder); 63 | } else { 64 | holder = (ViewHolder) convertView.getTag(); 65 | } 66 | holder.imageView.setImageResource(R.drawable.ic_album_tick_blue); 67 | Glide.with(PickerApplication.getAppContext()).load(data.get(position)).skipMemoryCache(true) 68 | .diskCacheStrategy(DiskCacheStrategy.NONE) 69 | .dontAnimate().into(holder.imageView); 70 | return convertView; 71 | } 72 | 73 | 74 | class ViewHolder { 75 | public ImageView imageView; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/app/PickerApplication.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample.app; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.imnjh.imagepicker.PickerConfig; 7 | import com.imnjh.imagepicker.SImagePicker; 8 | import com.imnjh.imagepickersample.R; 9 | import com.imnjh.imagepickersample.imageloader.FrescoImageLoader; 10 | 11 | /** 12 | * Created by Martin on 2017/1/17. 13 | */ 14 | 15 | public class PickerApplication extends Application { 16 | 17 | private static Application instance; 18 | 19 | @Override 20 | public void onCreate() { 21 | super.onCreate(); 22 | instance = this; 23 | SImagePicker.init(new PickerConfig.Builder().setAppContext(this) 24 | .setImageLoader(new FrescoImageLoader()) 25 | .setToolbaseColor(getResources().getColor(R.color.colorPrimary)).build()); 26 | } 27 | 28 | 29 | public static Context getAppContext() { 30 | return instance; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/cache/Cache.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample.cache; 2 | 3 | import java.io.File; 4 | 5 | import com.imnjh.imagepicker.util.FileUtil; 6 | 7 | /** 8 | * Created by Martin on 2017/1/17. 9 | */ 10 | public abstract class Cache { 11 | 12 | public abstract boolean exist(String fileName); 13 | 14 | public abstract String getAbsolutePath(String fileName); 15 | 16 | public abstract File getDirectory(); 17 | 18 | public abstract boolean deleteCacheItem(String fileName); 19 | 20 | public void clear() { 21 | FileUtil.deleteDirectory(getDirectory()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/cache/CacheManager.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample.cache; 2 | 3 | import com.imnjh.imagepickersample.app.PickerApplication; 4 | import com.imnjh.imagepicker.util.SystemUtil; 5 | 6 | import java.io.File; 7 | /** 8 | * Created by Martin on 2017/1/17. 9 | */ 10 | public class CacheManager { 11 | 12 | public static final String ROOT_STORE = "PickerSample"; 13 | 14 | private InnerCache imageInnerCache; 15 | 16 | private LocalCache imageCache; 17 | 18 | private static CacheManager instance; 19 | 20 | private CacheManager() { 21 | init(); 22 | } 23 | 24 | public static synchronized CacheManager getInstance() { 25 | if (instance == null) { 26 | instance = new CacheManager(); 27 | } 28 | return instance; 29 | } 30 | 31 | 32 | private void init() { 33 | initRootCache(SystemUtil.getStoreDir(PickerApplication.getAppContext())); 34 | } 35 | 36 | private boolean initRootCache(File storage) { 37 | File cacheRoot = new File(storage, ROOT_STORE); 38 | boolean isMkRoot = true; 39 | if (!cacheRoot.exists()) { 40 | isMkRoot = cacheRoot.mkdirs(); 41 | } else if (!cacheRoot.isDirectory()) { 42 | isMkRoot = false; 43 | } 44 | if (!isMkRoot) { 45 | return false; 46 | } 47 | return true; 48 | } 49 | 50 | public InnerCache getImageInnerCache() { 51 | if (imageInnerCache == null) { 52 | imageInnerCache = new InnerCache(); 53 | 54 | } 55 | return imageInnerCache; 56 | } 57 | 58 | public LocalCache getImageCache() { 59 | if (imageCache == null) { 60 | imageCache = new LocalCache("Image"); 61 | } 62 | return imageCache; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/cache/InnerCache.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample.cache; 2 | 3 | import java.io.File; 4 | 5 | import android.content.Context; 6 | import android.os.Environment; 7 | 8 | import com.imnjh.imagepickersample.app.PickerApplication; 9 | import com.imnjh.imagepicker.util.FileUtil; 10 | 11 | /** 12 | * Created by Martin on 2017/1/17. 13 | */ 14 | public class InnerCache extends Cache { 15 | 16 | private File innerCache; 17 | 18 | public InnerCache() { 19 | innerCache = getCacheDirCreateIfNotExist(); 20 | } 21 | 22 | private File getCacheDirCreateIfNotExist() { 23 | File cachePath = new File(getInnerCacheDir(PickerApplication.getAppContext())); 24 | if (!cachePath.isDirectory()) { 25 | try { 26 | cachePath.mkdirs(); 27 | } catch (Exception e) { 28 | e.printStackTrace(); 29 | } 30 | try { 31 | new File(cachePath, ".nomedia").createNewFile(); 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | return cachePath; 37 | } 38 | 39 | private String getInnerCacheDir(Context context) { 40 | String cachePath; 41 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 42 | || !Environment.isExternalStorageRemovable()) { 43 | cachePath = context.getExternalCacheDir().getPath(); 44 | } else { 45 | cachePath = context.getCacheDir().getPath(); 46 | } 47 | return cachePath; 48 | } 49 | 50 | 51 | public boolean exist(String fileName) { 52 | String path = innerCache + File.separator + fileName; 53 | return FileUtil.exist(path); 54 | } 55 | 56 | @Override 57 | public String getAbsolutePath(String fileName) { 58 | return getDirectory().getAbsolutePath() + File.separator + fileName; 59 | } 60 | 61 | @Override 62 | public File getDirectory() { 63 | return getCacheDirCreateIfNotExist(); 64 | } 65 | 66 | @Override 67 | public boolean deleteCacheItem(String fileName) { 68 | String filePath = getAbsolutePath(fileName); 69 | return FileUtil.deleteFile(filePath); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/cache/LocalCache.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample.cache; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import com.imnjh.imagepicker.util.FileUtil; 7 | import com.imnjh.imagepicker.util.LogUtils; 8 | import com.imnjh.imagepicker.util.SystemUtil; 9 | import com.imnjh.imagepickersample.app.PickerApplication; 10 | 11 | /** 12 | * Created by Martin on 2017/1/17. 13 | */ 14 | public final class LocalCache extends Cache { 15 | 16 | private final String dataName; 17 | 18 | private final String dataPath; 19 | 20 | /** 21 | * @param dataName cache directory name 22 | */ 23 | public LocalCache(String dataName) { 24 | this.dataName = dataName; 25 | StringBuilder pathBuilder = new StringBuilder(getRushCachePath()); 26 | pathBuilder.append(File.separatorChar); 27 | pathBuilder.append(getDataName()); 28 | pathBuilder.append(File.separatorChar); 29 | dataPath = pathBuilder.toString(); 30 | initCacheRoot(dataPath); 31 | } 32 | 33 | 34 | 35 | private String getRushCachePath() { 36 | File dataDir = SystemUtil.getStoreDir(PickerApplication.getAppContext()); 37 | return getDirectoryCreateIfNotExist( 38 | dataDir.getPath() + File.separator + CacheManager.ROOT_STORE) 39 | .getPath(); 40 | } 41 | 42 | private File getDirectoryCreateIfNotExist(String pathStr) { 43 | File file = new File(pathStr); 44 | if (!file.isDirectory()) { 45 | file.mkdirs(); 46 | } 47 | return file; 48 | } 49 | 50 | @Override 51 | public File getDirectory() { 52 | return getDirectoryCreateIfNotExist(dataPath); 53 | } 54 | 55 | 56 | @Override 57 | public String getAbsolutePath(String fileName) { 58 | return getDirectory() + File.separator + fileName; 59 | } 60 | 61 | @Override 62 | public boolean deleteCacheItem(String fileName) { 63 | String filePath = getAbsolutePath(fileName); 64 | return FileUtil.deleteFile(filePath); 65 | } 66 | 67 | @Override 68 | public boolean exist(String relativePath) { 69 | String filePath = getAbsolutePath(relativePath); 70 | boolean ret = FileUtil.exist(filePath); 71 | return ret; 72 | } 73 | 74 | public String getDataName() { 75 | return dataName; 76 | } 77 | 78 | 79 | private void initCacheRoot(String root) { 80 | getDirectoryCreateIfNotExist(root); 81 | File ignoreFile = new File(root, ".nomedia"); 82 | try { 83 | ignoreFile.createNewFile(); 84 | } catch (IOException e) { 85 | LogUtils.e(LocalCache.class.getSimpleName(), "Failed to create ignore file.", e); 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/imageloader/FrescoImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample.imageloader; 2 | 3 | import okhttp3.OkHttpClient; 4 | 5 | import android.content.Context; 6 | import android.net.Uri; 7 | import android.widget.ImageView; 8 | 9 | import com.facebook.cache.disk.DiskCacheConfig; 10 | import com.facebook.drawee.backends.pipeline.Fresco; 11 | import com.facebook.drawee.interfaces.DraweeController; 12 | import com.facebook.drawee.view.DraweeView; 13 | import com.facebook.drawee.view.SimpleDraweeView; 14 | import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory; 15 | import com.facebook.imagepipeline.common.ResizeOptions; 16 | import com.facebook.imagepipeline.core.ImagePipelineConfig; 17 | import com.facebook.imagepipeline.request.ImageRequest; 18 | import com.facebook.imagepipeline.request.ImageRequestBuilder; 19 | import com.imnjh.imagepicker.ImageLoader; 20 | import com.imnjh.imagepickersample.app.PickerApplication; 21 | import com.imnjh.imagepickersample.ScaleTypeFillCenterInside; 22 | import com.imnjh.imagepickersample.cache.CacheManager; 23 | 24 | /** 25 | * Created by Martin on 2017/1/17. 26 | */ 27 | 28 | public class FrescoImageLoader implements ImageLoader { 29 | 30 | public FrescoImageLoader() { 31 | init(); 32 | } 33 | 34 | private void init() { 35 | DiskCacheConfig diskCacheConfig = 36 | DiskCacheConfig 37 | .newBuilder(PickerApplication.getAppContext()) 38 | .setBaseDirectoryPath( 39 | CacheManager.getInstance().getImageCache() 40 | .getDirectory()) 41 | .build(); 42 | ImagePipelineConfig config = 43 | OkHttpImagePipelineConfigFactory 44 | .newBuilder( 45 | PickerApplication.getAppContext(), 46 | new OkHttpClient.Builder().build()) 47 | .setDownsampleEnabled(true).setMainDiskCacheConfig(diskCacheConfig) 48 | .build(); 49 | Fresco.initialize(PickerApplication.getAppContext(), config); 50 | } 51 | 52 | @Override 53 | public void bindImage(ImageView photoImageView, Uri uri, int width, int height) { 54 | DraweeView draweeView = (DraweeView) photoImageView; 55 | final ImageRequestBuilder requestBuilder = ImageRequestBuilder.newBuilderWithSource(uri); 56 | if (width > 0 && height > 0) { 57 | requestBuilder.setResizeOptions(new ResizeOptions(width, height)); 58 | } 59 | ImageRequest imageRequest = requestBuilder.build(); 60 | DraweeController controller = Fresco.newDraweeControllerBuilder() 61 | .setOldController(draweeView.getController()) 62 | .setImageRequest(imageRequest).build(); 63 | draweeView.setController(controller); 64 | } 65 | 66 | @Override 67 | public void bindImage(ImageView imageView, Uri uri) { 68 | bindImage(imageView, uri, 0, 0); 69 | } 70 | 71 | @Override 72 | public ImageView createImageView(Context context) { 73 | SimpleDraweeView simpleDraweeView = new SimpleDraweeView(context); 74 | return simpleDraweeView; 75 | } 76 | 77 | @Override 78 | public ImageView createFakeImageView(Context context) { 79 | SimpleDraweeView fakeImage = new SimpleDraweeView(context); 80 | fakeImage.getHierarchy().setActualImageScaleType(ScaleTypeFillCenterInside.INSTANCE); 81 | return fakeImage; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/imageloader/GlideImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample.imageloader; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.widget.ImageView; 6 | 7 | import com.bumptech.glide.Glide; 8 | import com.imnjh.imagepicker.ImageLoader; 9 | import com.imnjh.imagepickersample.R; 10 | import com.imnjh.imagepickersample.app.PickerApplication; 11 | 12 | /** 13 | * Created by Martin on 2017/1/18. 14 | */ 15 | 16 | public class GlideImageLoader implements ImageLoader { 17 | @Override 18 | public void bindImage(ImageView imageView, Uri uri, int width, int height) { 19 | Glide.with(PickerApplication.getAppContext()).load(uri).placeholder(R.mipmap.ic_launcher) 20 | .error(R.mipmap.ic_launcher).override(width, height).dontAnimate().into(imageView); 21 | } 22 | 23 | @Override 24 | public void bindImage(ImageView imageView, Uri uri) { 25 | Glide.with(PickerApplication.getAppContext()).load(uri).placeholder(R.mipmap.ic_launcher) 26 | .error(R.mipmap.ic_launcher).dontAnimate().into(imageView); 27 | } 28 | 29 | @Override 30 | public ImageView createImageView(Context context) { 31 | ImageView imageView = new ImageView(context); 32 | imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 33 | return imageView; 34 | } 35 | 36 | @Override 37 | public ImageView createFakeImageView(Context context) { 38 | return new ImageView(context); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/imnjh/imagepickersample/widget/ExpandGridView.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepickersample.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.ViewGroup; 6 | import android.widget.GridView; 7 | 8 | public class ExpandGridView extends GridView { 9 | public ExpandGridView(Context context) { 10 | super(context); 11 | } 12 | 13 | public ExpandGridView(Context context, AttributeSet attrs) { 14 | super(context, attrs); 15 | } 16 | 17 | public ExpandGridView(Context context, AttributeSet attrs, int defStyleAttr) { 18 | super(context, attrs, defStyleAttr); 19 | } 20 | 21 | 22 | @Override 23 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 24 | int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, 25 | MeasureSpec.AT_MOST); 26 | super.onMeasure(widthMeasureSpec, expandSpec); 27 | ViewGroup.LayoutParams params = getLayoutParams(); 28 | params.height = getMeasuredHeight(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | 20 | 21 | 26 | 27 |