├── .idea └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── wgd │ │ └── myimagelibrary │ │ ├── App.java │ │ ├── BagImageActivity.java │ │ ├── BitmapFromUriUtil.java │ │ ├── ImageGetInfoUtil.java │ │ ├── ImageInfoBean.java │ │ ├── MainActivity.java │ │ └── PathFromUriUtil.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_image_bag.xml │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── file_paths_public.xml ├── build.gradle ├── gdcplibrary ├── build.gradle └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wgd │ │ └── gdcp │ │ └── gdcplibrary │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── wgd │ │ │ └── gdcp │ │ │ └── gdcplibrary │ │ │ ├── GDBitmapUtil.java │ │ │ ├── GDCompress.java │ │ │ ├── GDCompressA.java │ │ │ ├── GDCompressC.java │ │ │ ├── GDCompressImageListener.java │ │ │ ├── GDCompressImageListenerA.java │ │ │ ├── GDCompressImageS.java │ │ │ ├── GDCompressImageSListener.java │ │ │ ├── GDCompressUtil.java │ │ │ ├── GDConfig.java │ │ │ ├── GDImageBean.java │ │ │ ├── GDTools.java │ │ │ ├── ImageUtils.java │ │ │ └── thread │ │ │ └── ThreadManager.java │ ├── jniLibs │ │ ├── arm64-v8a │ │ │ ├── README.md │ │ │ └── libgdimage.so │ │ ├── armeabi-v7a │ │ │ ├── README.md │ │ │ └── libgdimage.so │ │ └── armeabi │ │ │ ├── README.md │ │ │ └── libgdimage.so │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── wgd │ └── gdcp │ └── gdcplibrary │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── settings.gradle /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImageCompress 2 | ImageCompress是基于libjpeg-turbo来进行图片的压缩,它的优点在于相较于libjpeg来说压缩时间大大的缩短了(4.5M的图片压缩完耗时约1.2秒),而相较于Android系统的图片压缩来说ImageCompress在图片处理的过程使用了哈弗曼表,这大大的提高了相同体积下图片的清晰度。 3 | 4 | 查看使用与效果可以去这里:https://blog.csdn.net/hh7181521/article/details/81014839 5 | 6 | 7 | 特别声明:终于有时间来处理这个了,这次优化了OOM问题,总体上讲提供了两种方式(1、耗时短但OOM几率大;2、耗时长但OOM几率很小) 8 | 9 | 10 | 一:添加依赖 11 | 1、 对于Android Studio的用户,可以选择添加: 12 | 13 | compile 'com.wgd.gdcp.gdcplibrary:ImageCompress:1.0.10' 14 | 或 15 | implementation 'com.wgd.gdcp.gdcplibrary:ImageCompress:1.0.10' 16 | 如果报错的话可以在项目的build.gradle中加入 17 | 18 | allprojects { 19 | repositories { 20 | maven { url "https://dl.bintray.com/wangruijun/maven" } 21 | jcenter() 22 | } 23 | } 24 | 25 | 二:使用图片压缩 26 | (1)、直接用lib库压缩,不会调试旋转角度、图片分辨率大小等 27 | 28 | 强调:这里runOnUiThread 方法可以不用,但要注意的是:虽然jar包中处理了,但不保证一定是在主线程中回调。 29 | 30 | 31 | // path 为图片地址,savePath是图片要保存的地址 32 | new GDCompress(MainActivity.this, path, savePath, new GDCompressImageListener() { 33 | @Override 34 | public void OnSuccess(String path) { 35 | MainActivity.this.runOnUiThread(new Runnable() { 36 | @Override 37 | public void run() { 38 | 39 | } 40 | }); 41 | } 42 | 43 | @Override 44 | public void OnError(int code, String errorMsg) { 45 | 46 | } 47 | }); 48 | 49 | (2)、单个图片压缩,多线程模式 50 | new GDCompressC(MainActivity.this, 51 | new GDConfig().setmPath(tempCompressImgPath), new GDCompressImageListener() { 52 | @Override 53 | public void OnSuccess(String path) { 54 | MainActivity.this.runOnUiThread(new Runnable() { 55 | @Override 56 | public void run() { 57 | 58 | } 59 | }); 60 | } 61 | 62 | @Override 63 | public void OnError(int code, String errorMsg) { 64 | 65 | } 66 | }); 67 | 68 | (3)、单个图片压缩,单线程模式 69 | String tempCompressImgPath = mSelectedPath.get(0);// 70 | GDImageBean imageBeana = new GDImageBean(); 71 | imageBeana.setmGDConfig(new GDConfig().setmPath(tempCompressImgPath)); 72 | new GDCompressA(MainActivity.this, imageBeana 73 | , new GDCompressImageListenerA() { 74 | GDCompressImageListener() { 75 | @Override 76 | public void OnSuccess(GDImageBean gdImageBean) { 77 | MainActivity.this.runOnUiThread(new Runnable() { 78 | @Override 79 | public void run() { 80 | 81 | } 82 | }); 83 | } 84 | 85 | @Override 86 | public void OnError(GDImageBean gdImageBean) { 87 | 88 | } 89 | }); 90 | 91 | (4)、批量压缩,多线程模式,速度快,但OOM几率大 92 | GDCompressC gdCompressC = new GDCompressC(MainActivity.this, new GDCompressImageListener() { 93 | @Override 94 | public void OnSuccess(String path) { 95 | Log.i(TAG, "OnSuccess: ===批量=====图片压缩=====btn_img_c2========path==" + path); 96 | } 97 | 98 | @Override 99 | public void OnError(int code, String errorMsg) { 100 | Log.i(TAG, "OnError: ===批量=====图片压缩=====btn_img_c2=========="); 101 | } 102 | }); 103 | for (int i = 0; i < mSelected.size(); i++) { 104 | gdCompressC.start(new GDConfig().setmPath(mSelectedPath.get(i))); 105 | } 106 | 107 | (5)、批量压缩,单线程模式,速度慢,但OOM几率小很多 108 | GDCompressA gdCompressA = new GDCompressA(MainActivity.this, new GDCompressImageListenerA() { 109 | @Override 110 | public void OnSuccess(GDImageBean gdImageBean) { 111 | Log.i(TAG, "OnSuccess: ===批量=====图片压缩=====btn_img_c3========path==" + gdImageBean.getmGDConfig().getmPath()); 112 | } 113 | 114 | @Override 115 | public void OnError(GDImageBean gdImageBean) { 116 | Log.i(TAG, "OnError: ===批量=====图片压缩=====btn_img_c3=========="); 117 | } 118 | }); 119 | for (int i = 0; i < mSelected.size(); i++) { 120 | GDImageBean imageBeana = new GDImageBean(); 121 | imageBeana.setmGDConfig(new GDConfig().setmPath(mSelectedPath.get(i))); 122 | gdCompressA.start(imageBeana); 123 | } 124 | 125 | (6)、多个图片同时压缩(GDConfig中属性下边统一解释) 126 | 注:这里OnError方法中返回的数据GDImageBean中通过code字段来判断图片是否成功压缩;而OnSuccess方法中所有的图片都是压缩完成的(即只要有一 个图片压缩失败都是回调OnError方法)。 127 | List imageBeans = new ArrayList<>(); 128 | for (int i = 0; i < selectList.size(); i++) { 129 | final String imgpath = selectList.get(i).getPath(); 130 | imageBeans.add(new GDImageBean(new GDConfig().setmPath(imgpath))); 131 | } 132 | new GDCompressImageS(SendCircleActivity.this, imageBeans, new GDCompressImageSListener() { @Override 133 | public void OnSuccess(List imageBeanList) { 134 | 135 | } 136 | 137 | @Override 138 | public void OnError(List imageBeanList) { 139 | 140 | } 141 | }); 142 | 143 | (7)、GDConfig中属性统一解释 144 | new GDConfig() 145 | .setmPath(tempCompressImgPath)//要压缩图片的原路径 146 | .setSavePath(tempCompressImgPath)//压缩图片的保存路径,如果不设置将替换原文件 147 | .setChangeWH(true)//是否要进行调整图片分辨率以压缩到更小 148 | .setWidth(720)//需要调整分辨率的时候有效,压缩后的宽度(按比例计算后的,而不是直接使用这个) 149 | .setHeight(1280)//需要调整分辨率的时候有效,压缩后的高度(按比例计算后的,而不是直接使用这个) 150 | 151 | 152 | 联系作者:QQ:1772889689 邮箱:1772889689@qq.com 微信:gdihh8180 153 | 注:联系请注明联系目的 154 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.wgd.myimagelibrary" 7 | minSdkVersion 15 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(include: ['*.jar'], dir: 'libs') 23 | implementation 'com.android.support:appcompat-v7:27.1.1' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.2' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 28 | implementation 'com.google.code.gson:gson:2.8.2' 29 | /*6.0权限申请依赖库 RxPermissions 和 RxJava2*/ 30 | //RxPermissions 31 | implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar' 32 | //RxJava2 33 | implementation 'io.reactivex.rxjava2:rxjava:2.+' 34 | 35 | /* View注解依赖库 butterknife 项目地址:https://github.com/JakeWharton/butterknife*/ 36 | implementation 'com.jakewharton:butterknife:8.8.1' 37 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 38 | /* 知乎开源的图片选择库 使用uri */ 39 | implementation 'com.zhihu.android:matisse:0.+' 40 | /* 图片加载框架 */ 41 | // implementation 'com.github.bumptech.glide:glide:3.8.0' 42 | implementation 'com.github.bumptech.glide:glide:3.+' 43 | annotationProcessor 'com.github.bumptech.glide:compiler:4.3.1' 44 | implementation 'com.android.support:multidex:1.0.1' 45 | implementation project(':gdcplibrary') 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/wgd/myimagelibrary/App.java: -------------------------------------------------------------------------------- 1 | package com.wgd.myimagelibrary; 2 | 3 | import android.support.multidex.MultiDexApplication; 4 | 5 | public class App extends MultiDexApplication { 6 | 7 | 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/wgd/myimagelibrary/BagImageActivity.java: -------------------------------------------------------------------------------- 1 | package com.wgd.myimagelibrary; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.graphics.BitmapFactory; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | //import butterknife.BindView; 13 | //import butterknife.ButterKnife; 14 | 15 | public class BagImageActivity extends AppCompatActivity { 16 | 17 | // @BindView(R.id.image) 18 | // ImageView image ; 19 | // @BindView(R.id.txt_image_info) 20 | // TextView txt_image_info ; 21 | 22 | public static void start(Activity activity, String path){ 23 | Intent intent = new Intent(activity, BagImageActivity.class); 24 | intent.putExtra("path", path); 25 | activity.startActivity(intent); 26 | } 27 | 28 | @Override 29 | protected void onCreate(@Nullable Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_image_bag); 32 | // ButterKnife.bind(this); 33 | String path = getIntent().getStringExtra("path"); 34 | // image.setImageBitmap(BitmapFactory 35 | // .decodeFile(path)); 36 | 37 | 38 | 39 | 40 | // Bitmap bitmap = BitmapFactory.decodeFile(path); 41 | 42 | // ImageInfoBean imageInfoBean = new ImageGetInfoUtil().getImageInfo(path); 43 | // 44 | // txt_image_info.setText("imageWidth=" + imageInfoBean.getWightHeightSize()[0] + "\n" 45 | // + "imageHeight=" + imageInfoBean.getWightHeightSize()[1] + "\n" 46 | // + "imageSize=" + imageInfoBean.getFileSize() + "\n" 47 | //// + "imageWidthExif=" + imageInfo.getmExifInterface().getAttribute(ExifInterface.TAG_IMAGE_LENGTH ) + "\n" 48 | //// + "imageHeightExif=" + imageInfo.getmExifInterface().getAttribute(ExifInterface.TAG_IMAGE_LENGTH ) + "\n" 49 | // + "" ); 50 | 51 | // ImageInfo imageInfo = ImageInfoUtils.getImageInfo(BagImageActivity.this, bitmap); 52 | // txt_image_info.setText("imageWidth=" + imageInfo.getImageWidth() + "\n" 53 | // + "imageHeight=" + imageInfo.getImageHeight() + "\n" 54 | // + "imageSize=" + imageInfo.getImageSize() + "\n" 55 | // + "imageWidthExif=" + imageInfo.getmExifInterface().getAttribute(ExifInterface.TAG_IMAGE_WIDTH ) + "\n" 56 | // + "imageHeightExif=" + imageInfo.getmExifInterface().getAttribute(ExifInterface.TAG_IMAGE_LENGTH ) + "\n" 57 | // + "" ); 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/wgd/myimagelibrary/BitmapFromUriUtil.java: -------------------------------------------------------------------------------- 1 | package com.wgd.myimagelibrary; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.net.Uri; 6 | import android.provider.MediaStore; 7 | import android.util.Log; 8 | 9 | public class BitmapFromUriUtil { 10 | 11 | public static Bitmap getBitmapFromUri(Context context, Uri uri) 12 | { 13 | try 14 | { 15 | // 读取uri所在的图片 16 | Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri); 17 | return bitmap; 18 | } 19 | catch (Exception e) 20 | { 21 | Log.e("[Android]", e.getMessage()); 22 | Log.e("[Android]", "目录为:" + uri); 23 | e.printStackTrace(); 24 | return null; 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/wgd/myimagelibrary/ImageGetInfoUtil.java: -------------------------------------------------------------------------------- 1 | package com.wgd.myimagelibrary; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.os.Environment; 6 | 7 | import java.io.File; 8 | 9 | public class ImageGetInfoUtil { 10 | 11 | public ImageInfoBean getImageInfo(Bitmap bitmap){ 12 | ImageInfoBean imageInfoBean = new ImageInfoBean(); 13 | try { 14 | int[] originSize = new int[2]; 15 | originSize[0] = bitmap.getWidth(); 16 | originSize[1] = bitmap.getHeight(); 17 | String path = Environment.getExternalStorageDirectory() + "/imageinfo/cache01.jpg" ; 18 | imageInfoBean = new ImageInfoBean(originSize, "", new File(path).length() >> 10); 19 | }catch (Exception e){e.printStackTrace();} 20 | return imageInfoBean; 21 | } 22 | 23 | public ImageInfoBean getImageInfo(String path){ 24 | int[] originSize = computeSize(path); 25 | return new ImageInfoBean(originSize, path, new File(path).length() >> 10); 26 | } 27 | 28 | private int[] computeSize(String srcImg) { 29 | int[] size = new int[2]; 30 | 31 | BitmapFactory.Options options = new BitmapFactory.Options(); 32 | options.inJustDecodeBounds = true; 33 | options.inSampleSize = 1; 34 | 35 | BitmapFactory.decodeFile(srcImg, options); 36 | size[0] = options.outWidth; 37 | size[1] = options.outHeight; 38 | 39 | return size; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/wgd/myimagelibrary/ImageInfoBean.java: -------------------------------------------------------------------------------- 1 | package com.wgd.myimagelibrary; 2 | 3 | public class ImageInfoBean { 4 | 5 | private int[] wightHeightSize; 6 | private String filepath ; 7 | private long fileSize ; 8 | 9 | public ImageInfoBean(){} 10 | public ImageInfoBean(int[] wightHeightSize, String filepath, long fileSize){ 11 | this.wightHeightSize = wightHeightSize; 12 | this.filepath = filepath; 13 | this.fileSize = fileSize; 14 | } 15 | 16 | public int[] getWightHeightSize() { 17 | return wightHeightSize; 18 | } 19 | 20 | public void setWightHeightSize(int[] wightHeightSize) { 21 | this.wightHeightSize = wightHeightSize; 22 | } 23 | 24 | public String getFilepath() { 25 | return filepath; 26 | } 27 | 28 | public void setFilepath(String filepath) { 29 | this.filepath = filepath; 30 | } 31 | 32 | public long getFileSize() { 33 | return fileSize; 34 | } 35 | 36 | public void setFileSize(long fileSize) { 37 | this.fileSize = fileSize; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/wgd/myimagelibrary/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.wgd.myimagelibrary; 2 | 3 | import android.Manifest; 4 | import android.app.ProgressDialog; 5 | import android.content.Intent; 6 | import android.content.res.Resources; 7 | import android.graphics.Bitmap; 8 | import android.graphics.BitmapFactory; 9 | import android.net.Uri; 10 | import android.os.Bundle; 11 | import android.support.v7.app.AppCompatActivity; 12 | import android.util.Log; 13 | import android.view.View; 14 | import android.widget.Button; 15 | import android.widget.ImageView; 16 | import android.widget.TextView; 17 | import android.widget.Toast; 18 | 19 | import com.bumptech.glide.Glide; 20 | import com.tbruyelle.rxpermissions2.RxPermissions; 21 | import com.wgd.gdcp.gdcplibrary.GDCompress; 22 | import com.wgd.gdcp.gdcplibrary.GDCompressA; 23 | import com.wgd.gdcp.gdcplibrary.GDCompressC; 24 | import com.wgd.gdcp.gdcplibrary.GDCompressImageListener; 25 | import com.wgd.gdcp.gdcplibrary.GDCompressImageListenerA; 26 | import com.wgd.gdcp.gdcplibrary.GDCompressImageS; 27 | import com.wgd.gdcp.gdcplibrary.GDCompressImageSListener; 28 | import com.wgd.gdcp.gdcplibrary.GDConfig; 29 | import com.wgd.gdcp.gdcplibrary.GDImageBean; 30 | import com.wgd.gdcp.gdcplibrary.GDTools; 31 | import com.wgd.gdcp.gdcplibrary.ImageUtils; 32 | import com.zhihu.matisse.Matisse; 33 | import com.zhihu.matisse.MimeType; 34 | import com.zhihu.matisse.engine.impl.GlideEngine; 35 | import com.zhihu.matisse.internal.entity.CaptureStrategy; 36 | 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | 40 | import butterknife.BindView; 41 | import butterknife.ButterKnife; 42 | import butterknife.OnClick; 43 | import io.reactivex.Observer; 44 | import io.reactivex.disposables.Disposable; 45 | /* 46 | * 47 | * //gradlew clean build bintrayUpload -PbintrayUser=wangruijun -PbintrayKey=a0d662943bb4c5ae83cd3d539113c91b04570499 -PdryRun=false 48 | * */ 49 | public class MainActivity extends AppCompatActivity { 50 | 51 | private static final String TAG = "GDCimage"; 52 | @BindView(R.id.btn_select_img) 53 | Button selectImg ; 54 | @BindView(R.id.image_view) 55 | ImageView image_view ; 56 | @BindView(R.id.btn_img_crop) 57 | Button btn_img_crop ; 58 | @BindView(R.id.image_view_c2) 59 | ImageView image_view_c2 ; 60 | @BindView(R.id.image_view_c3) 61 | ImageView image_view_c3 ; 62 | @BindView(R.id.image_view_crop) 63 | ImageView image_view_crop ; 64 | @BindView(R.id.txt_yuantu) 65 | TextView txt_yuantu ; 66 | @BindView(R.id.txt_crop) 67 | TextView txt_crop ; 68 | @BindView(R.id.txt_c2) 69 | TextView txt_c2 ; 70 | @BindView(R.id.txt_c3) 71 | TextView txt_c3 ; 72 | 73 | private final int REQUEST_CODE_CHOOSE= 1001 ; 74 | 75 | @Override 76 | protected void onCreate(Bundle savedInstanceState) { 77 | super.onCreate(savedInstanceState); 78 | setContentView(R.layout.activity_main); 79 | ButterKnife.bind(this); 80 | } 81 | 82 | 83 | @OnClick({R.id.btn_select_img, R.id.btn_img_crop, R.id.image_view, R.id.image_view_crop, R.id.btn_img_more 84 | ,R.id.btn_img_c2,R.id.btn_img_c3 85 | }) 86 | public void onClick(View view) { 87 | switch (view.getId()){ 88 | case R.id.btn_select_img : 89 | getRxPermissions(); 90 | break; 91 | case R.id.btn_img_crop : 92 | if (null==mSelected || mSelected.size()<=0){ 93 | Toast.makeText(MainActivity.this, "请先选择图片!", Toast.LENGTH_LONG).show(); 94 | }else { 95 | String tempCompressImgPath = mSelectedPath.get(0);//事先准备好的sd卡目录下的图片 96 | // 单个图片压缩,多线程模式 97 | new GDCompressC(MainActivity.this, 98 | new GDConfig().setmPath(tempCompressImgPath), new GDCompressImageListener() { 99 | // new GDConfig().setmPath(tempCompressImgPath).setChangeWH(true), new GDCompressImageListener() { 100 | @Override 101 | public void OnSuccess(String path) { 102 | MainActivity.this.runOnUiThread(new Runnable() { 103 | @Override 104 | public void run() { 105 | image_view_crop.setImageBitmap(BitmapFactory 106 | .decodeFile(mSelectedPath.get(0))); 107 | ImageInfoBean imageInfoBean = new ImageGetInfoUtil().getImageInfo(mSelectedPath.get(0)); 108 | 109 | txt_crop.setText("imageWidth=" + imageInfoBean.getWightHeightSize()[0] + "\n" 110 | + "imageHeight=" + imageInfoBean.getWightHeightSize()[1] + "\n" 111 | + "imageSize=" + imageInfoBean.getFileSize() + "\n" 112 | + "" ); 113 | } 114 | }); 115 | } 116 | 117 | @Override 118 | public void OnError(int code, String errorMsg) { 119 | 120 | } 121 | }); 122 | 123 | } 124 | break; 125 | case R.id.btn_img_c2 : 126 | if (null==mSelected || mSelected.size()<=0){ 127 | Toast.makeText(MainActivity.this, "请先选择图片!", Toast.LENGTH_LONG).show(); 128 | }else if (1 == mSelected.size()){ 129 | // 单个图片压缩,多线程模式 130 | String tempCompressImgPath = mSelectedPath.get(0);//事先准备好的sd卡目录下的图片 131 | 132 | new GDCompressC(MainActivity.this, 133 | new GDConfig().setmPath(tempCompressImgPath), new GDCompressImageListener() { 134 | // new GDConfig().setmPath(tempCompressImgPath).setChangeWH(true), new GDCompressImageListener() { 135 | @Override 136 | public void OnSuccess(String path) { 137 | MainActivity.this.runOnUiThread(new Runnable() { 138 | @Override 139 | public void run() { 140 | image_view_c2.setImageBitmap(BitmapFactory 141 | .decodeFile(mSelectedPath.get(0))); 142 | ImageInfoBean imageInfoBean = new ImageGetInfoUtil().getImageInfo(mSelectedPath.get(0)); 143 | 144 | txt_c2.setText("imageWidth=" + imageInfoBean.getWightHeightSize()[0] + "\n" 145 | + "imageHeight=" + imageInfoBean.getWightHeightSize()[1] + "\n" 146 | + "imageSize=" + imageInfoBean.getFileSize() + "\n" 147 | + "" ); 148 | } 149 | }); 150 | } 151 | 152 | @Override 153 | public void OnError(int code, String errorMsg) { 154 | 155 | } 156 | }); 157 | 158 | }else { 159 | // 批量压缩,多线程模式,速度快,但OOM的几率大 160 | GDCompressC gdCompressC = new GDCompressC(MainActivity.this, new GDCompressImageListener() { 161 | @Override 162 | public void OnSuccess(String path) { 163 | Log.i(TAG, "OnSuccess: ===批量=====图片压缩=====btn_img_c2========path==" + path); 164 | } 165 | 166 | @Override 167 | public void OnError(int code, String errorMsg) { 168 | Log.i(TAG, "OnError: ===批量=====图片压缩=====btn_img_c2=========="); 169 | } 170 | }); 171 | for (int i = 0; i < mSelected.size(); i++) { 172 | gdCompressC.start(new GDConfig().setmPath(mSelectedPath.get(i))); 173 | } 174 | } 175 | break; 176 | case R.id.btn_img_c3 : 177 | if (null==mSelected || mSelected.size()<=0){ 178 | Toast.makeText(MainActivity.this, "请先选择图片!", Toast.LENGTH_LONG).show(); 179 | }else if (1 == mSelected.size()){ 180 | // 单个图片压缩,单线程模式 181 | String tempCompressImgPath = mSelectedPath.get(0);//事先准备好的sd卡目录下的图片 182 | GDImageBean imageBeana = new GDImageBean(); 183 | imageBeana.setmGDConfig(new GDConfig().setmPath(tempCompressImgPath)); 184 | new GDCompressA(MainActivity.this, imageBeana 185 | , new GDCompressImageListenerA() { 186 | // new GDConfig().setmPath(tempCompressImgPath).setChangeWH(true), new GDCompressImageListener() { 187 | @Override 188 | public void OnSuccess(GDImageBean gdImageBean) { 189 | MainActivity.this.runOnUiThread(new Runnable() { 190 | @Override 191 | public void run() { 192 | image_view_c3.setImageBitmap(BitmapFactory 193 | .decodeFile(mSelectedPath.get(0))); 194 | ImageInfoBean imageInfoBean = new ImageGetInfoUtil().getImageInfo(mSelectedPath.get(0)); 195 | 196 | txt_c3.setText("imageWidth=" + imageInfoBean.getWightHeightSize()[0] + "\n" 197 | + "imageHeight=" + imageInfoBean.getWightHeightSize()[1] + "\n" 198 | + "imageSize=" + imageInfoBean.getFileSize() + "\n" 199 | + "" ); 200 | } 201 | }); 202 | } 203 | 204 | @Override 205 | public void OnError(GDImageBean gdImageBean) { 206 | 207 | } 208 | }); 209 | 210 | }else { 211 | // 批量压缩,单线程模式,速度慢,但OOM几率小很多 212 | GDCompressA gdCompressA = new GDCompressA(MainActivity.this, new GDCompressImageListenerA() { 213 | @Override 214 | public void OnSuccess(GDImageBean gdImageBean) { 215 | Log.i(TAG, "OnSuccess: ===批量=====图片压缩=====btn_img_c3========path==" + gdImageBean.getmGDConfig().getmPath()); 216 | } 217 | 218 | @Override 219 | public void OnError(GDImageBean gdImageBean) { 220 | Log.i(TAG, "OnError: ===批量=====图片压缩=====btn_img_c3=========="); 221 | } 222 | }); 223 | for (int i = 0; i < mSelected.size(); i++) { 224 | GDImageBean imageBeana = new GDImageBean(); 225 | imageBeana.setmGDConfig(new GDConfig().setmPath(mSelectedPath.get(i))); 226 | gdCompressA.start(imageBeana); 227 | } 228 | } 229 | break; 230 | case R.id.image_view: 231 | case R.id.image_view_crop: 232 | BagImageActivity.start(MainActivity.this, mSelectedPath.get(0)); 233 | break; 234 | case R.id.btn_img_more: 235 | if (null==mSelected || mSelected.size()<=0){ 236 | Toast.makeText(MainActivity.this, "请先选择图片!", Toast.LENGTH_LONG).show(); 237 | }else { 238 | // 批量压缩,单线程模式,速度慢,但OOM几率小很多,内部通过GDCompressA实现 239 | showProgress("", false); 240 | List data = new ArrayList<>(); 241 | for (int i = 0; i < mSelectedPath.size(); i++) { 242 | GDImageBean gdImageBean = new GDImageBean(); 243 | GDConfig gdConfig = new GDConfig(); 244 | gdConfig.setmPath(mSelectedPath.get(i)); 245 | Log.i("GDCimage", "onClick: ===============mSelectedPath.get(i)============" + mSelectedPath.get(i)); 246 | gdImageBean.setmGDConfig(gdConfig); 247 | data.add(gdImageBean); 248 | } 249 | new GDCompressImageS(MainActivity.this, data, new GDCompressImageSListener() { 250 | @Override 251 | public void OnSuccess(List imageBeanList) { 252 | Toast.makeText(MainActivity.this, "图片批量压缩成功!", Toast.LENGTH_LONG).show(); 253 | hideProgress(); 254 | } 255 | 256 | @Override 257 | public void OnError(List imageBeanList) { 258 | // 这里的code为0是成功;为1是失败 259 | Toast.makeText(MainActivity.this, "图片批量压缩失败!" , Toast.LENGTH_LONG).show(); 260 | hideProgress(); 261 | } 262 | }); 263 | } 264 | break; 265 | } 266 | } 267 | 268 | 269 | private void getRxPermissions(){ 270 | 271 | RxPermissions rxPermission = new RxPermissions(MainActivity.this); 272 | //每条权限单独判断是否同意 273 | //一次直接返回是否全部同意 274 | rxPermission.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, 275 | Manifest.permission.CAMERA) 276 | .subscribe(new Observer() { 277 | @Override 278 | public void onSubscribe(Disposable d) { 279 | // Toast.makeText(MainActivity.this, "onSubscribe", Toast.LENGTH_SHORT).show(); 280 | } 281 | 282 | @Override 283 | public void onNext(Boolean value) { 284 | if(value){ 285 | // Toast.makeText(MainActivity.this, "同意权限", Toast.LENGTH_SHORT).show(); 286 | getPic(); 287 | }else { 288 | Toast.makeText(MainActivity.this, "拒绝权限", Toast.LENGTH_SHORT).show(); 289 | } 290 | } 291 | 292 | @Override 293 | public void onError(Throwable e) { 294 | // Toast.makeText(MainActivity.this, "onError", Toast.LENGTH_SHORT).show(); 295 | } 296 | 297 | @Override 298 | public void onComplete() { 299 | // Toast.makeText(MainActivity.this, "onComplete", Toast.LENGTH_SHORT).show(); 300 | } 301 | }); 302 | 303 | } 304 | 305 | private void getPic(){ 306 | Matisse.from(MainActivity.this) 307 | .choose(MimeType.ofAll())//图片类型 308 | .countable(true)//true:选中后显示数字;false:选中后显示对号 309 | .maxSelectable(9)//可选的最大数 310 | .capture(true)//选择照片时,是否显示拍照 311 | .captureStrategy(new CaptureStrategy(true, "com.wgd.myimagelibrary.fileprovider"))//参数1 true表示拍照存储在共有目录,false表示存储在私有目录;参数2与 AndroidManifest中authorities值相同,用于适配7.0系统 必须设置 312 | .imageEngine(new GlideEngine())//图片加载引擎 313 | .forResult(REQUEST_CODE_CHOOSE);// 314 | } 315 | 316 | List mSelected; 317 | List mSelectedPath; 318 | 319 | @Override 320 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 321 | super.onActivityResult(requestCode, resultCode, data); 322 | if (requestCode == REQUEST_CODE_CHOOSE && resultCode == RESULT_OK) { 323 | try { 324 | mSelected = Matisse.obtainResult(data); 325 | mSelectedPath= Matisse.obtainPathResult(data); 326 | Glide.with(MainActivity.this) 327 | .load(mSelected.get(0)) 328 | .thumbnail(0.1f) 329 | .into(image_view); 330 | // ImageInfoBean imageInfoBean = new ImageGetInfoUtil().getImageInfo(mSelectedPath.get(0)); 331 | // txt_yuantu.setText("imageWidth=" + imageInfoBean.getWightHeightSize()[0] + "\n" 332 | // + "imageHeight=" + imageInfoBean.getWightHeightSize()[1] + "\n" 333 | // + "imageSize=" + imageInfoBean.getFileSize() + "\n" 334 | // + "" ); 335 | }catch (Exception e){ 336 | e.printStackTrace(); 337 | } 338 | } 339 | } 340 | 341 | ProgressDialog progressDialog; 342 | public void showProgress(String message,boolean iscancel) { 343 | if (progressDialog != null) { 344 | if (progressDialog.isShowing()) { 345 | return; 346 | } 347 | progressDialog.setMessage(message != null ? message 348 | : "loading..."); 349 | progressDialog.show(); 350 | } else { 351 | progressDialog = new ProgressDialog(MainActivity.this); 352 | progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); 353 | progressDialog.setIndeterminate(false); 354 | progressDialog.setCancelable(iscancel); 355 | progressDialog.setMessage(message != null ? message 356 | : "loading..."); 357 | progressDialog.show(); 358 | } 359 | } 360 | public void hideProgress() { 361 | if (progressDialog != null&&progressDialog.isShowing()) { 362 | progressDialog.dismiss(); 363 | } 364 | } 365 | 366 | } 367 | -------------------------------------------------------------------------------- /app/src/main/java/com/wgd/myimagelibrary/PathFromUriUtil.java: -------------------------------------------------------------------------------- 1 | package com.wgd.myimagelibrary; 2 | 3 | import android.content.Context; 4 | import android.content.CursorLoader; 5 | import android.database.Cursor; 6 | import android.net.Uri; 7 | import android.os.Build; 8 | import android.provider.DocumentsContract; 9 | import android.provider.MediaStore; 10 | import android.support.annotation.RequiresApi; 11 | 12 | public class PathFromUriUtil { 13 | 14 | /** 15 | * 16 | * 这里由于Matisse返回的Uri格式问题,弃用 17 | * 18 | * 根据图片的Uri获取图片的绝对路径(已经适配多种API) 19 | * @return 如果Uri对应的图片存在,那么返回该图片的绝对路径,否则返回null 20 | */ 21 | public static String getRealPathFromUri(Context context, Uri uri) { 22 | int sdkVersion = Build.VERSION.SDK_INT; 23 | if (sdkVersion < 11) { 24 | // SDK < Api11 25 | return getRealPathFromUri_BelowApi11(context, uri); 26 | } 27 | if (sdkVersion < 19) { 28 | // SDK > 11 && SDK < 19 29 | return getRealPathFromUri_Api11To18(context, uri); 30 | } 31 | // SDK > 19 32 | return getRealPathFromUri_AboveApi19(context, uri); 33 | } 34 | 35 | /** 36 | * 适配api19以上,根据uri获取图片的绝对路径 37 | */ 38 | @RequiresApi(api = Build.VERSION_CODES.KITKAT) 39 | private static String getRealPathFromUri_AboveApi19(Context context, Uri uri) { 40 | String filePath = null; 41 | String wholeID = DocumentsContract.getDocumentId(uri); 42 | 43 | // 使用':'分割 44 | String id = wholeID.split(":")[1]; 45 | 46 | String[] projection = { MediaStore.Images.Media.DATA }; 47 | String selection = MediaStore.Images.Media._ID + "=?"; 48 | String[] selectionArgs = { id }; 49 | 50 | Cursor cursor = context.getContentResolver().query( 51 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, 52 | selection, selectionArgs, null); 53 | int columnIndex = cursor.getColumnIndex(projection[0]); 54 | 55 | if (cursor.moveToFirst()) { 56 | filePath = cursor.getString(columnIndex); 57 | } 58 | cursor.close(); 59 | return filePath; 60 | } 61 | 62 | /** 63 | * 适配api11-api18,根据uri获取图片的绝对路径 64 | */ 65 | private static String getRealPathFromUri_Api11To18(Context context, Uri uri) { 66 | String filePath = null; 67 | String[] projection = { MediaStore.Images.Media.DATA }; 68 | 69 | CursorLoader loader = new CursorLoader(context, uri, projection, null, 70 | null, null); 71 | Cursor cursor = loader.loadInBackground(); 72 | 73 | if (cursor != null) { 74 | cursor.moveToFirst(); 75 | filePath = cursor.getString(cursor.getColumnIndex(projection[0])); 76 | cursor.close(); 77 | } 78 | return filePath; 79 | } 80 | 81 | /** 82 | * 适配api11以下(不包括api11),根据uri获取图片的绝对路径 83 | */ 84 | private static String getRealPathFromUri_BelowApi11(Context context, Uri uri) { 85 | String filePath = null; 86 | String[] projection = { MediaStore.Images.Media.DATA }; 87 | Cursor cursor = context.getContentResolver().query(uri, projection, 88 | null, null, null); 89 | if (cursor != null) { 90 | cursor.moveToFirst(); 91 | filePath = cursor.getString(cursor.getColumnIndex(projection[0])); 92 | cursor.close(); 93 | } 94 | return filePath; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_image_bag.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 16 |