├── application ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ └── strings.xml │ │ └── layout │ │ │ └── layout_example_activity.xml │ │ ├── java │ │ └── com │ │ │ └── iknow │ │ │ └── imageselect │ │ │ └── application │ │ │ └── ExampleActivity.java │ │ └── AndroidManifest.xml ├── build.gradle └── proguard-rules.pro ├── business ├── .gitignore ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── rawe │ │ └── gordon │ │ └── com │ │ └── business │ │ └── ZApplication.java ├── build.gradle └── proguard-rules.pro ├── image-selector ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ ├── drawable-hdpi │ │ │ ├── icon_back.png │ │ │ ├── video_icon.png │ │ │ ├── ic_launcher.png │ │ │ ├── img_pic_selected.png │ │ │ └── img_pic_unselected.png │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── dimens.xml │ │ │ ├── colors.xml │ │ │ ├── styles.xml │ │ │ └── attrs.xml │ │ ├── drawable │ │ │ ├── ic_arrow_back.xml │ │ │ ├── ic_play_circle.xml │ │ │ ├── ic_choosable.xml │ │ │ ├── ic_radio_button_unchecked.xml │ │ │ ├── ic_chosen.xml │ │ │ └── ic_radio_button_checked.xml │ │ └── layout │ │ │ ├── layout_photo_view_item.xml │ │ │ ├── image_select_item.xml │ │ │ ├── ablum_list_view_item.xml │ │ │ ├── abs_image_select_layout.xml │ │ │ └── layout_browse_detail_activity.xml │ │ └── java │ │ └── com │ │ └── iknow │ │ └── imageselect │ │ ├── view │ │ └── IImageChooseView.java │ │ ├── core │ │ └── CoreActivity.java │ │ ├── model │ │ ├── AlbumInfo.java │ │ └── MediaInfo.java │ │ ├── utils │ │ ├── DrawableUtil.java │ │ ├── DeviceInforHelper.java │ │ ├── CacheBean.java │ │ ├── PhotoDraweeViewUtil.java │ │ ├── ImageFilePathUtil.java │ │ └── MediaFileUtil.java │ │ ├── presenter │ │ ├── IImageChoosePresenter.java │ │ └── ImageChoosePresenterCompl.java │ │ ├── photodraweeview │ │ ├── OnScaleChangeListener.java │ │ ├── OnViewTapListener.java │ │ ├── OnPhotoTapListener.java │ │ ├── OnScaleDragGestureListener.java │ │ ├── IAttacher.java │ │ ├── DefaultOnDoubleTapListener.java │ │ ├── PhotoDraweeView.java │ │ ├── ScaleDragDetector.java │ │ └── Attacher.java │ │ ├── ImageSelectContextHolder.java │ │ ├── widget │ │ ├── SpacesItemDecoration.java │ │ ├── PicItemCheckedView.java │ │ └── TitleView.java │ │ ├── adapter │ │ └── AlbumListAdapter.java │ │ ├── configs │ │ └── ImagePipelineConfigFactory.java │ │ └── activities │ │ ├── MultiSelectImageActivity.java │ │ ├── SingleSelectImageActivity.java │ │ ├── AbsImageSelectActivity.java │ │ └── BrowseDetailActivity.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── README.md ├── shared.gradle ├── gradle.properties ├── gradlew.bat ├── gradlew └── LICENSE /application/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /business/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /image-selector/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':image-selector', ':application', ':business' 2 | -------------------------------------------------------------------------------- /image-selector/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /business/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Business 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iKnown/AndroidImagePicker/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .idea 10 | build 11 | -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable-hdpi/icon_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iKnown/AndroidImagePicker/HEAD/image-selector/src/main/res/drawable-hdpi/icon_back.png -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable-hdpi/video_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iKnown/AndroidImagePicker/HEAD/image-selector/src/main/res/drawable-hdpi/video_icon.png -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iKnown/AndroidImagePicker/HEAD/image-selector/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable-hdpi/img_pic_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iKnown/AndroidImagePicker/HEAD/image-selector/src/main/res/drawable-hdpi/img_pic_selected.png -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable-hdpi/img_pic_unselected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iKnown/AndroidImagePicker/HEAD/image-selector/src/main/res/drawable-hdpi/img_pic_unselected.png -------------------------------------------------------------------------------- /image-selector/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 确认 3 | 选择 4 | 原图 5 | 6 | -------------------------------------------------------------------------------- /image-selector/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52dp 4 | 14dp 5 | -------------------------------------------------------------------------------- /application/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Pop Chooser 3 | please click to choose a picture... 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ### 本项目是一个类似Android微信图片选择的项目 3 | ### 使用到的相关库和代码结构 4 | * MVP设计模式 5 | * fresco图片加载库 6 | * 图片和视频的查询操作 7 | 8 | ImagePicker 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 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.10-all.zip 7 | -------------------------------------------------------------------------------- /image-selector/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2B3131 4 | #2B3131 5 | #FF4081 6 | #2B3131 7 | 8 | -------------------------------------------------------------------------------- /business/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/view/IImageChooseView.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.view; 2 | 3 | /** 4 | * Author: J.Chou 5 | * Email: who_know_me@163.com 6 | * Created: 2016年04月06日 6:40 PM 7 | * Description: 8 | */ 9 | 10 | public interface IImageChooseView { 11 | void reloadData(); 12 | } 13 | -------------------------------------------------------------------------------- /application/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply from: rootDir.path + '/shared.gradle' 3 | 4 | android { 5 | defaultConfig { 6 | applicationId 'org.iknown' 7 | } 8 | } 9 | 10 | dependencies { 11 | compile fileTree(dir: 'libs', include: ['*.jar']) 12 | compile project(':business') 13 | } 14 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/core/CoreActivity.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.core; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | 5 | /** 6 | * Author: Jason.Chou 7 | * Email: who_know_me@163.com 8 | * Created: 2016年04月26日 5:08 PM 9 | * Description: 10 | */ 11 | public class CoreActivity extends AppCompatActivity { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/model/AlbumInfo.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.model; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Author: J.Chou 7 | * Email: who_know_me@163.com 8 | * Created: 2016年04月06日 4:27 PM 9 | * Description:相册专辑类 10 | */ 11 | 12 | public class AlbumInfo extends MediaInfo { 13 | public ArrayList medias; 14 | } 15 | -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable/ic_play_circle.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable/ic_choosable.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable/ic_radio_button_unchecked.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/utils/DrawableUtil.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.utils; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.support.v7.widget.AppCompatDrawableManager; 6 | 7 | /** 8 | * Created by gordon on 5/9/16. 9 | */ 10 | public class DrawableUtil { 11 | public static Drawable decodeFromVector(Context context, int resId) { 12 | return AppCompatDrawableManager.get().getDrawable(context, resId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable/ic_chosen.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/presenter/IImageChoosePresenter.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.presenter; 2 | 3 | import com.iknow.imageselect.model.MediaInfo; 4 | import java.util.ArrayList; 5 | 6 | /** 7 | * Author: J.Chou 8 | * Email: who_know_me@163.com 9 | * Created: 2016年04月06日 6:37 PM 10 | * Description: 11 | */ 12 | 13 | public interface IImageChoosePresenter { 14 | void doCancel(); 15 | String takePhotos(); 16 | void doSendAction(); 17 | void doPreview(ArrayList hasCheckedImages); 18 | } 19 | -------------------------------------------------------------------------------- /image-selector/src/main/res/drawable/ic_radio_button_checked.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /shared.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | compileSdkVersion 23 3 | buildToolsVersion "23.0.2" 4 | 5 | android { 6 | defaultConfig { 7 | minSdkVersion 14 8 | targetSdkVersion 23 9 | versionCode 1 10 | versionName "1.0" 11 | } 12 | 13 | lintOptions { 14 | abortOnError false 15 | checkReleaseBuilds = false 16 | } 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/photodraweeview/OnScaleChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.photodraweeview; 2 | 3 | /** 4 | * Interface definition for callback to be invoked when attached ImageView scale changes 5 | * 6 | * @author Marek Sebera 7 | */ 8 | public interface OnScaleChangeListener { 9 | /** 10 | * Callback for when the scale changes 11 | * 12 | * @param scaleFactor the scale factor 13 | * @param focusX focal point X position 14 | * @param focusY focal point Y position 15 | */ 16 | void onScaleChange(float scaleFactor, float focusX, float focusY); 17 | } 18 | -------------------------------------------------------------------------------- /business/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(dir: 'libs', include: ['*.jar']) 23 | compile project(':image-selector') 24 | compile 'com.android.support:appcompat-v7:23.3.0' 25 | } 26 | -------------------------------------------------------------------------------- /business/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/gordon/Space/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /application/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/gordon/Space/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /image-selector/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/choujason/AndroidStudioSDK/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 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/photodraweeview/OnViewTapListener.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.photodraweeview; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * Interface definition for a callback to be invoked when the ImageView is tapped with a single 7 | * tap. 8 | * 9 | * @author Chris Banes 10 | */ 11 | public interface OnViewTapListener { 12 | /** 13 | * A callback to receive where the user taps on a ImageView. You will receive a callback if 14 | * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored. 15 | * 16 | * @param view - View the user tapped. 17 | * @param x - where the user tapped from the left of the View. 18 | * @param y - where the user tapped from the top of the View. 19 | */ 20 | void onViewTap(View view, float x, float y); 21 | } -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/ImageSelectContextHolder.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Created by gordon on 8/1/16. 7 | */ 8 | public class ImageSelectContextHolder { 9 | private Context context; 10 | 11 | private ImageSelectContextHolder() { 12 | } 13 | 14 | private static class Holder { 15 | private static ImageSelectContextHolder instance = new ImageSelectContextHolder(); 16 | } 17 | 18 | public static ImageSelectContextHolder getInstance() { 19 | return Holder.instance; 20 | } 21 | 22 | public void init(Context context) { 23 | this.context = context; 24 | } 25 | 26 | public Context getContext() { 27 | return context; 28 | } 29 | 30 | public void setContext(Context context) { 31 | this.context = context; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /business/src/main/java/rawe/gordon/com/business/ZApplication.java: -------------------------------------------------------------------------------- 1 | package rawe.gordon.com.business; 2 | 3 | import android.app.Application; 4 | import com.facebook.drawee.backends.pipeline.Fresco; 5 | import com.iknow.imageselect.ImageSelectContextHolder; 6 | import com.iknow.imageselect.configs.ImagePipelineConfigFactory; 7 | 8 | /** 9 | * Author: Jason.Chou 10 | * Email: who_know_me@163.com 11 | * Created: 2016年04月26日 4:54 PM 12 | * Description: 13 | */ 14 | public class ZApplication extends Application{ 15 | 16 | private static ZApplication mApplication; 17 | public static ZApplication getApplication() { 18 | return mApplication; 19 | } 20 | 21 | @Override public void onCreate() { 22 | super.onCreate(); 23 | mApplication = this; 24 | Fresco.initialize(this, ImagePipelineConfigFactory.getImagePipelineConfig(this)); 25 | ImageSelectContextHolder.getInstance().init(getApplicationContext()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /image-selector/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: rootDir.path + '/shared.gradle' 3 | apply plugin: 'com.novoda.bintray-release' 4 | 5 | 6 | publish { 7 | artifactId = 'image-selector'//模块名称 8 | userOrg = rootProject.userOrg 9 | groupId = rootProject.groupId 10 | uploadName = rootProject.uploadName //模块上传后所在的文件夹名称 11 | publishVersion = rootProject.publishVersion//模块版本号 12 | desc = rootProject.description//模块的描述 13 | website = rootProject.website //模块的网站 14 | licences = rootProject.licences //模块的licences 15 | } 16 | 17 | dependencies { 18 | compile fileTree(dir: 'libs', include: ['*.jar']) 19 | compile 'com.android.support:appcompat-v7:23.3.0' 20 | compile 'com.android.support:recyclerview-v7:23.3.0' 21 | compile 'com.android.support:cardview-v7:23.3.0' 22 | compile 'com.facebook.fresco:fresco:0.10.0' 23 | compile 'com.github.chrisbanes:PhotoView:1.2.6' 24 | } 25 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/photodraweeview/OnPhotoTapListener.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.photodraweeview; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * Interface definition for a callback to be invoked when the Photo is tapped with a single 7 | * tap. 8 | * 9 | * @author Chris Banes 10 | */ 11 | public interface OnPhotoTapListener { 12 | 13 | /** 14 | * A callback to receive where the user taps on a photo. You will only receive a callback if 15 | * the user taps on the actual photo, tapping on 'whitespace' will be ignored. 16 | * 17 | * @param view - View the user tapped. 18 | * @param x - where the user tapped from the of the Drawable, as percentage of the 19 | * Drawable width. 20 | * @param y - where the user tapped from the top of the Drawable, as percentage of the 21 | * Drawable height. 22 | */ 23 | void onPhotoTap(View view, float x, float y); 24 | } 25 | -------------------------------------------------------------------------------- /application/src/main/res/layout/layout_example_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 21 | 22 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/widget/SpacesItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.widget; 2 | 3 | import android.graphics.Rect; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | 7 | /** 8 | * Author: Jason.Chou 9 | * Email: who_know_me@163.com 10 | * Created: 2016年05月04日 11:27 AM 11 | * Description: 12 | */ 13 | public class SpacesItemDecoration extends RecyclerView.ItemDecoration{ 14 | 15 | private int halfSpace; 16 | 17 | public SpacesItemDecoration(int space) { 18 | this.halfSpace = space / 2; 19 | } 20 | 21 | @Override 22 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 23 | 24 | if (parent.getPaddingLeft() != halfSpace) { 25 | parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace); 26 | parent.setClipToPadding(false); 27 | } 28 | 29 | outRect.top = halfSpace; 30 | outRect.bottom = halfSpace; 31 | outRect.left = halfSpace; 32 | outRect.right = halfSpace; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /application/src/main/java/com/iknow/imageselect/application/ExampleActivity.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.application; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | 9 | import com.iknow.imageselect.activities.MultiSelectImageActivity; 10 | 11 | /** 12 | * Created by gordon on 5/10/16. 13 | */ 14 | public class ExampleActivity extends AppCompatActivity implements View.OnClickListener { 15 | 16 | @Override 17 | protected void onCreate(@Nullable Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.layout_example_activity); 20 | findViewById(R.id.card).setOnClickListener(this); 21 | } 22 | 23 | @Override 24 | public void onClick(View view) { 25 | if (view.getId() == R.id.card) { 26 | Intent intent = new Intent(this, MultiSelectImageActivity.class); 27 | startActivity(intent); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/model/MediaInfo.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Author: J.Chou 7 | * Email: who_know_me@163.com 8 | * Created: 2016年04月06日 3:17 PM 9 | * Description: 10 | */ 11 | 12 | public class MediaInfo implements Serializable,Cloneable { 13 | 14 | public long fileId; 15 | /** 图片显示的名称 */ 16 | public String name = ""; 17 | /** 作者名称 */ 18 | public String author = ""; 19 | /** 图片描述信息 */ 20 | public String description = ""; 21 | 22 | public String filePath;//图片所在文件夹的路径 23 | 24 | public String createTime; 25 | 26 | public String thumbPath;//缩略图路径 27 | 28 | public String fileName;//图片全路径,包含图片文件名的路径信息 29 | 30 | public int rotate; 31 | 32 | public String lat; 33 | 34 | public String lon; 35 | 36 | public int mediaType; 37 | 38 | public static MediaInfo obtain(String path){ 39 | MediaInfo imageInfo = new MediaInfo(); 40 | imageInfo.fileName = path; 41 | return imageInfo; 42 | } 43 | 44 | public static MediaInfo buildOneImage(String path){ 45 | 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /image-selector/src/main/res/layout/layout_photo_view_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 18 | 19 | 29 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/utils/DeviceInforHelper.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.utils; 2 | 3 | import android.util.DisplayMetrics; 4 | import android.util.TypedValue; 5 | 6 | import com.iknow.imageselect.ImageSelectContextHolder; 7 | 8 | /** 9 | * Author: Jason.Chou 10 | * Email: who_know_me@163.com 11 | * Created: 2016年04月26日 4:13 PM 12 | * Description: 13 | */ 14 | public class DeviceInforHelper { 15 | 16 | public static int getPixelFromDip(float f) { 17 | return getPixelFromDip(ImageSelectContextHolder.getInstance().getContext().getResources().getDisplayMetrics(), f); 18 | } 19 | 20 | public static int getPixelFromDip(DisplayMetrics dm, float dip) { 21 | return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, dm) + 0.5f); 22 | } 23 | 24 | public static int getScreenWidth() { 25 | return ImageSelectContextHolder.getInstance().getContext().getResources().getDisplayMetrics().widthPixels; 26 | } 27 | 28 | public static int getScreenHeight() { 29 | return ImageSelectContextHolder.getInstance().getContext().getResources().getDisplayMetrics().heightPixels; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /image-selector/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/photodraweeview/OnScaleDragGestureListener.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.photodraweeview; 2 | 3 | /** 4 | * **************************************************************************** 5 | * Copyright 2011, 2012 Chris Banes. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ***************************************************************************** 19 | */ 20 | 21 | public interface OnScaleDragGestureListener { 22 | void onDrag(float dx, float dy); 23 | 24 | void onFling(float startX, float startY, float velocityX, float velocityY); 25 | 26 | void onScale(float scaleFactor, float focusX, float focusY); 27 | 28 | void onScaleEnd(); 29 | } 30 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/utils/CacheBean.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class CacheBean { 7 | 8 | private static Map cacheBeanMap = new HashMap(); 9 | 10 | private static Map> paramMap = new HashMap>(); 11 | 12 | public static void putParam(String token, String key, Object value) { 13 | Map params = paramMap.get(token); 14 | if (params == null) { 15 | params = new HashMap(); 16 | } 17 | params.put(key, value); 18 | paramMap.put(token, params); 19 | } 20 | 21 | public static Object getParam(String token, String key) { 22 | Map params = paramMap.get(token); 23 | if (params == null) { 24 | return null; 25 | } 26 | return params.get(key); 27 | } 28 | 29 | public static void clean(String token) { 30 | cacheBeanMap.remove(token); 31 | paramMap.remove(token); 32 | } 33 | 34 | public static CacheBean get(String token) { 35 | CacheBean commonCacheBean = cacheBeanMap.get(token); 36 | if (commonCacheBean == null) { 37 | commonCacheBean = new CacheBean(); 38 | cacheBeanMap.put(token, commonCacheBean); 39 | } 40 | return commonCacheBean; 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/utils/PhotoDraweeViewUtil.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.utils; 2 | 3 | import android.graphics.drawable.Animatable; 4 | import android.net.Uri; 5 | 6 | import com.facebook.drawee.backends.pipeline.Fresco; 7 | import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder; 8 | import com.facebook.drawee.controller.BaseControllerListener; 9 | import com.facebook.imagepipeline.image.ImageInfo; 10 | 11 | import com.iknow.imageselect.photodraweeview.PhotoDraweeView; 12 | 13 | /** 14 | * Created by gordon on 5/9/16. 15 | */ 16 | public class PhotoDraweeViewUtil { 17 | public static void display(final PhotoDraweeView draweeView, Uri uri) { 18 | PipelineDraweeControllerBuilder controller = Fresco.newDraweeControllerBuilder(); 19 | controller.setUri(uri); 20 | controller.setOldController(draweeView.getController()); 21 | controller.setControllerListener(new BaseControllerListener() { 22 | @Override 23 | public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) { 24 | super.onFinalImageSet(id, imageInfo, animatable); 25 | if (imageInfo == null || draweeView == null) { 26 | return; 27 | } 28 | draweeView.update(imageInfo.getWidth(), imageInfo.getHeight()); 29 | } 30 | }); 31 | draweeView.setController(controller.build()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /image-selector/src/main/res/layout/image_select_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 21 | 29 | 37 | 38 | 39 | 48 | -------------------------------------------------------------------------------- /application/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 21 | 22 | 27 | 28 | 34 | 35 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /image-selector/src/main/res/layout/ablum_list_view_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 35 | 36 | 43 | 44 | 45 | 52 | 53 | 61 | 62 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/photodraweeview/IAttacher.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.photodraweeview; 2 | 3 | import android.view.GestureDetector; 4 | import android.view.View; 5 | 6 | /** 7 | * **************************************************************************** 8 | * Copyright 2011, 2012 Chris Banes. 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * ***************************************************************************** 22 | */ 23 | 24 | public interface IAttacher { 25 | 26 | public static final float DEFAULT_MAX_SCALE = 3.0f; 27 | public static final float DEFAULT_MID_SCALE = 1.75f; 28 | public static final float DEFAULT_MIN_SCALE = 1.0f; 29 | public static final long ZOOM_DURATION = 200L; 30 | 31 | float getMinimumScale(); 32 | 33 | float getMediumScale(); 34 | 35 | float getMaximumScale(); 36 | 37 | void setMaximumScale(float maximumScale); 38 | 39 | void setMediumScale(float mediumScale); 40 | 41 | void setMinimumScale(float minimumScale); 42 | 43 | float getScale(); 44 | 45 | void setScale(float scale); 46 | 47 | void setScale(float scale, boolean animate); 48 | 49 | void setScale(float scale, float focalX, float focalY, boolean animate); 50 | 51 | void setZoomTransitionDuration(long duration); 52 | 53 | void setAllowParentInterceptOnEdge(boolean allow); 54 | 55 | void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener listener); 56 | 57 | void setOnScaleChangeListener(OnScaleChangeListener listener); 58 | 59 | void setOnLongClickListener(View.OnLongClickListener listener); 60 | 61 | void setOnPhotoTapListener(OnPhotoTapListener listener); 62 | 63 | void setOnViewTapListener(OnViewTapListener listener); 64 | 65 | OnPhotoTapListener getOnPhotoTapListener(); 66 | 67 | OnViewTapListener getOnViewTapListener(); 68 | 69 | void update(int imageInfoWidth, int imageInfoHeight); 70 | } 71 | -------------------------------------------------------------------------------- /image-selector/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/presenter/ImageChoosePresenterCompl.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.presenter; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Environment; 7 | import android.provider.MediaStore; 8 | import android.text.format.DateFormat; 9 | import android.widget.Toast; 10 | 11 | import com.iknow.imageselect.ImageSelectContextHolder; 12 | import com.iknow.imageselect.activities.AbsImageSelectActivity; 13 | import com.iknow.imageselect.activities.BrowseDetailActivity; 14 | import com.iknow.imageselect.model.MediaInfo; 15 | import com.iknow.imageselect.view.IImageChooseView; 16 | import java.io.File; 17 | import java.util.ArrayList; 18 | import java.util.Calendar; 19 | import java.util.Locale; 20 | 21 | /** 22 | * Author: J.Chou 23 | * Email: who_know_me@163.com 24 | * Created: 2016年04月06日 6:38 PM 25 | * Description: 26 | */ 27 | 28 | public class ImageChoosePresenterCompl implements IImageChoosePresenter { 29 | 30 | Activity context; 31 | IImageChooseView imageChooseView; 32 | String mCameraImagePath; 33 | 34 | public ImageChoosePresenterCompl(Activity context, IImageChooseView chooseView) { 35 | this.context = context; 36 | this.imageChooseView = chooseView; 37 | } 38 | 39 | @Override 40 | public void doCancel() { 41 | context.finish(); 42 | } 43 | 44 | @Override 45 | public String takePhotos() { 46 | try { 47 | final Intent intent = new Intent(); 48 | final String dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + "/"; 49 | final File f = new File(dir); 50 | if (!f.exists()) { 51 | f.mkdir(); 52 | } 53 | 54 | mCameraImagePath = dir + DateFormat.format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".jpg"; 55 | final File f1 = new File(mCameraImagePath); 56 | final Uri uri = Uri.fromFile(f1); 57 | 58 | intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); 59 | intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); 60 | if (intent.resolveActivity(ImageSelectContextHolder.getInstance().getContext().getPackageManager()) != null) { 61 | context.startActivityForResult(intent, AbsImageSelectActivity.PHOTO_REQUEST_CAMERA); 62 | } 63 | } catch (Throwable e) { 64 | e.printStackTrace(); 65 | Toast.makeText(context, "启动相机失败,请重试", Toast.LENGTH_LONG).show(); 66 | } 67 | 68 | return mCameraImagePath; 69 | } 70 | 71 | @Override public void doSendAction() { 72 | 73 | } 74 | 75 | @Override public void doPreview(ArrayList hasCheckedImages) { 76 | if(hasCheckedImages.isEmpty()) 77 | return; 78 | BrowseDetailActivity.goToBrowseDetailActivitySelected(context,hasCheckedImages); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/photodraweeview/DefaultOnDoubleTapListener.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.photodraweeview; 2 | 3 | import android.graphics.RectF; 4 | import android.view.GestureDetector; 5 | import android.view.MotionEvent; 6 | 7 | import com.facebook.drawee.generic.GenericDraweeHierarchy; 8 | import com.facebook.drawee.view.DraweeView; 9 | 10 | public class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener { 11 | 12 | private Attacher mAttacher; 13 | 14 | public DefaultOnDoubleTapListener(Attacher attacher) { 15 | setPhotoDraweeViewAttacher(attacher); 16 | } 17 | 18 | @Override public boolean onSingleTapConfirmed(MotionEvent e) { 19 | 20 | if (mAttacher == null) { 21 | return false; 22 | } 23 | DraweeView draweeView = mAttacher.getDraweeView(); 24 | if (draweeView == null) { 25 | return false; 26 | } 27 | 28 | if (mAttacher.getOnPhotoTapListener() != null) { 29 | final RectF displayRect = mAttacher.getDisplayRect(); 30 | 31 | if (null != displayRect) { 32 | final float x = e.getX(), y = e.getY(); 33 | if (displayRect.contains(x, y)) { 34 | float xResult = (x - displayRect.left) / displayRect.width(); 35 | float yResult = (y - displayRect.top) / displayRect.height(); 36 | mAttacher.getOnPhotoTapListener().onPhotoTap(draweeView, xResult, yResult); 37 | return true; 38 | } 39 | } 40 | } 41 | 42 | if (mAttacher.getOnViewTapListener() != null) { 43 | mAttacher.getOnViewTapListener().onViewTap(draweeView, e.getX(), e.getY()); 44 | return true; 45 | } 46 | 47 | return false; 48 | } 49 | 50 | @Override public boolean onDoubleTap(MotionEvent event) { 51 | if (mAttacher == null) { 52 | return false; 53 | } 54 | 55 | try { 56 | float scale = mAttacher.getScale(); 57 | float x = event.getX(); 58 | float y = event.getY(); 59 | 60 | if (scale < mAttacher.getMediumScale()) { 61 | mAttacher.setScale(mAttacher.getMediumScale(), x, y, true); 62 | } else if (scale >= mAttacher.getMediumScale() && scale < mAttacher.getMaximumScale()) { 63 | mAttacher.setScale(mAttacher.getMaximumScale(), x, y, true); 64 | } else { 65 | mAttacher.setScale(mAttacher.getMinimumScale(), x, y, true); 66 | } 67 | } catch (Exception e) { 68 | // Can sometimes happen when getX() and getY() is called 69 | } 70 | return true; 71 | } 72 | 73 | @Override public boolean onDoubleTapEvent(MotionEvent event) { 74 | return false; 75 | } 76 | 77 | public void setPhotoDraweeViewAttacher(Attacher attacher) { 78 | mAttacher = attacher; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/widget/PicItemCheckedView.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.drawable.Drawable; 6 | import android.util.AttributeSet; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.widget.Checkable; 10 | import android.widget.FrameLayout; 11 | import android.widget.ImageView; 12 | import android.widget.RelativeLayout; 13 | import com.facebook.drawee.view.SimpleDraweeView; 14 | import com.iknow.imageselect.R; 15 | import com.iknow.imageselect.utils.DeviceInforHelper; 16 | 17 | public class PicItemCheckedView extends RelativeLayout implements Checkable { 18 | 19 | private Context mContext; 20 | private boolean mChecked; 21 | private SimpleDraweeView mImgView = null; 22 | private ImageView mSelectView,mUnSelectView,mVideoIcon; 23 | private View mSelectPanelView; 24 | 25 | private static final int columnNum = 3; 26 | 27 | public PicItemCheckedView(Context context,boolean isHide) { 28 | this(context, null, 0,isHide); 29 | } 30 | 31 | public PicItemCheckedView(Context context) { 32 | this(context, null, 0,false); 33 | } 34 | 35 | public PicItemCheckedView(Context context, AttributeSet attrs) { 36 | this(context, attrs, 0,false); 37 | } 38 | 39 | public PicItemCheckedView(Context context, AttributeSet attrs, int defStyle,boolean isHide) { 40 | super(context, attrs, defStyle); 41 | mContext = context; 42 | LayoutInflater.from(mContext).inflate(R.layout.image_select_item, this); 43 | mImgView = (SimpleDraweeView) findViewById(R.id.img_view); 44 | mVideoIcon = (ImageView) findViewById(R.id.video_icon); 45 | 46 | int size = DeviceInforHelper.getScreenWidth()/ columnNum; 47 | FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mImgView.getLayoutParams(); 48 | params.width = size; 49 | params.height = size; 50 | mImgView.setLayoutParams(params); 51 | 52 | mSelectView = (ImageView) findViewById(R.id.select); 53 | mUnSelectView = (ImageView) findViewById(R.id.unselect); 54 | if(isHide) { 55 | mUnSelectView.setVisibility(View.GONE); 56 | } 57 | 58 | mSelectPanelView = findViewById(R.id.album_pic_select_panel); 59 | } 60 | 61 | @Override 62 | public void setChecked(boolean checked) { 63 | mChecked = checked; 64 | mSelectView.setVisibility(checked ? View.VISIBLE : View.GONE); 65 | mUnSelectView.setVisibility(checked ? View.GONE : View.VISIBLE); 66 | } 67 | 68 | @Override 69 | public boolean isChecked() { 70 | return mChecked; 71 | } 72 | 73 | @Override 74 | public void toggle() { 75 | setChecked(!mChecked); 76 | } 77 | 78 | public void setImgResId(int resId) { 79 | if (mImgView != null) { 80 | mImgView.setBackgroundResource(resId); 81 | } 82 | } 83 | 84 | public void setImageView(Bitmap bp){ 85 | if(mImgView != null){ 86 | mImgView.setImageBitmap(bp); 87 | } 88 | } 89 | 90 | public void setImageView(Drawable db){ 91 | if(mImgView != null){ 92 | mImgView.setImageDrawable(db); 93 | } 94 | } 95 | 96 | public ImageView getVideoIcon() { 97 | return mVideoIcon; 98 | } 99 | 100 | public SimpleDraweeView getImageView(){ 101 | return mImgView; 102 | } 103 | 104 | public View getSelectPanelView(){ 105 | return mSelectPanelView; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/utils/ImageFilePathUtil.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.utils; 2 | 3 | import android.text.TextUtils; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.security.NoSuchAlgorithmException; 7 | 8 | /** 9 | * Author: J.Chou 10 | * Email: who_know_me@163.com 11 | * Created: 2016年04月06日 3:46 PM 12 | * Description: 13 | */ 14 | 15 | public class ImageFilePathUtil { 16 | public static final String DESTINATION_CACHE_FOLDER = android.os.Environment.getExternalStorageDirectory() 17 | .getPath() + "/Android/data/com.iknow.imageselect/"; 18 | 19 | public static String getImgUrl(String url){ 20 | 21 | if(TextUtils.isEmpty(url) || url.length() < 5){ 22 | return ""; 23 | } 24 | 25 | if (url.substring(0, 4).equalsIgnoreCase("http")) { 26 | String filePath = getLocalImagePath(url); 27 | File f = new File(filePath); 28 | if (f.exists()) { 29 | url = "file:///"+ filePath; 30 | } 31 | } else { 32 | String filePath2 = getLocalImagePath(url); 33 | File f = new File(filePath2); 34 | if (f.exists()) { 35 | url = "file:///"+ filePath2; 36 | } else{ 37 | url = "file:///" + url; 38 | } 39 | 40 | } 41 | return url; 42 | } 43 | 44 | public static final String getCacheImagePath() { 45 | String path = DESTINATION_CACHE_FOLDER + "image/"; 46 | initPath(path); 47 | 48 | createNoMediaScanFile(path); 49 | 50 | return path; 51 | } 52 | 53 | /** 54 | * 创建nomedia文件,防止图库扫描 55 | * 56 | * @param path 57 | */ 58 | private static void createNoMediaScanFile(String path) { 59 | File noMediaFile = new File(path + "nomedia"); 60 | if (!noMediaFile.exists()) { 61 | try { 62 | noMediaFile.createNewFile(); 63 | } catch (IOException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | } 68 | 69 | public static final String getCacheDbPath() { 70 | String path = DESTINATION_CACHE_FOLDER + "data/"; 71 | initPath(path); 72 | return path; 73 | } 74 | 75 | public static final String getImagePath(String url){ 76 | 77 | if(TextUtils.isEmpty(url)){ 78 | return ""; 79 | } 80 | 81 | int index = url.lastIndexOf("?"); 82 | 83 | if(index == -1){ 84 | return url; 85 | } 86 | 87 | return url.substring(0, index); 88 | } 89 | 90 | public static final String getLocalFileUri(String filePath){ 91 | File f = new File(filePath); 92 | return "file://"+f.getAbsolutePath(); 93 | } 94 | 95 | public static final String getLocalImagePath(String url) { 96 | return getCacheImagePath() + getLocalImageFileName(url); 97 | } 98 | 99 | public static final String getLocalImageFileName(String url) { 100 | return getMD5(url.getBytes()); 101 | } 102 | 103 | private static void initPath(String path) { 104 | File file = new File(path); 105 | if (!file.exists()) { 106 | file.mkdirs(); 107 | } 108 | } 109 | 110 | public static String getMD5(byte[] source) { 111 | StringBuilder sb = new StringBuilder(); 112 | java.security.MessageDigest md5 = null; 113 | try { 114 | md5 = java.security.MessageDigest.getInstance("MD5"); 115 | md5.update(source); 116 | } 117 | catch (NoSuchAlgorithmException e) { 118 | } 119 | if (md5 != null) { 120 | for (byte b : md5.digest()) { 121 | sb.append(String.format("%02X", b)); 122 | } 123 | } 124 | return sb.toString(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /image-selector/src/main/res/layout/abs_image_select_layout.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 16 | 26 | 38 | 39 | 40 | 47 | 53 | 60 | 61 | 62 | 63 | 64 | 65 | 72 | 82 | 83 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/adapter/AlbumListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.adapter; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.text.TextUtils; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.BaseAdapter; 10 | import android.widget.ImageView; 11 | import android.widget.TextView; 12 | import com.facebook.drawee.backends.pipeline.Fresco; 13 | import com.facebook.drawee.backends.pipeline.PipelineDraweeController; 14 | import com.facebook.drawee.view.SimpleDraweeView; 15 | import com.facebook.imagepipeline.common.ResizeOptions; 16 | import com.facebook.imagepipeline.request.ImageRequest; 17 | import com.facebook.imagepipeline.request.ImageRequestBuilder; 18 | import com.iknow.imageselect.R; 19 | import com.iknow.imageselect.model.AlbumInfo; 20 | import com.iknow.imageselect.model.MediaInfo; 21 | import com.iknow.imageselect.utils.DrawableUtil; 22 | import com.iknow.imageselect.utils.ImageFilePathUtil; 23 | import java.util.LinkedList; 24 | 25 | /** 26 | * Author: Jason.Chou 27 | * Email: who_know_me@163.com 28 | * Created: 2016年04月12日 3:26 PM 29 | * Description: 30 | */ 31 | public class AlbumListAdapter extends BaseAdapter{ 32 | private LinkedList adapterListData; 33 | private Context context; 34 | private String hasChooseItemName; 35 | public AlbumListAdapter(Context context,LinkedList dataList,String itemName) { 36 | super(); 37 | this.context = context; 38 | this.adapterListData = dataList; 39 | this.hasChooseItemName = itemName; 40 | } 41 | 42 | @Override public int getCount() { 43 | return adapterListData.size(); 44 | } 45 | 46 | @Override public Object getItem(int id) { 47 | return adapterListData.get(id); 48 | } 49 | 50 | @Override public long getItemId(int id) { 51 | return id; 52 | } 53 | 54 | @Override public View getView(int position, View convertView, ViewGroup viewGroup) { 55 | ViewHolder h; 56 | AlbumInfo albumInfo = (AlbumInfo) getItem(position); 57 | if(null == convertView){ 58 | convertView = LayoutInflater.from(context).inflate(R.layout.ablum_list_view_item, null); 59 | h = new ViewHolder(); 60 | h.albumCover = (SimpleDraweeView) convertView.findViewById(R.id.album_cover); 61 | h.albumName = (TextView) convertView.findViewById(R.id.album_name); 62 | h.albumNumber = (TextView) convertView.findViewById(R.id.album_count); 63 | h.chooseIcon = (ImageView) convertView.findViewById(R.id.icon_item_choosen); 64 | convertView.setTag(h); 65 | }else{ 66 | h = (ViewHolder) convertView.getTag(); 67 | } 68 | 69 | MediaInfo imageInfo = albumInfo.medias.get(0); 70 | String path = imageInfo.fileName; 71 | if(!TextUtils.isEmpty(imageInfo.thumbPath)){ 72 | path = imageInfo.thumbPath; 73 | } 74 | 75 | ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(ImageFilePathUtil.getImgUrl(path))) 76 | .setResizeOptions(new ResizeOptions(100, 100)).build(); 77 | 78 | PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder() 79 | .setOldController(h.albumCover.getController()) 80 | .setImageRequest(request) 81 | .build(); 82 | h.albumCover.setController(controller); 83 | 84 | h.albumName.setText(albumInfo.name); 85 | h.albumNumber.setText(""+albumInfo.medias.size()); 86 | if(hasChooseItemName.equals(albumInfo.name)) { 87 | h.chooseIcon.setImageDrawable(DrawableUtil.decodeFromVector(context, R.drawable.ic_radio_button_checked)); 88 | h.chooseIcon.setVisibility(View.VISIBLE); 89 | }else 90 | h.chooseIcon.setVisibility(View.GONE); 91 | return convertView; 92 | } 93 | 94 | static class ViewHolder{ 95 | SimpleDraweeView albumCover; 96 | TextView albumName,albumNumber; 97 | ImageView chooseIcon; 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/configs/ImagePipelineConfigFactory.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.configs; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | import android.os.Environment; 6 | import com.facebook.cache.disk.DiskCacheConfig; 7 | import com.facebook.common.internal.Supplier; 8 | import com.facebook.common.util.ByteConstants; 9 | import com.facebook.imagepipeline.cache.MemoryCacheParams; 10 | import com.facebook.imagepipeline.core.ImagePipelineConfig; 11 | import java.io.File; 12 | 13 | /** 14 | * Author: Jason.Chou 15 | * Email: who_know_me@163.com 16 | * Created: 2016年04月27日 11:13 AM 17 | * Description: 18 | */ 19 | public class ImagePipelineConfigFactory { 20 | 21 | private static final String IMAGE_PIPELINE_CACHE_DIR = "imagepipeline_cache"; 22 | 23 | private static ImagePipelineConfig sImagePipelineConfig; 24 | // private static ImagePipelineConfig sOkHttpImagePipelineConfig; 25 | 26 | private static final int MAX_HEAP_SIZE = (int) Runtime.getRuntime().maxMemory(); 27 | 28 | public static final int MAX_DISK_CACHE_SIZE = 300 * ByteConstants.MB; 29 | public static final int MAX_MEMORY_CACHE_SIZE = MAX_HEAP_SIZE / 3; 30 | 31 | /** 32 | * Creates config using android http stack as network backend. 33 | */ 34 | public static ImagePipelineConfig getImagePipelineConfig(Context context) { 35 | if (sImagePipelineConfig == null) { 36 | ImagePipelineConfig.Builder configBuilder = ImagePipelineConfig.newBuilder(context); 37 | configureCaches(configBuilder, context); 38 | sImagePipelineConfig = configBuilder.build(); 39 | } 40 | return sImagePipelineConfig; 41 | } 42 | 43 | /** 44 | * Creates config using OkHttp as network backed. 45 | */ 46 | /* public static ImagePipelineConfig getOkHttpImagePipelineConfig(Context context) { 47 | if (sOkHttpImagePipelineConfig == null) { 48 | OkHttpClient okHttpClient = new OkHttpClient(); 49 | ImagePipelineConfig.Builder configBuilder = 50 | OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient); 51 | configureCaches(configBuilder, context); 52 | sOkHttpImagePipelineConfig = configBuilder.build(); 53 | } 54 | return sOkHttpImagePipelineConfig; 55 | }*/ 56 | 57 | /** 58 | * Configures disk and memory cache not to exceed common limits 59 | */ 60 | private static void configureCaches(ImagePipelineConfig.Builder configBuilder, Context context) { 61 | final MemoryCacheParams bitmapCacheParams = new MemoryCacheParams( 62 | MAX_MEMORY_CACHE_SIZE, // Max total size of elements in the cache 63 | Integer.MAX_VALUE, // Max entries in the cache 64 | MAX_MEMORY_CACHE_SIZE, // Max total size of elements in eviction queue 65 | Integer.MAX_VALUE, // Max length of eviction queue 66 | Integer.MAX_VALUE); // Max cache entry size 67 | configBuilder 68 | .setBitmapMemoryCacheParamsSupplier( 69 | new Supplier() { 70 | public MemoryCacheParams get() { 71 | return bitmapCacheParams; 72 | } 73 | }) 74 | .setMainDiskCacheConfig(DiskCacheConfig.newBuilder(context) 75 | .setBaseDirectoryPath(getExternalCacheDir(context)) 76 | .setBaseDirectoryName(IMAGE_PIPELINE_CACHE_DIR) 77 | .setMaxCacheSize(MAX_DISK_CACHE_SIZE) 78 | .build()) 79 | .setDownsampleEnabled(true) 80 | .setSmallImageDiskCacheConfig(DiskCacheConfig.newBuilder(context) 81 | .setBaseDirectoryPath(getExternalCacheDir(context)) 82 | .setBaseDirectoryName(IMAGE_PIPELINE_CACHE_DIR) 83 | .setMaxCacheSize(MAX_DISK_CACHE_SIZE) 84 | .build() ); 85 | } 86 | 87 | public static File getExternalCacheDir(final Context context) { 88 | if (hasExternalCacheDir()) 89 | return context.getExternalCacheDir(); 90 | 91 | // Before Froyo we need to construct the external cache dir ourselves 92 | final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/"; 93 | return createFile(Environment.getExternalStorageDirectory().getPath() + cacheDir, ""); 94 | } 95 | 96 | public static boolean hasExternalCacheDir() { 97 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; 98 | } 99 | 100 | public static File createFile(String folderPath, String fileName) { 101 | File destDir = new File(folderPath); 102 | if (!destDir.exists()) { 103 | destDir.mkdirs(); 104 | } 105 | return new File(folderPath, fileName); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/activities/MultiSelectImageActivity.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.activities; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Color; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | import com.facebook.drawee.backends.pipeline.Fresco; 11 | import com.facebook.drawee.backends.pipeline.PipelineDraweeController; 12 | import com.facebook.imagepipeline.common.ResizeOptions; 13 | import com.facebook.imagepipeline.request.ImageRequest; 14 | import com.facebook.imagepipeline.request.ImageRequestBuilder; 15 | import com.iknow.imageselect.R; 16 | import com.iknow.imageselect.model.MediaInfo; 17 | import com.iknow.imageselect.utils.ImageFilePathUtil; 18 | import com.iknow.imageselect.widget.PicItemCheckedView; 19 | import com.iknow.imageselect.widget.TitleView; 20 | 21 | /** 22 | * Author: Jason.Chou 23 | * Email: who_know_me@163.com 24 | * Created: 2016年04月12日 5:02 PM 25 | * Description: 26 | */ 27 | public class MultiSelectImageActivity extends AbsImageSelectActivity{ 28 | 29 | private TextView mSendBtn,mPreviewBtn; 30 | 31 | @Override 32 | protected void initTitleView(TitleView titleView) { 33 | mSendBtn = (TextView) titleView.getRBtnView(); 34 | mSendBtn.setOnClickListener(new View.OnClickListener() { 35 | @Override public void onClick(View pView) { 36 | imageChoosePresenter.doSendAction(); 37 | } 38 | }); 39 | } 40 | 41 | @Override protected void initBottomView(View bottomView) { 42 | mPreviewBtn = (TextView) bottomView.findViewById(R.id.preview_btn); 43 | mPreviewBtn.setOnClickListener(new View.OnClickListener() { 44 | @Override public void onClick(View pView) { 45 | imageChoosePresenter.doPreview(hasCheckedImages); 46 | } 47 | }); 48 | } 49 | 50 | @Override protected void onBindViewHolderToChild(MediaInfo model,ImageSelectViewHolder holder, int position) { 51 | ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(ImageFilePathUtil.getImgUrl(model.fileName))) 52 | .setResizeOptions(new ResizeOptions(100, 100)).build(); 53 | 54 | PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder() 55 | .setOldController(holder.picImageView.getController()) 56 | .setImageRequest(request) 57 | .build(); 58 | holder.picImageView.setController(controller); 59 | 60 | holder.videoIcon.setVisibility(model.mediaType == 3 ? View.VISIBLE:View.GONE); 61 | } 62 | 63 | @Override public View getRecyclerItemView(ViewGroup parentView, int position) { 64 | return new PicItemCheckedView(this); 65 | } 66 | 67 | 68 | @Override 69 | protected void onImageSelectItemClick(View view, int position) { 70 | if(view instanceof PicItemCheckedView){ 71 | PicItemCheckedView item = (PicItemCheckedView)view; 72 | boolean isChecked = item.isChecked(); 73 | item.setChecked(!isChecked); 74 | if(isChecked && hasCheckedImages.contains(allMedias.get(position))) 75 | hasCheckedImages.remove(allMedias.get(position)); 76 | else if(!isChecked && !hasCheckedImages.contains(allMedias.get(position))){ 77 | hasCheckedImages.add(allMedias.get(position)); 78 | } 79 | 80 | if(hasCheckedImages.size() > 0 ) { 81 | mSendBtn.setTextAppearance(mContext,R.style.text_16_ffffff); 82 | mSendBtn.setEnabled(true); 83 | mPreviewBtn.setEnabled(true); 84 | mPreviewBtn.setText("预览"+"("+hasCheckedImages.size()+")"); 85 | mPreviewBtn.setTextColor(Color.parseColor("#ffffff")); 86 | }else { 87 | mSendBtn.setEnabled(false); 88 | mPreviewBtn.setEnabled(false); 89 | mPreviewBtn.setText("预览"); 90 | mSendBtn.setTextAppearance(mContext,R.style.gray_text_16); 91 | mPreviewBtn.setTextAppearance(mContext,R.style.gray_text_16); 92 | } 93 | 94 | } 95 | } 96 | 97 | @Override 98 | protected void onImageItemClick(View view, int position) { 99 | BrowseDetailActivity.goToBrowseDetailActivity(this,allMedias,position); 100 | } 101 | 102 | @Override protected void onCameraActivityResult(String path) { 103 | Bundle bd = new Bundle(); 104 | hasCheckedImages.clear(); 105 | hasCheckedImages.add(MediaInfo.buildOneImage(path)); 106 | bd.putSerializable("ImageData",hasCheckedImages); 107 | Intent intent = new Intent(); 108 | intent.putExtras(bd); 109 | MultiSelectImageActivity.this.setResult(RESULT_OK, intent); 110 | MultiSelectImageActivity.this.finish(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/photodraweeview/PhotoDraweeView.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.photodraweeview; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.support.annotation.NonNull; 6 | import android.util.AttributeSet; 7 | import android.view.GestureDetector; 8 | import android.view.MotionEvent; 9 | import com.facebook.drawee.generic.GenericDraweeHierarchy; 10 | import com.facebook.drawee.view.SimpleDraweeView; 11 | 12 | public class PhotoDraweeView extends SimpleDraweeView implements IAttacher { 13 | 14 | private Attacher mAttacher; 15 | 16 | public PhotoDraweeView(Context context, GenericDraweeHierarchy hierarchy) { 17 | super(context, hierarchy); 18 | init(); 19 | } 20 | 21 | public PhotoDraweeView(Context context) { 22 | super(context); 23 | init(); 24 | } 25 | 26 | public PhotoDraweeView(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | init(); 29 | } 30 | 31 | public PhotoDraweeView(Context context, AttributeSet attrs, int defStyle) { 32 | super(context, attrs, defStyle); 33 | init(); 34 | } 35 | 36 | protected void init() { 37 | if (mAttacher == null || mAttacher.getDraweeView() == null) { 38 | mAttacher = new Attacher(this); 39 | } 40 | } 41 | 42 | @Override public boolean onTouchEvent(MotionEvent event) { 43 | 44 | return super.onTouchEvent(event); 45 | } 46 | 47 | @Override protected void onDraw(@NonNull Canvas canvas) { 48 | int saveCount = canvas.save(); 49 | canvas.concat(mAttacher.getDrawMatrix()); 50 | super.onDraw(canvas); 51 | canvas.restoreToCount(saveCount); 52 | } 53 | 54 | @Override protected void onAttachedToWindow() { 55 | init(); 56 | super.onAttachedToWindow(); 57 | } 58 | 59 | @Override protected void onDetachedFromWindow() { 60 | mAttacher.onDetachedFromWindow(); 61 | super.onDetachedFromWindow(); 62 | } 63 | 64 | @Override public float getMinimumScale() { 65 | return mAttacher.getMinimumScale(); 66 | } 67 | 68 | @Override public float getMediumScale() { 69 | return mAttacher.getMediumScale(); 70 | } 71 | 72 | @Override public float getMaximumScale() { 73 | return mAttacher.getMaximumScale(); 74 | } 75 | 76 | @Override public void setMinimumScale(float minimumScale) { 77 | mAttacher.setMinimumScale(minimumScale); 78 | } 79 | 80 | @Override public void setMediumScale(float mediumScale) { 81 | mAttacher.setMediumScale(mediumScale); 82 | } 83 | 84 | @Override public void setMaximumScale(float maximumScale) { 85 | mAttacher.setMaximumScale(maximumScale); 86 | } 87 | 88 | @Override public float getScale() { 89 | return mAttacher.getScale(); 90 | } 91 | 92 | @Override public void setScale(float scale) { 93 | mAttacher.setScale(scale); 94 | } 95 | 96 | @Override public void setScale(float scale, boolean animate) { 97 | mAttacher.setScale(scale, animate); 98 | } 99 | 100 | @Override public void setScale(float scale, float focalX, float focalY, boolean animate) { 101 | mAttacher.setScale(scale, focalX, focalY, animate); 102 | } 103 | 104 | @Override public void setZoomTransitionDuration(long duration) { 105 | mAttacher.setZoomTransitionDuration(duration); 106 | } 107 | 108 | @Override public void setAllowParentInterceptOnEdge(boolean allow) { 109 | mAttacher.setAllowParentInterceptOnEdge(allow); 110 | } 111 | 112 | @Override public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener listener) { 113 | mAttacher.setOnDoubleTapListener(listener); 114 | } 115 | 116 | @Override public void setOnScaleChangeListener(OnScaleChangeListener listener) { 117 | mAttacher.setOnScaleChangeListener(listener); 118 | } 119 | 120 | @Override public void setOnLongClickListener(OnLongClickListener listener) { 121 | mAttacher.setOnLongClickListener(listener); 122 | } 123 | 124 | @Override public void setOnPhotoTapListener(OnPhotoTapListener listener) { 125 | mAttacher.setOnPhotoTapListener(listener); 126 | } 127 | 128 | @Override public void setOnViewTapListener(OnViewTapListener listener) { 129 | mAttacher.setOnViewTapListener(listener); 130 | } 131 | 132 | @Override public OnPhotoTapListener getOnPhotoTapListener() { 133 | return mAttacher.getOnPhotoTapListener(); 134 | } 135 | 136 | @Override public OnViewTapListener getOnViewTapListener() { 137 | return mAttacher.getOnViewTapListener(); 138 | } 139 | 140 | @Override public void update(int imageInfoWidth, int imageInfoHeight) { 141 | mAttacher.update(imageInfoWidth, imageInfoHeight); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /image-selector/src/main/res/layout/layout_browse_detail_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 24 | 25 | 30 | 31 | 36 | 37 | 38 | 42 | 43 | 51 | 52 | 56 | 57 | 63 | 64 | 74 | 75 | 76 | 77 | 78 | 86 | 87 | 93 | 94 | 98 | 99 | 107 | 108 | 109 | 113 | 114 | 120 | 121 | 125 | 126 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/activities/SingleSelectImageActivity.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.activities; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import com.iknow.imageselect.R; 12 | import com.iknow.imageselect.model.MediaInfo; 13 | import com.iknow.imageselect.widget.PicItemCheckedView; 14 | import com.iknow.imageselect.widget.TitleView; 15 | import java.util.ArrayList; 16 | 17 | /** 18 | * Author: Jason.Chou 19 | * Email: who_know_me@163.com 20 | * Created: 2016年04月12日 5:03 PM 21 | * Description: 22 | */ 23 | public class SingleSelectImageActivity extends AbsImageSelectActivity { 24 | 25 | // =========================================================== 26 | // Constants 27 | // =========================================================== 28 | 29 | // =========================================================== 30 | // Fields 31 | // =========================================================== 32 | private ArrayList hasCheckedView = new ArrayList<>(); 33 | private View mCameraIv; 34 | private TextView mPreviewBtn; 35 | // =========================================================== 36 | // Constructors 37 | // =========================================================== 38 | public static void startActivityForResult(Activity context){ 39 | context.startActivityForResult(new Intent(context,SingleSelectImageActivity.class), 40 | SINGLE_PIC_SELECT_REQUEST); 41 | } 42 | // =========================================================== 43 | // Getter & Setter 44 | // =========================================================== 45 | 46 | // =========================================================== 47 | // Methods for/from SuperClass/Interfaces 48 | // =========================================================== 49 | @Override 50 | protected void initTitleView(TitleView titleView) { 51 | this.mCameraIv = titleView.getRBtnView(); 52 | titleView.setOnRightBtnClickListener(new TitleView.OnRightBtnClickListener() { 53 | @Override public void onRBtnClick(View v) { 54 | mTakeCameraImagePath = imageChoosePresenter.takePhotos(); 55 | } 56 | }); 57 | titleView.setOnLeftBtnClickListener(new TitleView.OnLeftBtnClickListener() { 58 | @Override public void onRBtnClick(View v) { 59 | imageChoosePresenter.doCancel(); 60 | } 61 | }); 62 | 63 | } 64 | 65 | @Override protected void initBottomView(View bottomView) { 66 | mPreviewBtn = (TextView) bottomView.findViewById(R.id.preview_btn); 67 | mPreviewBtn.setTextAppearance(mContext,R.style.gray_text_18_style); 68 | mPreviewBtn.setEnabled(false); 69 | mPreviewBtn.setOnClickListener(new View.OnClickListener() { 70 | @Override public void onClick(View pView) { 71 | Bundle bd = new Bundle(); 72 | imageChoosePresenter.doPreview(hasCheckedImages); 73 | } 74 | }); 75 | } 76 | 77 | @Override protected void onBindViewHolderToChild(MediaInfo model, 78 | ImageSelectViewHolder holder, int position) { 79 | 80 | } 81 | 82 | @Override 83 | protected View getRecyclerItemView(ViewGroup parentView, int position) { 84 | if (parentView == null) { 85 | parentView = new PicItemCheckedView(this,true); 86 | } 87 | 88 | try { 89 | PicItemCheckedView view = ((PicItemCheckedView) parentView); 90 | long picId = allMedias.get(position).fileId; 91 | if (picId < 0) { 92 | throw new RuntimeException("the pic id is not num"); 93 | } 94 | 95 | final ImageView iv = view.getImageView(); 96 | iv.setScaleType(ImageView.ScaleType.FIT_XY); 97 | 98 | 99 | //if (hasCheckedImages.contains(allMedias.get(position))) { 100 | // view.setChecked(true); 101 | //} else { 102 | // view.setChecked(false); 103 | //} 104 | } catch (Exception e) { 105 | e.printStackTrace(); 106 | } 107 | return parentView; 108 | } 109 | 110 | @Override 111 | protected void onImageSelectItemClick(View view, int position) { 112 | if(view instanceof PicItemCheckedView){ 113 | 114 | PicItemCheckedView item = (PicItemCheckedView)view; 115 | boolean isChecked = item.isChecked(); 116 | 117 | if(isChecked) { 118 | hasCheckedImages.remove(allMedias.get(position)); 119 | item.setChecked(false); 120 | } 121 | else{ 122 | resetImagesChecked(); 123 | hasCheckedView.add(item); 124 | hasCheckedImages.add(allMedias.get(position)); 125 | item.setChecked(true); 126 | } 127 | 128 | if(hasCheckedImages.size() > 0 ) { 129 | mPreviewBtn.setTextAppearance(mContext,R.style.text_18_ffffff); 130 | mPreviewBtn.setEnabled(true); 131 | }else { 132 | mPreviewBtn.setTextAppearance(mContext,R.style.gray_text_18_style); 133 | mPreviewBtn.setEnabled(false); 134 | } 135 | } 136 | } 137 | 138 | @Override 139 | protected void onImageItemClick(View view, int position) { 140 | 141 | } 142 | 143 | @Override protected void onCameraActivityResult(String path) { 144 | Bundle bd = new Bundle(); 145 | hasCheckedImages.clear(); 146 | hasCheckedImages.add(MediaInfo.buildOneImage(path)); 147 | bd.putSerializable("ImageData",hasCheckedImages); 148 | Intent intent = new Intent(); 149 | intent.putExtras(bd); 150 | SingleSelectImageActivity.this.setResult(RESULT_OK, intent); 151 | SingleSelectImageActivity.this.finish(); 152 | } 153 | 154 | // =========================================================== 155 | // Methods 156 | // =========================================================== 157 | private void resetImagesChecked(){ 158 | if(hasCheckedView.size()>0){ 159 | hasCheckedView.get(0).setChecked(false); 160 | hasCheckedView.clear(); 161 | hasCheckedImages.clear(); 162 | } 163 | } 164 | // =========================================================== 165 | // Inner and Anonymous Classes 166 | // =========================================================== 167 | } 168 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/photodraweeview/ScaleDragDetector.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.photodraweeview; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.MotionEventCompat; 5 | import android.view.MotionEvent; 6 | import android.view.ScaleGestureDetector; 7 | import android.view.VelocityTracker; 8 | import android.view.ViewConfiguration; 9 | 10 | /** 11 | * **************************************************************************** 12 | * Copyright 2011, 2012 Chris Banes. 13 | * 14 | * Licensed under the Apache License, Version 2.0 (the "License"); 15 | * you may not use this file except in compliance with the License. 16 | * You may obtain a copy of the License at 17 | * 18 | * http://www.apache.org/licenses/LICENSE-2.0 19 | * 20 | * Unless required by applicable law or agreed to in writing, software 21 | * distributed under the License is distributed on an "AS IS" BASIS, 22 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23 | * See the License for the specific language governing permissions and 24 | * limitations under the License. 25 | * ***************************************************************************** 26 | */ 27 | public class ScaleDragDetector implements ScaleGestureDetector.OnScaleGestureListener { 28 | 29 | private static final int INVALID_POINTER_ID = -1; 30 | 31 | private final float mTouchSlop; 32 | private final float mMinimumVelocity; 33 | private final ScaleGestureDetector mScaleDetector; 34 | private final OnScaleDragGestureListener mScaleDragGestureListener; 35 | 36 | private VelocityTracker mVelocityTracker; 37 | private boolean mIsDragging; 38 | float mLastTouchX; 39 | float mLastTouchY; 40 | private int mActivePointerId = INVALID_POINTER_ID; 41 | private int mActivePointerIndex = 0; 42 | 43 | public ScaleDragDetector(Context context, OnScaleDragGestureListener scaleDragGestureListener) { 44 | mScaleDetector = new ScaleGestureDetector(context, this); 45 | mScaleDragGestureListener = scaleDragGestureListener; 46 | 47 | final ViewConfiguration configuration = ViewConfiguration.get(context); 48 | mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); 49 | mTouchSlop = configuration.getScaledTouchSlop(); 50 | } 51 | 52 | @Override public boolean onScale(ScaleGestureDetector detector) { 53 | float scaleFactor = detector.getScaleFactor(); 54 | 55 | if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) { 56 | return false; 57 | } 58 | 59 | mScaleDragGestureListener.onScale(scaleFactor, detector.getFocusX(), detector.getFocusY()); 60 | return true; 61 | } 62 | 63 | @Override public boolean onScaleBegin(ScaleGestureDetector detector) { 64 | return true; 65 | } 66 | 67 | @Override public void onScaleEnd(ScaleGestureDetector detector) { 68 | mScaleDragGestureListener.onScaleEnd(); 69 | } 70 | 71 | public boolean isScaling() { 72 | return mScaleDetector.isInProgress(); 73 | } 74 | 75 | public boolean isDragging() { 76 | return mIsDragging; 77 | } 78 | 79 | private float getActiveX(MotionEvent ev) { 80 | try { 81 | return MotionEventCompat.getX(ev, mActivePointerIndex); 82 | } catch (Exception e) { 83 | return ev.getX(); 84 | } 85 | } 86 | 87 | private float getActiveY(MotionEvent ev) { 88 | try { 89 | return MotionEventCompat.getY(ev, mActivePointerIndex); 90 | } catch (Exception e) { 91 | return ev.getY(); 92 | } 93 | } 94 | 95 | public boolean onTouchEvent(MotionEvent ev) { 96 | mScaleDetector.onTouchEvent(ev); 97 | final int action = MotionEventCompat.getActionMasked(ev); 98 | onTouchActivePointer(action, ev); 99 | onTouchDragEvent(action, ev); 100 | return true; 101 | } 102 | 103 | private void onTouchActivePointer(int action, MotionEvent ev) { 104 | switch (action) { 105 | case MotionEvent.ACTION_DOWN: 106 | mActivePointerId = ev.getPointerId(0); 107 | break; 108 | case MotionEvent.ACTION_CANCEL: 109 | case MotionEvent.ACTION_UP: 110 | mActivePointerId = INVALID_POINTER_ID; 111 | break; 112 | case MotionEvent.ACTION_POINTER_UP: 113 | final int pointerIndex = MotionEventCompat.getActionIndex(ev); 114 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 115 | if (pointerId == mActivePointerId) { 116 | final int newPointerIndex = (pointerIndex == 0) ? 1 : 0; 117 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 118 | mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex); 119 | mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex); 120 | } 121 | 122 | break; 123 | } 124 | 125 | mActivePointerIndex = MotionEventCompat.findPointerIndex(ev, 126 | mActivePointerId != INVALID_POINTER_ID ? mActivePointerId : 0); 127 | } 128 | 129 | private void onTouchDragEvent(int action, MotionEvent ev) { 130 | switch (action) { 131 | case MotionEvent.ACTION_DOWN: { 132 | mVelocityTracker = VelocityTracker.obtain(); 133 | if (null != mVelocityTracker) { 134 | mVelocityTracker.addMovement(ev); 135 | } 136 | 137 | mLastTouchX = getActiveX(ev); 138 | mLastTouchY = getActiveY(ev); 139 | mIsDragging = false; 140 | break; 141 | } 142 | 143 | case MotionEvent.ACTION_MOVE: { 144 | final float x = getActiveX(ev); 145 | final float y = getActiveY(ev); 146 | final float dx = x - mLastTouchX, dy = y - mLastTouchY; 147 | 148 | if (!mIsDragging) { 149 | mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop; 150 | } 151 | 152 | if (mIsDragging) { 153 | mScaleDragGestureListener.onDrag(dx, dy); 154 | mLastTouchX = x; 155 | mLastTouchY = y; 156 | 157 | if (null != mVelocityTracker) { 158 | mVelocityTracker.addMovement(ev); 159 | } 160 | } 161 | break; 162 | } 163 | 164 | case MotionEvent.ACTION_CANCEL: { 165 | if (null != mVelocityTracker) { 166 | mVelocityTracker.recycle(); 167 | mVelocityTracker = null; 168 | } 169 | break; 170 | } 171 | 172 | case MotionEvent.ACTION_UP: { 173 | if (mIsDragging) { 174 | if (null != mVelocityTracker) { 175 | mLastTouchX = getActiveX(ev); 176 | mLastTouchY = getActiveY(ev); 177 | 178 | mVelocityTracker.addMovement(ev); 179 | mVelocityTracker.computeCurrentVelocity(1000); 180 | 181 | final float vX = mVelocityTracker.getXVelocity(), vY = 182 | mVelocityTracker.getYVelocity(); 183 | 184 | if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) { 185 | mScaleDragGestureListener.onFling(mLastTouchX, mLastTouchY, -vX, -vY); 186 | } 187 | } 188 | } 189 | if (null != mVelocityTracker) { 190 | mVelocityTracker.recycle(); 191 | mVelocityTracker = null; 192 | } 193 | break; 194 | } 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/activities/AbsImageSelectActivity.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.activities; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.GridLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.text.TextUtils; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.AdapterView; 14 | import android.widget.ImageView; 15 | import android.widget.ListView; 16 | import android.widget.TextView; 17 | import android.widget.Toast; 18 | import com.facebook.drawee.view.SimpleDraweeView; 19 | import com.iknow.imageselect.ImageSelectContextHolder; 20 | import com.iknow.imageselect.R; 21 | import com.iknow.imageselect.adapter.AlbumListAdapter; 22 | import com.iknow.imageselect.core.CoreActivity; 23 | import com.iknow.imageselect.model.AlbumInfo; 24 | import com.iknow.imageselect.model.MediaInfo; 25 | import com.iknow.imageselect.presenter.IImageChoosePresenter; 26 | import com.iknow.imageselect.presenter.ImageChoosePresenterCompl; 27 | import com.iknow.imageselect.utils.MediaFileUtil; 28 | import com.iknow.imageselect.view.IImageChooseView; 29 | import com.iknow.imageselect.widget.PicItemCheckedView; 30 | import com.iknow.imageselect.widget.SpacesItemDecoration; 31 | import com.iknow.imageselect.widget.TitleView; 32 | import java.io.File; 33 | import java.util.ArrayList; 34 | import java.util.LinkedList; 35 | 36 | /** 37 | * Author: J.Chou 38 | * Email: who_know_me@163.com 39 | * Created: 2016年04月06日 3:16 PM 40 | * Description: 41 | */ 42 | 43 | public abstract class AbsImageSelectActivity extends CoreActivity implements IImageChooseView, 44 | View.OnClickListener { 45 | // =========================================================== 46 | // Constants 47 | // =========================================================== 48 | public static final int PHOTO_REQUEST_CAMERA = 0x007; 49 | public static final int MULTI_PIC_SELECT_REQUEST = 0x008; 50 | public static final int SINGLE_PIC_SELECT_REQUEST = 0x009; 51 | private static final String ALL_VIDEO = "所有视频"; 52 | private static final String PIC_AND_VIDEO = "图片和视频"; 53 | // =========================================================== 54 | // Fields 55 | // =========================================================== 56 | protected AppCompatActivity mContext; 57 | protected String mTakeCameraImagePath; 58 | private boolean showAlbumList = false; 59 | private ListView albumListView; 60 | private View albumView; 61 | protected TextView allImagesTv; 62 | protected TitleView gsTitleView; 63 | protected ArrayList allMedias = new ArrayList<>(); 64 | protected ArrayList imageAndVideoAlbums = new ArrayList<>(); 65 | protected ArrayList hasCheckedImages = new ArrayList<>(); 66 | private AlbumInfo allAlbumInfo = new AlbumInfo(); 67 | private AlbumInfo videoAlbumInfo = new AlbumInfo(); 68 | private LinkedList albumLinkedList = new LinkedList<>(); 69 | protected IImageChoosePresenter imageChoosePresenter; 70 | private RecyclerAdapter imageGridAdapter; 71 | private RecyclerView recyclerView; 72 | 73 | protected abstract void initTitleView(TitleView titleView); 74 | protected abstract void initBottomView(View bottomView); 75 | protected abstract void onBindViewHolderToChild(MediaInfo model,ImageSelectViewHolder holder,int position); 76 | protected abstract View getRecyclerItemView(ViewGroup parentView, int position); 77 | protected abstract void onImageSelectItemClick(View view, int position); 78 | protected abstract void onImageItemClick(View view, int position); 79 | protected abstract void onCameraActivityResult(String path); 80 | 81 | // =========================================================== 82 | // Constructors 83 | // =========================================================== 84 | 85 | // =========================================================== 86 | // Getter & Setter 87 | // =========================================================== 88 | 89 | // =========================================================== 90 | // Methods for/from SuperClass/Interfaces 91 | // =========================================================== 92 | @Override 93 | public void onCreate(Bundle savedInstanceState) { 94 | super.onCreate(savedInstanceState); 95 | this.mContext = this; 96 | setContentView(R.layout.abs_image_select_layout); 97 | allMedias = MediaFileUtil.getAllMediaFiles(ImageSelectContextHolder.getInstance().getContext()); 98 | imageAndVideoAlbums = (ArrayList) allMedias.clone(); 99 | videoAlbumInfo.medias = MediaFileUtil.getAllVideoFiles(ImageSelectContextHolder.getInstance().getContext()); 100 | imageChoosePresenter = new ImageChoosePresenterCompl(this, this); 101 | initTitleView(gsTitleView = (TitleView) this.findViewById(R.id.titlebar)); 102 | initBottomView(this.findViewById(R.id.bottomView)); 103 | initContentView(); 104 | } 105 | 106 | 107 | @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { 108 | if (resultCode == Activity.RESULT_OK) { 109 | 110 | 111 | if (requestCode == PHOTO_REQUEST_CAMERA) {//拍照返回 112 | if (TextUtils.isEmpty(mTakeCameraImagePath)) { 113 | return; 114 | } 115 | 116 | final File f = new File(mTakeCameraImagePath); 117 | if (f == null || !f.exists()) { 118 | Toast.makeText(ImageSelectContextHolder.getInstance().getContext(), "照相失败", Toast.LENGTH_SHORT).show(); 119 | return; 120 | } 121 | 122 | try { 123 | Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); 124 | scanIntent.setData(Uri.fromFile(f)); 125 | AbsImageSelectActivity.this.sendBroadcast(scanIntent); 126 | 127 | onCameraActivityResult(mTakeCameraImagePath); 128 | } catch (Throwable e) { 129 | Toast.makeText(AbsImageSelectActivity.this, "照相失败", Toast.LENGTH_SHORT).show(); 130 | e.printStackTrace(); 131 | } 132 | 133 | } 134 | } 135 | super.onActivityResult(requestCode, resultCode, data); 136 | } 137 | 138 | @Override public void onClick(View view) { 139 | if(R.id.all_album_tv == view.getId()) { 140 | if(!showAlbumList) { 141 | showAlbumListView(); 142 | try { 143 | albumLinkedList.clear(); 144 | allAlbumInfo.medias = imageAndVideoAlbums; 145 | if(!allAlbumInfo.medias.isEmpty()){ 146 | allAlbumInfo.name = PIC_AND_VIDEO; 147 | albumLinkedList.add(allAlbumInfo); 148 | }else { 149 | allAlbumInfo.medias = MediaFileUtil.getAllMediaFiles(ImageSelectContextHolder.getInstance().getContext()); 150 | allAlbumInfo.name = PIC_AND_VIDEO; 151 | albumLinkedList.add(allAlbumInfo); 152 | } 153 | 154 | if(!videoAlbumInfo.medias.isEmpty()) { 155 | videoAlbumInfo.name = ALL_VIDEO; 156 | albumLinkedList.add(videoAlbumInfo); 157 | }else{ 158 | videoAlbumInfo.medias = MediaFileUtil.getAllVideoFiles(ImageSelectContextHolder.getInstance().getContext()); 159 | videoAlbumInfo.name = ALL_VIDEO; 160 | albumLinkedList.add(videoAlbumInfo); 161 | } 162 | 163 | albumLinkedList.addAll(MediaFileUtil.getThumbnailsPhotosInfo(this)); 164 | albumListView.setAdapter(new AlbumListAdapter(this, albumLinkedList,allImagesTv.getText().toString())); 165 | }catch(Exception e) { 166 | e.printStackTrace(); 167 | } 168 | }else{ 169 | hideAlbumListView(); 170 | } 171 | }else if(R.id.empty_place == view.getId()) { 172 | hideAlbumListView(); 173 | } 174 | } 175 | 176 | @Override 177 | public void reloadData() { 178 | } 179 | 180 | @Override 181 | protected void onDestroy() { 182 | super.onDestroy(); 183 | allMedias = null; 184 | imageAndVideoAlbums = null; 185 | albumLinkedList = null; 186 | } 187 | 188 | // =========================================================== 189 | // Methods 190 | // =========================================================== 191 | private void initContentView() { 192 | 193 | findViewById(R.id.empty_place).setOnClickListener(this); 194 | albumView = findViewById(R.id.albumView); 195 | albumListView = (ListView) findViewById(R.id.all_album_list); 196 | albumListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 197 | @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { 198 | AlbumInfo albumInfo = (AlbumInfo) adapterView.getItemAtPosition(position); 199 | allMedias = albumInfo.medias; 200 | recyclerView.setAdapter(imageGridAdapter = new RecyclerAdapter(albumInfo.medias)); 201 | allImagesTv.setText(albumInfo.name); 202 | gsTitleView.setTitleText(albumInfo.name); 203 | hideAlbumListView(); 204 | } 205 | }); 206 | 207 | allImagesTv = (TextView) findViewById(R.id.all_album_tv); 208 | allImagesTv.setOnClickListener(this); 209 | 210 | recyclerView = (RecyclerView) this.findViewById(R.id.album_pic_recyclerview); 211 | GridLayoutManager manager = new GridLayoutManager(this, 3); 212 | recyclerView.addItemDecoration(new SpacesItemDecoration(5)); 213 | recyclerView.setHasFixedSize(true); 214 | recyclerView.setAdapter(imageGridAdapter = new RecyclerAdapter(allMedias)); 215 | recyclerView.setLayoutManager(manager); 216 | } 217 | 218 | private void showAlbumListView() { 219 | showAlbumList = true; 220 | albumView.setVisibility(View.VISIBLE); 221 | } 222 | 223 | private void hideAlbumListView() { 224 | albumView.setVisibility(View.INVISIBLE); 225 | showAlbumList = false; 226 | } 227 | 228 | // =========================================================== 229 | // Inner and Anonymous Classes 230 | // =========================================================== 231 | 232 | public class RecyclerAdapter extends RecyclerView.Adapter { 233 | 234 | private ArrayList albumMedias; 235 | 236 | public RecyclerAdapter(ArrayList allMedias) { 237 | this.albumMedias = allMedias; 238 | } 239 | 240 | @Override public ImageSelectViewHolder onCreateViewHolder(ViewGroup parent, int position) { 241 | View view = getRecyclerItemView(parent,position); 242 | return new ImageSelectViewHolder(view); 243 | } 244 | 245 | @Override 246 | public void onBindViewHolder(ImageSelectViewHolder holder, final int position) { 247 | MediaInfo mediaInfo = albumMedias.get(position); 248 | onBindViewHolderToChild(mediaInfo,holder,position); 249 | } 250 | 251 | @Override public int getItemCount() { 252 | return albumMedias.size(); 253 | } 254 | 255 | } 256 | 257 | public class ImageSelectViewHolder extends RecyclerView.ViewHolder { 258 | public SimpleDraweeView picImageView; 259 | public View panelView; 260 | public ImageView videoIcon; 261 | 262 | public ImageSelectViewHolder(final View itemView) { 263 | super(itemView); 264 | if(itemView instanceof PicItemCheckedView){ 265 | picImageView = ((PicItemCheckedView) itemView).getImageView(); 266 | videoIcon = ((PicItemCheckedView) itemView).getVideoIcon(); 267 | panelView = ((PicItemCheckedView) itemView).getSelectPanelView(); 268 | panelView.setOnClickListener(new View.OnClickListener() { 269 | @Override public void onClick(View pView) { 270 | onImageSelectItemClick(itemView,getAdapterPosition()); 271 | } 272 | }); 273 | picImageView.setOnClickListener(new View.OnClickListener() { 274 | @Override 275 | public void onClick(View view) { 276 | onImageItemClick(view,getAdapterPosition()); 277 | 278 | } 279 | }); 280 | } 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/activities/BrowseDetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.activities; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.view.PagerAdapter; 9 | import android.support.v4.view.ViewPager; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.util.Log; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.ImageView; 15 | import android.widget.TextView; 16 | 17 | import com.bumptech.glide.Glide; 18 | import com.iknow.imageselect.R; 19 | import com.iknow.imageselect.model.MediaInfo; 20 | import com.iknow.imageselect.utils.CacheBean; 21 | import com.iknow.imageselect.utils.DrawableUtil; 22 | import com.iknow.imageselect.utils.ImageFilePathUtil; 23 | import com.iknow.imageselect.utils.PhotoDraweeViewUtil; 24 | 25 | import java.io.Serializable; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | import com.iknow.imageselect.photodraweeview.PhotoDraweeView; 30 | 31 | /** 32 | * Created by gordon on 5/9/16. 33 | */ 34 | public class BrowseDetailActivity extends AppCompatActivity implements View.OnClickListener { 35 | 36 | public static final String TOKEN = "BrowseDetailActivity"; 37 | public static final String ALL_MEDIA_LIST = "all_media_list"; 38 | public static final String MEDIA_KEY = "MEDIA_KEY"; 39 | public static final String CHOOSEN_PICS = "CHOOSEN_PICS"; 40 | public static final int CODE_BROWSE_AND_CHOOSE = 0x1128; 41 | 42 | public static final String TYPE_SELECTED = "TYPE_SELECTED"; 43 | public static final String TYPE_EMPTY = "TYPE_EMPTY"; 44 | public static String ENTRY_TYPE = "ENTRY_TYPE"; 45 | public static String CURRENT_INDEX = "CURRENT_INDEX"; 46 | 47 | private String fromType = TYPE_EMPTY; 48 | 49 | private ImageView backImage, orginalImage, chooseImage; 50 | private View send, backArea, originalArea, chooseArea; 51 | private TextView navigationText; 52 | private ViewPager viewPager; 53 | private List medias; 54 | private int count = 0; 55 | 56 | @Override 57 | protected void onCreate(@Nullable Bundle savedInstanceState) { 58 | super.onCreate(savedInstanceState); 59 | prepareData(); 60 | setContentView(R.layout.layout_browse_detail_activity); 61 | findViews(); 62 | addIcons(); 63 | addListeners(); 64 | initialize(); 65 | } 66 | 67 | private void findViews() { 68 | navigationText = (TextView) findViewById(R.id.nav_text); 69 | backArea = findViewById(R.id.back_container); 70 | backImage = (ImageView) findViewById(R.id.back_image); 71 | orginalImage = (ImageView) findViewById(R.id.radio_original); 72 | chooseImage = (ImageView) findViewById(R.id.radio_choose); 73 | originalArea = findViewById(R.id.original_container); 74 | chooseArea = findViewById(R.id.choose_container); 75 | send = findViewById(R.id.send); 76 | viewPager = (ViewPager) findViewById(R.id.view_pager); 77 | } 78 | 79 | private void prepareData() { 80 | medias = new ArrayList<>(); 81 | Intent fromIntent = getIntent(); 82 | if (fromIntent.getExtras() != null) { 83 | currentIndex = fromIntent.getExtras().getInt(CURRENT_INDEX); 84 | fromType = fromIntent.getExtras().getString(ENTRY_TYPE); 85 | List pass = (ArrayList) CacheBean.getParam(TOKEN, ALL_MEDIA_LIST); 86 | if (pass == null) { 87 | //Say something 88 | pass = new ArrayList<>(); 89 | } 90 | CacheBean.clean(TOKEN); 91 | for (MediaInfo info : pass) { 92 | medias.add(fromType.equals(TYPE_EMPTY) ? new BrowseDetailModel(info) : new BrowseDetailModel(true, info)); 93 | } 94 | 95 | } 96 | // medias.addAll(Arrays.asList(new BrowseDetailModel(new MediaInfo()), new BrowseDetailModel(new MediaInfo()), new BrowseDetailModel(new MediaInfo()))); 97 | count = medias.size(); 98 | } 99 | 100 | public void initialize() { 101 | viewPager.setAdapter(new CycleBrowseAdapter()); 102 | viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 103 | @Override 104 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 105 | 106 | } 107 | 108 | @Override 109 | public void onPageSelected(int position) { 110 | setCurrentIndex(position); 111 | } 112 | 113 | @Override 114 | public void onPageScrollStateChanged(int state) { 115 | 116 | } 117 | }); 118 | viewPager.setCurrentItem(currentIndex); 119 | setNavigationText((currentIndex + 1) + "/" + count); 120 | } 121 | 122 | private void addIcons() { 123 | backImage.setImageDrawable(DrawableUtil.decodeFromVector(getApplicationContext(), R.drawable.ic_arrow_back)); 124 | orginalImage.setImageDrawable(DrawableUtil.decodeFromVector(getApplicationContext(), R.drawable.ic_radio_button_unchecked)); 125 | chooseImage.setImageDrawable(DrawableUtil.decodeFromVector(getApplicationContext(), R.drawable.ic_choosable)); 126 | } 127 | 128 | private void addListeners() { 129 | send.setOnClickListener(this); 130 | backArea.setOnClickListener(this); 131 | chooseArea.setOnClickListener(this); 132 | originalArea.setOnClickListener(this); 133 | } 134 | 135 | /** 136 | * variables 137 | */ 138 | private int currentIndex = 0; 139 | private boolean isOriginal = false; 140 | 141 | public void setCurrentIndex(int currentIndex) { 142 | this.currentIndex = currentIndex; 143 | refreshState(); 144 | } 145 | 146 | private void refreshState() { 147 | setChosen(medias.get(currentIndex).isSelected()); 148 | setNavigationText((currentIndex + 1) + "/" + count); 149 | } 150 | 151 | private void setNavigationText(String navigation) { 152 | this.navigationText.setText(navigation); 153 | } 154 | 155 | public boolean isChosen() { 156 | return medias.get(currentIndex).isSelected(); 157 | } 158 | 159 | public void setChosen(boolean chosen) { 160 | medias.get(currentIndex).setSelected(chosen); 161 | chooseImage.setImageDrawable(chosen ? DrawableUtil.decodeFromVector(getApplicationContext(), 162 | R.drawable.ic_chosen) : DrawableUtil.decodeFromVector(getApplicationContext(), R.drawable.ic_choosable)); 163 | } 164 | 165 | public boolean isOriginal() { 166 | return isOriginal; 167 | } 168 | 169 | public void setOriginal(boolean original) { 170 | isOriginal = original; 171 | orginalImage.setImageDrawable(original ? DrawableUtil.decodeFromVector(getApplicationContext(), 172 | R.drawable.ic_radio_button_checked) : DrawableUtil.decodeFromVector(getApplicationContext(), R.drawable.ic_radio_button_unchecked)); 173 | } 174 | 175 | @Override 176 | public void onClick(View view) { 177 | int id = view.getId(); 178 | if (id == R.id.choose_container) { 179 | setChosen(!isChosen()); 180 | } else if (id == R.id.original_container) { 181 | setOriginal(!isOriginal()); 182 | } else if (id == R.id.back_container) { 183 | handleBackAction(); 184 | } else if (id == R.id.send) { 185 | handleSureAction(); 186 | } 187 | } 188 | 189 | class CycleBrowseAdapter extends PagerAdapter { 190 | 191 | private List views; 192 | 193 | public CycleBrowseAdapter() { 194 | views = new ArrayList<>(); 195 | for (BrowseDetailModel model : medias) { 196 | views.add(getLayoutInflater().inflate(R.layout.layout_photo_view_item, viewPager, false)); 197 | } 198 | Log.d("gordon", "views count ->" + views.size()); 199 | } 200 | 201 | @Override 202 | public int getCount() { 203 | return medias.size(); 204 | } 205 | 206 | @Override 207 | public boolean isViewFromObject(View view, Object object) { 208 | return view == object; 209 | } 210 | 211 | @Override 212 | public Object instantiateItem(ViewGroup container, int position) { 213 | View itemView; 214 | container.addView(itemView = views.get(position)); 215 | ImageView videoIcon = (ImageView) itemView.findViewById(R.id.video_icon); 216 | TextView videoSizeTv = (TextView) itemView.findViewById(R.id.video_size_tv); 217 | final MediaInfo mediaInfo = medias.get(position).media; 218 | 219 | if (mediaInfo.mediaType == 3) { 220 | originalArea.setVisibility(View.GONE); 221 | videoIcon.setOnClickListener(new View.OnClickListener() { 222 | @Override 223 | public void onClick(View pView) { 224 | playVideo(mediaInfo); 225 | } 226 | }); 227 | } 228 | videoIcon.setVisibility(mediaInfo.mediaType == 3 ? View.VISIBLE : View.GONE); 229 | videoSizeTv.setVisibility(mediaInfo.mediaType == 3 ? View.VISIBLE : View.GONE); 230 | 231 | PhotoDraweeViewUtil.display((PhotoDraweeView) itemView.findViewById(R.id.photo_view), Uri.parse(ImageFilePathUtil.getImgUrl(medias.get(position).getMedia().fileName))); 232 | return itemView; 233 | } 234 | 235 | @Override 236 | public int getItemPosition(Object object) { 237 | return super.getItemPosition(object); 238 | } 239 | 240 | @Override 241 | public void destroyItem(ViewGroup container, int position, Object object) { 242 | Glide.clear(((View) object).findViewById(R.id.photo_view)); 243 | container.removeView((View) object); 244 | } 245 | } 246 | 247 | private void playVideo(MediaInfo pMediaInfo) { 248 | Uri uri = Uri.parse(pMediaInfo.fileName); 249 | Intent intent = new Intent(Intent.ACTION_VIEW); 250 | intent.setDataAndType(uri, "video/*"); 251 | startActivity(intent); 252 | } 253 | 254 | class BrowseDetailModel implements Serializable { 255 | private boolean isSelected = false; 256 | private MediaInfo media; 257 | 258 | public BrowseDetailModel(boolean isChosen, MediaInfo media) { 259 | this.isSelected = isChosen; 260 | this.media = media; 261 | } 262 | 263 | public BrowseDetailModel(MediaInfo media) { 264 | this.media = media; 265 | } 266 | 267 | public boolean isSelected() { 268 | return isSelected; 269 | } 270 | 271 | public void setSelected(boolean selected) { 272 | isSelected = selected; 273 | } 274 | 275 | public MediaInfo getMedia() { 276 | return media; 277 | } 278 | 279 | public void setMedia(MediaInfo media) { 280 | this.media = media; 281 | } 282 | } 283 | 284 | private void handleSureAction() { 285 | Bundle bundle = new Bundle(); 286 | bundle.putSerializable(CHOOSEN_PICS, (Serializable) getValidModels(medias)); 287 | Intent intent = new Intent(); 288 | intent.putExtras(bundle); 289 | setResult(RESULT_OK, intent); 290 | finish(); 291 | } 292 | 293 | private void handleBackAction() { 294 | finish(); 295 | } 296 | 297 | public static List getValidModels(List models) { 298 | List res = new ArrayList<>(); 299 | for (BrowseDetailModel model : models) { 300 | if (model.isSelected()) res.add(model.getMedia()); 301 | } 302 | return res; 303 | } 304 | 305 | public static void goToBrowseDetailActivity(Activity from, List mediaInfos, int index) { 306 | Intent intent = new Intent(from, BrowseDetailActivity.class); 307 | Bundle bundle = new Bundle(); 308 | bundle.putInt(CURRENT_INDEX, index); 309 | bundle.putString(ENTRY_TYPE, TYPE_EMPTY); 310 | intent.putExtras(bundle); 311 | CacheBean.putParam(BrowseDetailActivity.TOKEN, BrowseDetailActivity.ALL_MEDIA_LIST, mediaInfos); 312 | from.startActivityForResult(intent, CODE_BROWSE_AND_CHOOSE); 313 | } 314 | 315 | public static void goToBrowseDetailActivitySelected(Activity from, List mediaInfos) { 316 | Bundle bundle = new Bundle(); 317 | CacheBean.putParam(BrowseDetailActivity.TOKEN, BrowseDetailActivity.ALL_MEDIA_LIST, mediaInfos); 318 | bundle.putString(ENTRY_TYPE, TYPE_SELECTED); 319 | bundle.putInt(CURRENT_INDEX, 0); 320 | Intent intent = new Intent(from, BrowseDetailActivity.class); 321 | intent.putExtras(bundle); 322 | from.startActivityForResult(intent, CODE_BROWSE_AND_CHOOSE); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/widget/TitleView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.iknow.imageselect.widget; 5 | 6 | import android.app.Activity; 7 | import android.content.Context; 8 | import android.content.res.TypedArray; 9 | import android.graphics.Color; 10 | import android.graphics.drawable.Drawable; 11 | import android.support.v4.view.ViewCompat; 12 | import android.text.TextUtils; 13 | import android.util.AttributeSet; 14 | import android.view.Gravity; 15 | import android.view.KeyEvent; 16 | import android.view.View; 17 | import android.view.ViewGroup; 18 | import android.widget.FrameLayout; 19 | import android.widget.RelativeLayout; 20 | import android.widget.TextView; 21 | import com.iknow.imageselect.R; 22 | import com.iknow.imageselect.core.CoreActivity; 23 | import com.iknow.imageselect.utils.DeviceInforHelper; 24 | 25 | public class TitleView extends RelativeLayout implements View.OnClickListener{ 26 | 27 | // =========================================================== 28 | // Constants 29 | // =========================================================== 30 | private static final int Left_Btn_View_Id = 0x1001; 31 | private static final int Title_View_Id = 0x1002; 32 | private static final int Right_Btn_View_Id = 0x1003; 33 | private static final int DEFAULT_TITLE_HEIGHT = DeviceInforHelper.getPixelFromDip(50); 34 | private static final int DEFAULT_ICON_SIZE = DeviceInforHelper.getPixelFromDip(24); 35 | 36 | private static final int TITLE_TEXT_DEFAULT_STYLE = R.style.text_18_ffffff; 37 | private static final int BTN_TEXT_DEFAULT_STYLE = R.style.text_16_ffffff; 38 | // =========================================================== 39 | // Fields 40 | // =========================================================== 41 | private TextView mTitleTextView; 42 | private TextView mSubTitleTextView; 43 | private String mTitleText; 44 | private int mTitleStyle; 45 | 46 | private View mLeftBtnTextView; 47 | private View mLeftBtnImgView; 48 | private String mLeftBtnText; 49 | private Drawable mLBtnDrawable; 50 | private int mLBtnStyle; 51 | private boolean isShowLBtn; 52 | 53 | private View mRightBtnTextView; 54 | private View mRightBtnImgView; 55 | private String mRightBtnText; 56 | private Drawable mRBtnBgDrawable; 57 | private int mRBtnStyle; 58 | private boolean isShowRBtn; 59 | 60 | private OnTitleClickListener mOnTitleClickListener; 61 | private OnRightBtnClickListener mOnRightBtnClickListener; 62 | private OnLeftBtnClickListener mOnLeftBtnClickListener; 63 | 64 | // =========================================================== 65 | // Constructors 66 | // =========================================================== 67 | 68 | // =========================================================== 69 | // Getter & Setter 70 | // =========================================================== 71 | 72 | // =========================================================== 73 | // Methods for/from SuperClass/Interfaces 74 | // =========================================================== 75 | public TitleView(Context context){ 76 | this(context,null); 77 | } 78 | 79 | public TitleView(Context context, AttributeSet attrs) { 80 | super(context, attrs); 81 | initView(context,attrs); 82 | setupView(context); 83 | } 84 | 85 | private void initView(Context context, AttributeSet attrs) { 86 | if (attrs != null){ 87 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TitleView); 88 | mTitleText = a.getString(R.styleable.TitleView_title_text); 89 | mTitleStyle = a.getResourceId(R.styleable.TitleView_title_text_appearance, -1); 90 | 91 | isShowLBtn = a.getBoolean(R.styleable.TitleView_is_show_left_button, true); 92 | if(isShowLBtn){ 93 | mLeftBtnText = a.getString(R.styleable.TitleView_title_btn_left_text); 94 | mLBtnStyle = a.getResourceId(R.styleable.TitleView_title_btn_left_text_appearance, -1); 95 | mLBtnDrawable = a.getDrawable(R.styleable.TitleView_title_btn_left_drawable); 96 | if(mLBtnDrawable == null) 97 | mLBtnDrawable = getResources().getDrawable(R.drawable.icon_back); 98 | } 99 | 100 | isShowRBtn = a.getBoolean(R.styleable.TitleView_is_show_right_button, true); 101 | if(isShowRBtn){ 102 | mRightBtnText = a.getString(R.styleable.TitleView_title_btn_right_text); 103 | mRBtnStyle = a.getResourceId(R.styleable.TitleView_title_btn_right_text_appearance, -1); 104 | mRBtnBgDrawable = a.getDrawable(R.styleable.TitleView_title_btn_right_drawable); 105 | } 106 | 107 | a.recycle(); 108 | if (getBackground() == null) { 109 | setBackgroundResource(android.R.color.white); 110 | } 111 | //this.setVisibility(View.GONE); 112 | } 113 | } 114 | 115 | private void setupView(Context context) { 116 | // final DisplayMetrics dm = context.getResources().getDisplayMetrics(); 117 | int titleHight; 118 | 119 | /**new sub title view*/ 120 | TextView subTitleView = new TextView(context); 121 | this.mSubTitleTextView = subTitleView; 122 | subTitleView.setVisibility(View.GONE); 123 | subTitleView.setTextColor(Color.parseColor("#444444")); 124 | subTitleView.setTextSize(13); 125 | subTitleView.setSingleLine(); 126 | subTitleView.setGravity(Gravity.CENTER); 127 | subTitleView.setEllipsize(TextUtils.TruncateAt.END); 128 | subTitleView.setClickable(false); 129 | subTitleView.setText("Test subTitleView"); 130 | 131 | /**new title view*/ 132 | TextView titleView = new TextView(context); 133 | this.mTitleTextView = titleView; 134 | titleView.setId(Title_View_Id); 135 | titleView.setGravity(Gravity.CENTER); 136 | titleView.setEllipsize(TextUtils.TruncateAt.END); 137 | titleView.setSingleLine(); 138 | titleView.setClickable(true); 139 | if(mTitleStyle != -1) 140 | titleView.setTextAppearance(context, mTitleStyle); 141 | else 142 | titleView.setTextAppearance(context, TITLE_TEXT_DEFAULT_STYLE); 143 | 144 | if (!TextUtils.isEmpty(mTitleText)){ 145 | titleView.setText(mTitleText); 146 | } 147 | 148 | RelativeLayout mRelativeLayout = new RelativeLayout(context); 149 | //mRelativeLayout.setGravity(Gravity.CENTER_HORIZONTAL); 150 | 151 | /**titleView layoutParams*/ 152 | if(View.VISIBLE == subTitleView.getVisibility()) 153 | titleHight = LayoutParams.WRAP_CONTENT; 154 | else 155 | titleHight = DEFAULT_TITLE_HEIGHT; 156 | LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, titleHight); 157 | lp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); 158 | mRelativeLayout.addView(titleView, lp); 159 | 160 | /**subTitleView layoutParams*/ 161 | lp = new LayoutParams(LayoutParams.WRAP_CONTENT, DEFAULT_TITLE_HEIGHT); 162 | lp.addRule(BELOW, Title_View_Id); 163 | lp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); 164 | mRelativeLayout.addView(subTitleView, lp); 165 | 166 | //LayoutParams parms = new LayoutParams(LayoutParams.MATCH_PARENT, DEFAULT_TITLE_HEIGHT); 167 | //parms.addRule(RelativeLayout.RIGHT_OF, Left_Btn_View_Id); 168 | //parms.addRule(RelativeLayout.LEFT_OF, Right_Btn_View_Id); 169 | //parms.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); 170 | //mRelativeLayout.setLayoutParams(parms); 171 | 172 | addView(mRelativeLayout); 173 | 174 | /**Add left button view*/ 175 | if(isShowLBtn){ 176 | if (TextUtils.isEmpty(mLeftBtnText)){ 177 | mLeftBtnImgView = new View(context); 178 | mLeftBtnImgView.setBackground(mLBtnDrawable); 179 | FrameLayout containerLayout = new FrameLayout(context); 180 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(DEFAULT_ICON_SIZE,DEFAULT_ICON_SIZE); 181 | params.gravity = Gravity.CENTER|Gravity.LEFT; 182 | containerLayout.addView(mLeftBtnImgView, params); 183 | this.mLeftBtnTextView = containerLayout; 184 | 185 | }else{ 186 | TextView leftBtnView = new TextView(context); 187 | leftBtnView.setGravity(Gravity.CENTER|Gravity.LEFT); 188 | leftBtnView.setSingleLine(true); 189 | leftBtnView.setEllipsize(TextUtils.TruncateAt.END); 190 | leftBtnView.setText(mLeftBtnText); 191 | if (mLBtnStyle != -1){ 192 | leftBtnView.setTextAppearance(context, mLBtnStyle); 193 | } else { 194 | leftBtnView.setTextAppearance(context, BTN_TEXT_DEFAULT_STYLE); 195 | } 196 | this.mLeftBtnTextView = leftBtnView; 197 | } 198 | 199 | this.mLeftBtnTextView.setId(Left_Btn_View_Id); 200 | this.mLeftBtnTextView.setClickable(true); 201 | this.mLeftBtnTextView.setOnClickListener(this); 202 | LayoutParams lps = new LayoutParams(LayoutParams.WRAP_CONTENT, DEFAULT_TITLE_HEIGHT); 203 | lps.setMargins(DeviceInforHelper.getPixelFromDip(10), 0, 0, 0); 204 | lps.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE); 205 | lps.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); 206 | addView(mLeftBtnTextView, lps); 207 | } 208 | 209 | /**Add right button view*/ 210 | if(isShowRBtn){ 211 | if(TextUtils.isEmpty(mRightBtnText)){ 212 | mRightBtnImgView = new View(context); 213 | mRightBtnImgView.setBackground(mRBtnBgDrawable); 214 | FrameLayout containerLayout = new FrameLayout(context); 215 | FrameLayout.LayoutParams subLp = new FrameLayout.LayoutParams(DEFAULT_ICON_SIZE+15, DEFAULT_ICON_SIZE); 216 | subLp.gravity = Gravity.CENTER|Gravity.RIGHT; 217 | containerLayout.addView(mRightBtnImgView, subLp); 218 | this.mRightBtnTextView = containerLayout; 219 | }else{ 220 | TextView rightBtnView = new TextView(getContext()); 221 | rightBtnView.setGravity(Gravity.CENTER|Gravity.RIGHT); 222 | rightBtnView.setSingleLine(true); 223 | rightBtnView.setEllipsize(TextUtils.TruncateAt.END); 224 | rightBtnView.setText(mRightBtnText); 225 | if (mRBtnStyle != -1){ 226 | rightBtnView.setTextAppearance(context, mRBtnStyle); 227 | } else { 228 | rightBtnView.setTextAppearance(context, BTN_TEXT_DEFAULT_STYLE); 229 | } 230 | 231 | this.mRightBtnTextView = rightBtnView; 232 | } 233 | 234 | mRightBtnTextView.setId(Right_Btn_View_Id); 235 | mRightBtnTextView.setClickable(true); 236 | mRightBtnTextView.setOnClickListener(this); 237 | LayoutParams p = new LayoutParams(LayoutParams.WRAP_CONTENT, DEFAULT_TITLE_HEIGHT); 238 | p.setMargins(0, 0, DeviceInforHelper.getPixelFromDip(10), 0); 239 | p.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE); 240 | p.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); 241 | addView(mRightBtnTextView, p); 242 | } 243 | } 244 | 245 | @Override 246 | public void onClick(View v) { 247 | switch (v.getId()){ 248 | case Title_View_Id: 249 | if (mOnTitleClickListener != null) 250 | mOnTitleClickListener.onTitleClick(v); 251 | break; 252 | case Left_Btn_View_Id: 253 | if(mOnLeftBtnClickListener !=null) 254 | mOnLeftBtnClickListener.onRBtnClick(v); 255 | else 256 | sendKeyBackEvent(); 257 | break; 258 | case Right_Btn_View_Id: 259 | if (mOnRightBtnClickListener != null) 260 | mOnRightBtnClickListener.onRBtnClick(v); 261 | break; 262 | default: 263 | break; 264 | } 265 | } 266 | 267 | // =========================================================== 268 | // Methods 269 | // =========================================================== 270 | public interface OnTitleClickListener { 271 | void onTitleClick(View v); 272 | } 273 | 274 | public interface OnRightBtnClickListener { 275 | void onRBtnClick(View v); 276 | } 277 | 278 | public interface OnLeftBtnClickListener { 279 | void onRBtnClick(View v); 280 | } 281 | 282 | public void setOnTitleClickListener(OnTitleClickListener listener) { 283 | mOnTitleClickListener = listener; 284 | } 285 | 286 | public void setOnRightBtnClickListener(OnRightBtnClickListener listener) { 287 | mOnRightBtnClickListener = listener; 288 | } 289 | 290 | public void setOnLeftBtnClickListener(OnLeftBtnClickListener listener){ 291 | mOnLeftBtnClickListener = listener; 292 | } 293 | 294 | public void setTitleText(T obj) { 295 | if(obj == null) 296 | return; 297 | if(obj instanceof Integer) 298 | mTitleTextView.setText(getResources().getString((Integer) obj)); 299 | else if(obj instanceof CharSequence) 300 | mTitleTextView.setText((CharSequence)obj); 301 | } 302 | 303 | public void setRightBtnText(T obj) { 304 | if(obj == null) 305 | return; 306 | if(obj instanceof Integer) 307 | ((TextView)mRightBtnTextView).setText(getResources().getString((Integer) obj)); 308 | else if(obj instanceof CharSequence) 309 | ((TextView)mRightBtnTextView).setText((CharSequence)obj); 310 | } 311 | 312 | public View getRBtnView(){ 313 | return mRightBtnTextView; 314 | } 315 | 316 | public View getRBtnImgView(){ 317 | return mRightBtnImgView; 318 | } 319 | 320 | public void setRightBtnDrawable(T obj){ 321 | if(obj == null || mRightBtnImgView == null) 322 | return; 323 | if(obj instanceof Integer) 324 | mRightBtnImgView.setBackgroundResource((Integer) obj); 325 | else 326 | mLeftBtnImgView.setBackground((Drawable)obj); 327 | } 328 | 329 | public void setLeftBtnText(T obj) { 330 | if(obj == null) 331 | return; 332 | if(obj instanceof Integer) 333 | ((TextView)mLeftBtnTextView).setText(getResources().getString((Integer) obj)); 334 | else if(obj instanceof CharSequence) 335 | ((TextView)mLeftBtnTextView).setText((CharSequence)obj); 336 | } 337 | 338 | public void setLeftBtnDrawable(T obj){ 339 | if(obj == null || mLeftBtnImgView == null) 340 | return; 341 | if(obj instanceof Integer) 342 | mLeftBtnImgView.setBackgroundResource((Integer) obj); 343 | else 344 | mLeftBtnImgView.setBackground((Drawable)obj); 345 | } 346 | 347 | public void setSubTitleText(T obj){ 348 | if(obj == null) 349 | return; 350 | if(obj instanceof Integer) 351 | ((TextView)mSubTitleTextView).setText(getResources().getString((Integer) obj)); 352 | else if(obj instanceof CharSequence) 353 | ((TextView)mSubTitleTextView).setText((CharSequence)obj); 354 | setSubTitleVisibility(View.VISIBLE); 355 | } 356 | 357 | public void setSubTitleVisibility(int visibility){ 358 | mSubTitleTextView.setVisibility(visibility); 359 | ViewGroup.LayoutParams p = mTitleTextView.getLayoutParams(); 360 | if(visibility == View.GONE){ 361 | p.height = DEFAULT_TITLE_HEIGHT; 362 | }else if(visibility == View.VISIBLE){ 363 | p.height = LayoutParams.WRAP_CONTENT; 364 | } 365 | } 366 | 367 | private void sendKeyBackEvent() { 368 | final Context context = getContext(); 369 | if (context instanceof CoreActivity) { 370 | KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); 371 | ((CoreActivity) context).onKeyDown(KeyEvent.KEYCODE_BACK, keyEvent); 372 | } else if (context instanceof Activity) { 373 | KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); 374 | ((Activity) context).onKeyDown(KeyEvent.KEYCODE_BACK, keyEvent); 375 | } 376 | } 377 | // =========================================================== 378 | // Inner and Anonymous Classes 379 | // =========================================================== 380 | } 381 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/utils/MediaFileUtil.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.utils; 2 | 3 | import android.content.ContentResolver; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.net.Uri; 9 | import android.provider.BaseColumns; 10 | import android.provider.MediaStore; 11 | import android.support.v4.content.CursorLoader; 12 | import android.text.TextUtils; 13 | import android.util.Log; 14 | import com.iknow.imageselect.model.AlbumInfo; 15 | import com.iknow.imageselect.model.MediaInfo; 16 | import java.io.File; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.Iterator; 20 | import java.util.LinkedList; 21 | import java.util.Map; 22 | 23 | /** 24 | * Author: J.Chou 25 | * Email: who_know_me@163.com 26 | * Created: 2016年04月06日 4:24 PM 27 | * Description: 28 | */ 29 | 30 | public class MediaFileUtil { 31 | 32 | static String[] projection = { 33 | MediaStore.Files.FileColumns._ID, 34 | MediaStore.Files.FileColumns.DATA, 35 | MediaStore.Files.FileColumns.DATE_ADDED, 36 | MediaStore.Files.FileColumns.MEDIA_TYPE, 37 | MediaStore.Files.FileColumns.MIME_TYPE, 38 | MediaStore.Files.FileColumns.TITLE 39 | }; 40 | 41 | static String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "=" 42 | + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE 43 | + " OR " 44 | + MediaStore.Files.FileColumns.MEDIA_TYPE + "=" 45 | + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO; 46 | 47 | 48 | /** 49 | * param mContext 50 | * return 51 | */ 52 | public static LinkedList getThumbnailsPhotosInfo(Context mContext) { 53 | 54 | LinkedList bitmaps = new LinkedList(); 55 | Cursor cursor = null; 56 | 57 | try { 58 | cursor = mContext.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, 59 | null, MediaStore.Images.Media.DEFAULT_SORT_ORDER); 60 | if (cursor == null) { 61 | return bitmaps; 62 | } 63 | } catch (Exception err) { 64 | if (cursor != null) 65 | cursor.close(); 66 | return bitmaps; 67 | } 68 | 69 | AlbumInfo albumInfo = null; 70 | 71 | HashMap> albums = getAlbumsInfo(mContext, cursor); 72 | cursor.close(); 73 | 74 | String key = null; 75 | for (Iterator it = albums.entrySet().iterator(); it.hasNext();) { 76 | Map.Entry e = (Map.Entry) it.next(); 77 | LinkedList album = (LinkedList) e.getValue(); 78 | 79 | if (album != null && album.size() > 0) { 80 | albumInfo = new AlbumInfo(); 81 | key = (String) e.getKey(); 82 | albumInfo.name = key.substring(key.lastIndexOf("/") + 1); 83 | 84 | albumInfo.fileId = album.get(0).fileId; 85 | albumInfo.filePath = album.get(0).filePath; 86 | ArrayList list = new ArrayList(); 87 | for (int i = album.size() - 1; i >= 0; i--) { 88 | list.add(album.get(i)); 89 | } 90 | albumInfo.medias = list; 91 | 92 | if (albumInfo.filePath.endsWith("DCIM/Camera")) { 93 | albumInfo.name = "相册"; 94 | bitmaps.addFirst(albumInfo); 95 | } else { 96 | bitmaps.addLast(albumInfo); 97 | } 98 | 99 | } 100 | } 101 | 102 | return bitmaps; 103 | } 104 | 105 | public static HashMap> getAlbumsInfo(Context mContext,Cursor cursor) { 106 | HashMap> albumsInfos = new HashMap>(); 107 | String _path = MediaStore.Images.Media.DATA; 108 | String _album = MediaStore.Images.Media.DEFAULT_SORT_ORDER; 109 | String _time = MediaStore.Images.Media.DATE_ADDED; 110 | String _rotate = MediaStore.Images.Media.ORIENTATION; 111 | 112 | HashMap thumbInfos = getThumbImgInfo(mContext); 113 | 114 | MediaInfo imageInfo = null; 115 | File file = null; 116 | try { 117 | 118 | if (cursor.moveToFirst()) { 119 | do { 120 | int _id = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID)); 121 | String path = cursor.getString(cursor.getColumnIndex(_path)); 122 | String album = cursor.getString(cursor.getColumnIndex(_album)); 123 | String time = cursor.getString(cursor.getColumnIndex(_time)); 124 | int rotate = cursor.getInt(cursor.getColumnIndex(_rotate)); 125 | 126 | file = new File(path); 127 | if (!file.exists() || file.length() == 0) { 128 | continue; 129 | } 130 | 131 | String subPath = path.substring(0, path.lastIndexOf("/")); 132 | 133 | if (albumsInfos.containsKey(getAlbumKey(subPath, album))) { 134 | LinkedList albums = albumsInfos 135 | .remove(getAlbumKey(subPath, album)); 136 | imageInfo = new MediaInfo(); 137 | imageInfo.fileId = _id; 138 | imageInfo.filePath = subPath; 139 | imageInfo.fileName = path; 140 | imageInfo.createTime = time; 141 | imageInfo.rotate = rotate; 142 | if(thumbInfos.containsKey(_id)){ 143 | imageInfo.thumbPath = thumbInfos.get(_id); 144 | } 145 | albums.add(imageInfo); 146 | albumsInfos.put(getAlbumKey(subPath, album), albums); 147 | } else { 148 | LinkedList albums = new LinkedList(); 149 | imageInfo = new MediaInfo(); 150 | imageInfo.fileId = _id; 151 | imageInfo.filePath = subPath; 152 | imageInfo.fileName = path; 153 | imageInfo.createTime = time; 154 | imageInfo.rotate = rotate; 155 | if(thumbInfos.containsKey(_id)){ 156 | imageInfo.thumbPath = thumbInfos.get(_id); 157 | } 158 | albums.add(imageInfo); 159 | albumsInfos.put(getAlbumKey(subPath, album), albums); 160 | } 161 | } while (cursor.moveToNext()); 162 | } 163 | }catch(Exception e){ 164 | e.printStackTrace(); 165 | } 166 | 167 | return albumsInfos; 168 | } 169 | 170 | private static String getAlbumKey(String path, String name) { 171 | return path + "/" + name; 172 | } 173 | 174 | public static int getPicId(String pathAll) { 175 | String strId = pathAll.split("&")[0]; 176 | int id = -1; 177 | try { 178 | id = Integer.valueOf(strId); 179 | } catch (NumberFormatException e) { 180 | e.printStackTrace(); 181 | } 182 | return id; 183 | } 184 | 185 | public static Bitmap getThumbImage(Context mContext, int id) { 186 | return MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), id, 187 | MediaStore.Images.Thumbnails.MICRO_KIND, new BitmapFactory.Options()); 188 | 189 | } 190 | 191 | public static String getFileName(String url) { 192 | if (TextUtils.isEmpty(url)) { 193 | return null; 194 | } 195 | 196 | return url.substring(url.lastIndexOf("/") + 1); 197 | } 198 | 199 | public static Bitmap getThumbImage(Context mContext, int id, BitmapFactory.Options options) { 200 | return MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), id, 201 | MediaStore.Images.Thumbnails.MICRO_KIND, options); 202 | } 203 | 204 | private static HashMap getThumbImgInfo(Context mContext){ 205 | HashMap thumbMap = new HashMap(); 206 | 207 | if(mContext == null){ 208 | return thumbMap; 209 | } 210 | 211 | String[] projection = { MediaStore.Images.Thumbnails._ID, MediaStore.Images.Thumbnails.IMAGE_ID, 212 | MediaStore.Images.Thumbnails.DATA }; 213 | Cursor cursor = null; 214 | try { 215 | cursor = mContext.getContentResolver().query( 216 | MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, projection, null, null, null); 217 | if (cursor.moveToFirst()) { 218 | int image_id; 219 | String image_path; 220 | int image_idColumn = cursor.getColumnIndex(MediaStore.Images.Thumbnails.IMAGE_ID); 221 | int dataColumn = cursor.getColumnIndex(MediaStore.Images.Thumbnails.DATA); 222 | do { 223 | // Get the field values 224 | image_id = cursor.getInt(image_idColumn); 225 | image_path = cursor.getString(dataColumn); 226 | 227 | if(!new File(image_path).exists()){ 228 | continue; 229 | } 230 | 231 | if(!TextUtils.isEmpty(image_path)){ 232 | thumbMap.put(image_id, image_path); 233 | } 234 | } while (cursor.moveToNext()); 235 | } 236 | } catch (Exception e) { 237 | e.printStackTrace(); 238 | }finally{ 239 | if(cursor != null){ 240 | cursor.close(); 241 | } 242 | } 243 | 244 | return thumbMap; 245 | } 246 | 247 | /** 248 | * 获取缩略图路径通过图片的id 249 | * param mContext 250 | * param id 251 | * return 252 | */ 253 | public static String getThumbPathById(Context mContext, int id) { 254 | 255 | if(mContext == null){ 256 | return null; 257 | } 258 | 259 | String[] projection = { MediaStore.Images.Thumbnails._ID, MediaStore.Images.Thumbnails.IMAGE_ID, 260 | MediaStore.Images.Thumbnails.DATA }; 261 | 262 | String whereClause = MediaStore.Images.Thumbnails.IMAGE_ID + " = '"+ id + "'"; 263 | 264 | Cursor cursor = mContext.getContentResolver().query( 265 | MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, projection, whereClause, null, null); 266 | if (cursor == null || cursor.getCount() == 0) { 267 | if (cursor != null) 268 | cursor.close(); 269 | return null; 270 | } 271 | 272 | String thumbPath = ""; 273 | try { 274 | if (cursor.moveToFirst()) { 275 | int dataColumn = cursor.getColumnIndex(MediaStore.Images.Thumbnails.DATA); 276 | thumbPath = cursor.getString(dataColumn); 277 | } 278 | } catch (Exception e) { 279 | e.printStackTrace(); 280 | }finally{ 281 | if(cursor != null){ 282 | cursor.close(); 283 | } 284 | } 285 | 286 | return thumbPath; 287 | } 288 | 289 | public static Bitmap getThumbBitmap(Context mContext, String fileName) { 290 | Bitmap bitmap = null; 291 | BitmapFactory.Options options = new BitmapFactory.Options(); 292 | options.inDither = false; 293 | options.inPreferredConfig = Bitmap.Config.ARGB_8888; 294 | // select condition. 295 | String whereClause = MediaStore.Images.Media.DISPLAY_NAME + " = '" 296 | + fileName + "'"; 297 | 298 | // colection of results. 299 | Cursor cursor = mContext.getContentResolver().query( 300 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 301 | new String[] { MediaStore.Images.Media._ID }, whereClause, 302 | null, null); 303 | if (cursor == null || cursor.getCount() == 0) { 304 | if (cursor != null) 305 | cursor.close(); 306 | return null; 307 | } 308 | cursor.moveToFirst(); 309 | // image id in image table. 310 | String videoId = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID)); 311 | cursor.close(); 312 | if (videoId == null) { 313 | return null; 314 | } 315 | long videoIdLong = Long.parseLong(videoId); 316 | // via imageid get the bimap type thumbnail in thumbnail table. 317 | bitmap = MediaStore.Images.Thumbnails.getThumbnail( 318 | mContext.getContentResolver(), videoIdLong, 319 | MediaStore.Images.Thumbnails.MINI_KIND, options); 320 | return bitmap; 321 | } 322 | 323 | /*** 324 | * 获取本机相册所有的图片 325 | * param mContext 326 | */ 327 | public static ArrayList getAllImageFiles(Context mContext){ 328 | MediaInfo info; 329 | ArrayList _images = new ArrayList<>(); 330 | try { 331 | String sortOrder = MediaStore.Images.Media.DATE_MODIFIED + " desc"; 332 | Cursor cursor = mContext.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,null, null, null, sortOrder); 333 | while (cursor.moveToNext()) { 334 | info = new MediaInfo(); 335 | info.fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); 336 | info.name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)); 337 | info.createTime = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED)); 338 | info.fileId = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID)); 339 | info.lat = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.LATITUDE)); 340 | info.lon = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.LONGITUDE)); 341 | if(!TextUtils.isEmpty(info.lat)){ 342 | Log.e("jason","info.lat ="+info.lat); 343 | } 344 | if(!TextUtils.isEmpty(info.lon)){ 345 | Log.e("jason","info.lon ="+info.lon); 346 | } 347 | _images.add(info); 348 | } 349 | cursor.close(); 350 | }catch (Exception e){ 351 | e.printStackTrace(); 352 | } 353 | 354 | return _images; 355 | 356 | } 357 | 358 | public static ArrayList getAllVideoFiles(Context mContext){ 359 | MediaInfo mediaInfo; 360 | ArrayList videos = new ArrayList<>(); 361 | ContentResolver contentResolver = mContext.getContentResolver(); 362 | try { 363 | Cursor cursor = contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, 364 | null, null, MediaStore.Video.Media.DEFAULT_SORT_ORDER); 365 | while (cursor.moveToNext()) { 366 | mediaInfo = new MediaInfo(); 367 | mediaInfo.fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA)); 368 | mediaInfo.createTime = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATE_ADDED)); 369 | mediaInfo.name = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME)); 370 | mediaInfo.mediaType = 3; 371 | videos.add(mediaInfo); 372 | } 373 | 374 | cursor.close(); 375 | } catch (Exception e) { 376 | e.printStackTrace(); 377 | } 378 | return videos; 379 | } 380 | 381 | public static ArrayList getAllMediaFiles(Context mContext){ 382 | MediaInfo mediaInfo; 383 | ArrayList allMediasList = new ArrayList<>(); 384 | 385 | Uri queryUri = MediaStore.Files.getContentUri("external"); 386 | 387 | try { 388 | CursorLoader cursorLoader = new CursorLoader( 389 | mContext, 390 | queryUri, 391 | projection, 392 | selection, 393 | null, // Selection args (none). 394 | MediaStore.Files.FileColumns.DATE_ADDED + " DESC" // Sort order. 395 | ); 396 | 397 | Cursor cursor = cursorLoader.loadInBackground(); 398 | 399 | while (cursor.moveToNext()) { 400 | mediaInfo = new MediaInfo(); 401 | mediaInfo.fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)); 402 | mediaInfo.createTime = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATE_ADDED)); 403 | mediaInfo.name = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.TITLE)); 404 | mediaInfo.mediaType = cursor.getInt(cursor.getColumnIndex(MediaStore.Files.FileColumns.MEDIA_TYPE)); 405 | allMediasList.add(mediaInfo); 406 | } 407 | cursor.close(); 408 | }catch (Exception e){ 409 | e.printStackTrace(); 410 | } 411 | 412 | return allMediasList; 413 | } 414 | 415 | } 416 | -------------------------------------------------------------------------------- /image-selector/src/main/java/com/iknow/imageselect/photodraweeview/Attacher.java: -------------------------------------------------------------------------------- 1 | package com.iknow.imageselect.photodraweeview; 2 | 3 | import android.content.Context; 4 | import android.graphics.Matrix; 5 | import android.graphics.RectF; 6 | import android.os.Build; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.view.GestureDetectorCompat; 9 | import android.support.v4.view.MotionEventCompat; 10 | import android.support.v4.widget.ScrollerCompat; 11 | import android.view.GestureDetector; 12 | import android.view.MotionEvent; 13 | import android.view.View; 14 | import android.view.ViewParent; 15 | import android.view.animation.AccelerateDecelerateInterpolator; 16 | import android.view.animation.Interpolator; 17 | 18 | import com.facebook.drawee.drawable.ScalingUtils; 19 | import com.facebook.drawee.generic.GenericDraweeHierarchy; 20 | import com.facebook.drawee.view.DraweeView; 21 | 22 | import java.lang.ref.WeakReference; 23 | 24 | public class Attacher implements IAttacher, View.OnTouchListener, OnScaleDragGestureListener { 25 | 26 | private static final int EDGE_NONE = -1; 27 | private static final int EDGE_LEFT = 0; 28 | private static final int EDGE_RIGHT = 1; 29 | private static final int EDGE_BOTH = 2; 30 | 31 | private final float[] mMatrixValues = new float[9]; 32 | private final RectF mDisplayRect = new RectF(); 33 | private final Interpolator mZoomInterpolator = new AccelerateDecelerateInterpolator(); 34 | 35 | private float mMinScale = IAttacher.DEFAULT_MIN_SCALE; 36 | private float mMidScale = IAttacher.DEFAULT_MID_SCALE; 37 | private float mMaxScale = IAttacher.DEFAULT_MAX_SCALE; 38 | private long mZoomDuration = IAttacher.ZOOM_DURATION; 39 | 40 | private ScaleDragDetector mScaleDragDetector; 41 | private GestureDetectorCompat mGestureDetector; 42 | 43 | private boolean mBlockParentIntercept = false; 44 | private boolean mAllowParentInterceptOnEdge = true; 45 | private int mScrollEdge = EDGE_BOTH; 46 | 47 | private final Matrix mMatrix = new Matrix(); 48 | private int mImageInfoHeight = -1, mImageInfoWidth = -1; 49 | private FlingRunnable mCurrentFlingRunnable; 50 | private WeakReference> mDraweeView; 51 | 52 | private OnPhotoTapListener mPhotoTapListener; 53 | private OnViewTapListener mViewTapListener; 54 | private View.OnLongClickListener mLongClickListener; 55 | private OnScaleChangeListener mScaleChangeListener; 56 | 57 | public Attacher(DraweeView draweeView) { 58 | mDraweeView = new WeakReference<>(draweeView); 59 | draweeView.getHierarchy().setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER); 60 | draweeView.setOnTouchListener(this); 61 | mScaleDragDetector = new ScaleDragDetector(draweeView.getContext(), this); 62 | mGestureDetector = new GestureDetectorCompat(draweeView.getContext(), 63 | new GestureDetector.SimpleOnGestureListener() { 64 | @Override public void onLongPress(MotionEvent e) { 65 | super.onLongPress(e); 66 | if (null != mLongClickListener) { 67 | mLongClickListener.onLongClick(getDraweeView()); 68 | } 69 | } 70 | }); 71 | mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this)); 72 | } 73 | 74 | public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) { 75 | if (newOnDoubleTapListener != null) { 76 | this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener); 77 | } else { 78 | this.mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this)); 79 | } 80 | } 81 | 82 | @Nullable public DraweeView getDraweeView() { 83 | return mDraweeView.get(); 84 | } 85 | 86 | @Override public float getMinimumScale() { 87 | return mMinScale; 88 | } 89 | 90 | @Override 91 | 92 | public float getMediumScale() { 93 | return mMidScale; 94 | } 95 | 96 | @Override 97 | 98 | public float getMaximumScale() { 99 | return mMaxScale; 100 | } 101 | 102 | @Override public void setMaximumScale(float maximumScale) { 103 | checkZoomLevels(mMinScale, mMidScale, maximumScale); 104 | mMaxScale = maximumScale; 105 | } 106 | 107 | @Override public void setMediumScale(float mediumScale) { 108 | checkZoomLevels(mMinScale, mediumScale, mMaxScale); 109 | mMidScale = mediumScale; 110 | } 111 | 112 | @Override public void setMinimumScale(float minimumScale) { 113 | checkZoomLevels(minimumScale, mMidScale, mMaxScale); 114 | mMinScale = minimumScale; 115 | } 116 | 117 | @Override public float getScale() { 118 | return (float) Math.sqrt( 119 | (float) Math.pow(getMatrixValue(mMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow( 120 | getMatrixValue(mMatrix, Matrix.MSKEW_Y), 2)); 121 | } 122 | 123 | @Override public void setScale(float scale) { 124 | setScale(scale, false); 125 | } 126 | 127 | @Override public void setScale(float scale, boolean animate) { 128 | DraweeView draweeView = getDraweeView(); 129 | if (draweeView != null) { 130 | setScale(scale, (draweeView.getRight()) / 2, (draweeView.getBottom()) / 2, false); 131 | } 132 | } 133 | 134 | @Override public void setScale(float scale, float focalX, float focalY, boolean animate) { 135 | DraweeView draweeView = getDraweeView(); 136 | 137 | if (draweeView == null || scale < mMinScale || scale > mMaxScale) { 138 | return; 139 | } 140 | 141 | if (animate) { 142 | draweeView.post(new AnimatedZoomRunnable(getScale(), scale, focalX, focalY)); 143 | } else { 144 | mMatrix.setScale(scale, scale, focalX, focalY); 145 | checkMatrixAndInvalidate(); 146 | } 147 | } 148 | 149 | @Override public void setZoomTransitionDuration(long duration) { 150 | duration = duration < 0 ? IAttacher.ZOOM_DURATION : duration; 151 | mZoomDuration = duration; 152 | } 153 | 154 | @Override public void setAllowParentInterceptOnEdge(boolean allow) { 155 | mAllowParentInterceptOnEdge = allow; 156 | } 157 | 158 | @Override public void setOnScaleChangeListener(OnScaleChangeListener listener) { 159 | mScaleChangeListener = listener; 160 | } 161 | 162 | @Override public void setOnLongClickListener(View.OnLongClickListener listener) { 163 | mLongClickListener = listener; 164 | } 165 | 166 | @Override public void setOnPhotoTapListener(OnPhotoTapListener listener) { 167 | mPhotoTapListener = listener; 168 | } 169 | 170 | @Override public void setOnViewTapListener(OnViewTapListener listener) { 171 | mViewTapListener = listener; 172 | } 173 | 174 | @Override public OnPhotoTapListener getOnPhotoTapListener() { 175 | return mPhotoTapListener; 176 | } 177 | 178 | @Override public OnViewTapListener getOnViewTapListener() { 179 | return mViewTapListener; 180 | } 181 | 182 | @Override public void update(int imageInfoWidth, int imageInfoHeight) { 183 | mImageInfoWidth = imageInfoWidth; 184 | mImageInfoHeight = imageInfoHeight; 185 | updateBaseMatrix(); 186 | } 187 | 188 | private static void checkZoomLevels(float minZoom, float midZoom, float maxZoom) { 189 | if (minZoom >= midZoom) { 190 | throw new IllegalArgumentException("MinZoom has to be less than MidZoom"); 191 | } else if (midZoom >= maxZoom) { 192 | throw new IllegalArgumentException("MidZoom has to be less than MaxZoom"); 193 | } 194 | } 195 | 196 | private int getViewWidth() { 197 | 198 | DraweeView draweeView = getDraweeView(); 199 | 200 | if (draweeView != null) { 201 | 202 | return draweeView.getWidth() 203 | - draweeView.getPaddingLeft() 204 | - draweeView.getPaddingRight(); 205 | } 206 | 207 | return 0; 208 | } 209 | 210 | private int getViewHeight() { 211 | DraweeView draweeView = getDraweeView(); 212 | if (draweeView != null) { 213 | return draweeView.getHeight() 214 | - draweeView.getPaddingTop() 215 | - draweeView.getPaddingBottom(); 216 | } 217 | return 0; 218 | } 219 | 220 | private float getMatrixValue(Matrix matrix, int whichValue) { 221 | matrix.getValues(mMatrixValues); 222 | return mMatrixValues[whichValue]; 223 | } 224 | 225 | public Matrix getDrawMatrix() { 226 | return mMatrix; 227 | } 228 | 229 | public RectF getDisplayRect() { 230 | checkMatrixBounds(); 231 | return getDisplayRect(getDrawMatrix()); 232 | } 233 | 234 | public void checkMatrixAndInvalidate() { 235 | 236 | DraweeView draweeView = getDraweeView(); 237 | 238 | if (draweeView == null) { 239 | return; 240 | } 241 | 242 | if (checkMatrixBounds()) { 243 | draweeView.invalidate(); 244 | } 245 | } 246 | 247 | public boolean checkMatrixBounds() { 248 | RectF rect = getDisplayRect(getDrawMatrix()); 249 | if (rect == null) { 250 | return false; 251 | } 252 | 253 | float height = rect.height(); 254 | float width = rect.width(); 255 | float deltaX = 0.0F; 256 | float deltaY = 0.0F; 257 | int viewHeight = getViewHeight(); 258 | 259 | if (height <= (float) viewHeight) { 260 | deltaY = (viewHeight - height) / 2 - rect.top; 261 | } else if (rect.top > 0.0F) { 262 | deltaY = -rect.top; 263 | } else if (rect.bottom < (float) viewHeight) { 264 | deltaY = viewHeight - rect.bottom; 265 | } 266 | int viewWidth = getViewWidth(); 267 | if (width <= viewWidth) { 268 | deltaX = (viewWidth - width) / 2 - rect.left; 269 | mScrollEdge = EDGE_BOTH; 270 | } else if (rect.left > 0) { 271 | deltaX = -rect.left; 272 | mScrollEdge = EDGE_LEFT; 273 | } else if (rect.right < viewWidth) { 274 | deltaX = viewWidth - rect.right; 275 | mScrollEdge = EDGE_RIGHT; 276 | } else { 277 | mScrollEdge = EDGE_NONE; 278 | } 279 | 280 | mMatrix.postTranslate(deltaX, deltaY); 281 | return true; 282 | } 283 | 284 | private RectF getDisplayRect(Matrix matrix) { 285 | DraweeView draweeView = getDraweeView(); 286 | if (draweeView == null || (mImageInfoWidth == -1 && mImageInfoHeight == -1)) { 287 | return null; 288 | } 289 | mDisplayRect.set(0.0F, 0.0F, mImageInfoWidth, mImageInfoHeight); 290 | draweeView.getHierarchy().getActualImageBounds(mDisplayRect); 291 | matrix.mapRect(mDisplayRect); 292 | return mDisplayRect; 293 | } 294 | 295 | private void updateBaseMatrix() { 296 | if (mImageInfoWidth == -1 && mImageInfoHeight == -1) { 297 | return; 298 | } 299 | resetMatrix(); 300 | } 301 | 302 | private void resetMatrix() { 303 | mMatrix.reset(); 304 | checkMatrixBounds(); 305 | DraweeView draweeView = getDraweeView(); 306 | if (draweeView != null) { 307 | draweeView.invalidate(); 308 | } 309 | } 310 | 311 | private void checkMinScale() { 312 | DraweeView draweeView = getDraweeView(); 313 | if (draweeView == null) { 314 | return; 315 | } 316 | 317 | if (getScale() < mMinScale) { 318 | RectF rect = getDisplayRect(); 319 | if (null != rect) { 320 | draweeView.post(new AnimatedZoomRunnable(getScale(), mMinScale, rect.centerX(), 321 | rect.centerY())); 322 | } 323 | } 324 | } 325 | 326 | @Override public void onScale(float scaleFactor, float focusX, float focusY) { 327 | if (getScale() < mMaxScale || scaleFactor < 1.0F) { 328 | 329 | if (mScaleChangeListener != null) { 330 | mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY); 331 | } 332 | 333 | mMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY); 334 | checkMatrixAndInvalidate(); 335 | } 336 | } 337 | 338 | @Override public void onScaleEnd() { 339 | checkMinScale(); 340 | } 341 | 342 | @Override public void onDrag(float dx, float dy) { 343 | 344 | DraweeView draweeView = getDraweeView(); 345 | 346 | if (draweeView != null && !mScaleDragDetector.isScaling()) { 347 | mMatrix.postTranslate(dx, dy); 348 | checkMatrixAndInvalidate(); 349 | 350 | ViewParent parent = draweeView.getParent(); 351 | if (parent == null) { 352 | return; 353 | } 354 | 355 | if (mAllowParentInterceptOnEdge 356 | && !mScaleDragDetector.isScaling() 357 | && !mBlockParentIntercept) { 358 | if (mScrollEdge == EDGE_BOTH || (mScrollEdge == EDGE_LEFT && dx >= 1f) || ( 359 | mScrollEdge == EDGE_RIGHT 360 | && dx <= -1f)) { 361 | parent.requestDisallowInterceptTouchEvent(false); 362 | } 363 | } else { 364 | parent.requestDisallowInterceptTouchEvent(true); 365 | } 366 | } 367 | } 368 | 369 | @Override public void onFling(float startX, float startY, float velocityX, float velocityY) { 370 | DraweeView draweeView = getDraweeView(); 371 | if (draweeView == null) { 372 | return; 373 | } 374 | 375 | mCurrentFlingRunnable = new FlingRunnable(draweeView.getContext()); 376 | mCurrentFlingRunnable.fling(getViewWidth(), getViewHeight(), (int) velocityX, 377 | (int) velocityY); 378 | draweeView.post(mCurrentFlingRunnable); 379 | } 380 | 381 | @Override public boolean onTouch(View v, MotionEvent event) { 382 | int action = MotionEventCompat.getActionMasked(event); 383 | switch (action) { 384 | case MotionEvent.ACTION_DOWN: { 385 | ViewParent parent = v.getParent(); 386 | if (parent != null) { 387 | parent.requestDisallowInterceptTouchEvent(true); 388 | } 389 | cancelFling(); 390 | } 391 | break; 392 | case MotionEvent.ACTION_UP: 393 | case MotionEvent.ACTION_CANCEL: { 394 | ViewParent parent = v.getParent(); 395 | if (parent != null) { 396 | parent.requestDisallowInterceptTouchEvent(false); 397 | } 398 | } 399 | break; 400 | } 401 | 402 | boolean wasScaling = mScaleDragDetector.isScaling(); 403 | boolean wasDragging = mScaleDragDetector.isDragging(); 404 | 405 | boolean handled = mScaleDragDetector.onTouchEvent(event); 406 | 407 | boolean noScale = !wasScaling && !mScaleDragDetector.isScaling(); 408 | boolean noDrag = !wasDragging && !mScaleDragDetector.isDragging(); 409 | mBlockParentIntercept = noScale && noDrag; 410 | 411 | if (mGestureDetector.onTouchEvent(event)) { 412 | handled = true; 413 | } 414 | 415 | return handled; 416 | } 417 | 418 | private class AnimatedZoomRunnable implements Runnable { 419 | private final float mFocalX, mFocalY; 420 | private final long mStartTime; 421 | private final float mZoomStart, mZoomEnd; 422 | 423 | public AnimatedZoomRunnable(final float currentZoom, final float targetZoom, 424 | final float focalX, final float focalY) { 425 | mFocalX = focalX; 426 | mFocalY = focalY; 427 | mStartTime = System.currentTimeMillis(); 428 | mZoomStart = currentZoom; 429 | mZoomEnd = targetZoom; 430 | } 431 | 432 | @Override public void run() { 433 | 434 | DraweeView draweeView = getDraweeView(); 435 | if (draweeView == null) { 436 | return; 437 | } 438 | 439 | float t = interpolate(); 440 | float scale = mZoomStart + t * (mZoomEnd - mZoomStart); 441 | float deltaScale = scale / getScale(); 442 | 443 | onScale(deltaScale, mFocalX, mFocalY); 444 | 445 | if (t < 1f) { 446 | postOnAnimation(draweeView, this); 447 | } 448 | } 449 | 450 | private float interpolate() { 451 | float t = 1f * (System.currentTimeMillis() - mStartTime) / mZoomDuration; 452 | t = Math.min(1f, t); 453 | t = mZoomInterpolator.getInterpolation(t); 454 | return t; 455 | } 456 | } 457 | 458 | private class FlingRunnable implements Runnable { 459 | 460 | private final ScrollerCompat mScroller; 461 | private int mCurrentX, mCurrentY; 462 | 463 | public FlingRunnable(Context context) { 464 | mScroller = ScrollerCompat.create(context); 465 | } 466 | 467 | public void cancelFling() { 468 | mScroller.abortAnimation(); 469 | } 470 | 471 | public void fling(int viewWidth, int viewHeight, int velocityX, int velocityY) { 472 | final RectF rect = getDisplayRect(); 473 | if (null == rect) { 474 | return; 475 | } 476 | 477 | final int startX = Math.round(-rect.left); 478 | final int minX, maxX, minY, maxY; 479 | 480 | if (viewWidth < rect.width()) { 481 | minX = 0; 482 | maxX = Math.round(rect.width() - viewWidth); 483 | } else { 484 | minX = maxX = startX; 485 | } 486 | 487 | final int startY = Math.round(-rect.top); 488 | if (viewHeight < rect.height()) { 489 | minY = 0; 490 | maxY = Math.round(rect.height() - viewHeight); 491 | } else { 492 | minY = maxY = startY; 493 | } 494 | 495 | mCurrentX = startX; 496 | mCurrentY = startY; 497 | 498 | if (startX != maxX || startY != maxY) { 499 | mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0); 500 | } 501 | } 502 | 503 | @Override public void run() { 504 | if (mScroller.isFinished()) { 505 | return; 506 | } 507 | 508 | DraweeView draweeView = getDraweeView(); 509 | 510 | if (draweeView != null && mScroller.computeScrollOffset()) { 511 | final int newX = mScroller.getCurrX(); 512 | final int newY = mScroller.getCurrY(); 513 | mMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY); 514 | draweeView.invalidate(); 515 | mCurrentX = newX; 516 | mCurrentY = newY; 517 | postOnAnimation(draweeView, this); 518 | } 519 | } 520 | } 521 | 522 | private void cancelFling() { 523 | if (mCurrentFlingRunnable != null) { 524 | mCurrentFlingRunnable.cancelFling(); 525 | mCurrentFlingRunnable = null; 526 | } 527 | } 528 | 529 | private void postOnAnimation(View view, Runnable runnable) { 530 | if (Build.VERSION.SDK_INT >= 16) { 531 | view.postOnAnimation(runnable); 532 | } else { 533 | view.postDelayed(runnable, 16L); 534 | } 535 | } 536 | 537 | protected void onDetachedFromWindow() { 538 | cancelFling(); 539 | } 540 | } 541 | --------------------------------------------------------------------------------