之前写了调用系统相机拍照和本地选择照片,今天我们就来使用luban工具将原图片压缩之后分图片质量依然保持不变的方法,我相信这个方法一定能满足你。本文章讲解的是单选图片,而不是批量添加图片,多选的我没写过。
先看下图:
图片质量没变,而且图片也有原来的2.2M变成256K。
下面是处理的代码:
首先我们先在gradle里面添加使用到的依赖:
compile 'com.github.bumptech.glide:glide:3.7.0' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.1.0'OK,下面来时撸/撸/撸/码
LuBanActivity代码:
public class LuBanActivity extends AppCompatActivity { private static final String TAG = "Luban"; private static final int PHOTO_REQUEST_GALLERY = 2;// 从相册中选择 private String imgPath = ""; private TextView fileSize; private TextView imageSize; private TextView thumbFileSize; private TextView thumbImageSize; private ImageView image; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_lu_ban); fileSize = (TextView) findViewById(R.id.file_size); imageSize = (TextView) findViewById(R.id.image_size); thumbFileSize = (TextView) findViewById(R.id.thumb_file_size); thumbImageSize = (TextView) findViewById(R.id.thumb_image_size); image = (ImageView) findViewById(R.id.image); Button btnPhoto = (Button) findViewById(R.id.btn_photo); btnPhoto.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { gallery(); } }); } //从相册取图片 public void gallery() { // 激活系统图库,选择一张图片 Intent intent = new Intent(Intent.ACTION_PICK, null); intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(intent, PHOTO_REQUEST_GALLERY); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK && requestCode == PHOTO_REQUEST_GALLERY) {//相册 if (data != null) { Uri uri = data.getData(); /**获取路径**/ String[] proj = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query(uri, proj, null, null, null); if (cursor.moveToFirst()) { int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); imgPath = cursor.getString(column_index); } File file = new File(imgPath); fileSize.setText(file.length() / 1024 + "k"); imageSize.setText(computeSize(file)[0] + "*" + computeSize(file)[1]); /**两种方法都可以压缩**/ // compressWithRx(file); //Rxjava compressWithLs(file); //luban } } } /** * Rxjava处理 */ private void compressWithRx(File file) { Flowable.just(file) .observeOn(Schedulers.io()) .map(new Function<File, File>() { @Override public File apply(File file) throws Exception { return Luban.with(LuBanActivity.this).load(file).get(); } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<File>() { @Override public void accept(File file) throws Exception { Log.d(TAG, file.getAbsolutePath()); Glide.with(LuBanActivity.this).load(file).into(image); thumbFileSize.setText(file.length() / 1024 + "k"); thumbImageSize.setText(computeSize(file)[0] + "*" + computeSize(file)[1]); } }); } /** * 压缩单张图片 Listener 方式 * luban处理 */ private void compressWithLs(File file) { Luban.with(this) .load(file) .setCompressListener(new OnCompressListener() { @Override public void onStart() { Toast.makeText(LuBanActivity.this, "I'm start", Toast.LENGTH_SHORT).show(); } @Override public void onSuccess(File file) { Log.i("path", file.getAbsolutePath()); Glide.with(LuBanActivity.this).load(file).into(image); thumbFileSize.setText(file.length()/ 1024 + "k"); thumbImageSize.setText(computeSize(file)[0] + "*" + computeSize(file)[1]); } @Override public void onError(Throwable e) { } }).launch(); } /**获取图片的尺寸**/ private int[] computeSize(File srcImg) { int[] size = new int[2]; BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inSampleSize = 1; BitmapFactory.decodeFile(srcImg.getAbsolutePath(), options); size[0] = options.outWidth; size[1] = options.outHeight; return size; } }
xml布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="原图参数" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/file_size" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> <TextView android:id="@+id/image_size" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> </LinearLayout> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="压缩后参数" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/thumb_file_size" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> <TextView android:id="@+id/thumb_image_size" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> </LinearLayout> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@null" /> </LinearLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_photo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_margin="16dp" android:text="选择照片"/> </RelativeLayout> </LinearLayout>工具类:
Luban类
/**luban压缩方法**/ public class Luban implements Handler.Callback { private static final String TAG = "Luban"; private static final String DEFAULT_DISK_CACHE_DIR = "luban_disk_cache"; private static final int MSG_COMPRESS_SUCCESS = 0; private static final int MSG_COMPRESS_START = 1; private static final int MSG_COMPRESS_ERROR = 2; private File file; private OnCompressListener onCompressListener; private Handler mHandler; private Luban(Builder builder) { this.file = builder.file; this.onCompressListener = builder.onCompressListener; mHandler = new Handler(Looper.getMainLooper(), this); } public static Builder with(Context context) { return new Builder(context); } /** * Returns a file with a cache audio name in the private cache directory. * * @param context * A context. */ private File getImageCacheFile(Context context) { if (getImageCacheDir(context) != null) { return new File(getImageCacheDir(context) + "/" + System.currentTimeMillis() + (int) (Math.random() * 1000) + ".jpg"); } return null; } /** * Returns a directory with a default name in the private cache directory of the application to * use to store retrieved audio. * * @param context * A context. * * @see #getImageCacheDir(Context, String) */ @Nullable private File getImageCacheDir(Context context) { return getImageCacheDir(context, DEFAULT_DISK_CACHE_DIR); } /** * Returns a directory with the given name in the private cache directory of the application to * use to store retrieved media and thumbnails. * * @param context * A context. * @param cacheName * The name of the subdirectory in which to store the cache. * * @see #getImageCacheDir(Context) */ @Nullable private File getImageCacheDir(Context context, String cacheName) { File cacheDir = context.getExternalCacheDir(); if (cacheDir != null) { File result = new File(cacheDir, cacheName); if (!result.mkdirs() && (!result.exists() || !result.isDirectory())) { // File wasn't able to create a directory, or the result exists but not a directory return null; } return result; } if (Log.isLoggable(TAG, Log.ERROR)) { Log.e(TAG, "default disk cache dir is null"); } return null; } /** * start asynchronous compress thread */ @UiThread private void launch(final Context context) { if (file == null && onCompressListener != null) { onCompressListener.onError(new NullPointerException("image file cannot be null")); } new Thread(new Runnable() { @Override public void run() { try { mHandler.sendMessage(mHandler.obtainMessage(MSG_COMPRESS_START)); File result = new Engine(file, getImageCacheFile(context)).compress(); mHandler.sendMessage(mHandler.obtainMessage(MSG_COMPRESS_SUCCESS, result)); } catch (IOException e) { mHandler.sendMessage(mHandler.obtainMessage(MSG_COMPRESS_ERROR, e)); } } }).start(); } /** * start compress and return the file */ @WorkerThread private File get(final Context context) throws IOException { return new Engine(file, getImageCacheFile(context)).compress(); } @Override public boolean handleMessage(Message msg) { if (onCompressListener == null) return false; switch (msg.what) { case MSG_COMPRESS_START: onCompressListener.onStart(); break; case MSG_COMPRESS_SUCCESS: onCompressListener.onSuccess((File) msg.obj); break; case MSG_COMPRESS_ERROR: onCompressListener.onError((Throwable) msg.obj); break; } return false; } public static class Builder { private Context context; private File file; private OnCompressListener onCompressListener; Builder(Context context) { this.context = context; } private Luban build() { return new Luban(this); } public Builder load(File file) { this.file = file; return this; } public Builder putGear(int gear) { return this; } public Builder setCompressListener(OnCompressListener listener) { this.onCompressListener = listener; return this; } public void launch() { build().launch(context); } public File get() throws IOException { return build().get(context); } } }
Engine类
/** * Responsible for starting compress and managing active and cached resources. * 压缩算法思路 */ class Engine { private ExifInterface srcExif; private File srcImg; private File tagImg; private int srcWidth; private int srcHeight; Engine(File srcImg, File tagImg) throws IOException { if (isJpeg(srcImg)) { this.srcExif = new ExifInterface(srcImg.getAbsolutePath()); } this.tagImg = tagImg; this.srcImg = srcImg; BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inSampleSize = 1; BitmapFactory.decodeFile(srcImg.getAbsolutePath(), options); this.srcWidth = options.outWidth; this.srcHeight = options.outHeight; } private boolean isJpeg(File photo) { return photo.getAbsolutePath().contains("jpeg") || photo.getAbsolutePath().contains("jpg"); } private int computeSize() { int mSampleSize; srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;//临时宽,将宽变作偶数 srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;//临时高,将高变作偶数 srcWidth = srcWidth > srcHeight ? srcHeight : srcWidth;//将小的一边给width,最短边 srcHeight = srcWidth > srcHeight ? srcWidth : srcHeight;//将大的一边给height,最长边 double scale = ((double) srcWidth / srcHeight);//比例,图片短边除以长边为该图片比例 if (scale <= 1 && scale > 0.5625) { //判断最长边是否过界 if (srcHeight < 1664) {//最长边小于1664px mSampleSize = 1; } else if (srcHeight >= 1664 && srcHeight < 4990) { mSampleSize = 2; } else if (srcHeight >= 4990 && srcHeight < 10240) { mSampleSize = 4; } else { mSampleSize = srcHeight / 1280 == 0 ? 1 : srcHeight / 1280; } } else if (scale <= 0.5625 && scale > 0.5) {//比例在[0.5625,00.5)区间 mSampleSize = srcHeight / 1280 == 0 ? 1 : srcHeight / 1280; } else {//比例小于0.5 mSampleSize = (int) Math.ceil(srcHeight / (1280.0 / scale)); } return mSampleSize; } private Bitmap rotatingImage(Bitmap bitmap) { if (srcExif == null) return bitmap; Matrix matrix = new Matrix(); int angle = 0; int orientation = srcExif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: angle = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: angle = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: angle = 270; break; } matrix.postRotate(angle); return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } File compress() throws IOException { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = computeSize(); Bitmap tagBitmap = BitmapFactory.decodeFile(srcImg.getAbsolutePath(), options); ByteArrayOutputStream stream = new ByteArrayOutputStream(); tagBitmap = rotatingImage(tagBitmap); tagBitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream); tagBitmap.recycle(); FileOutputStream fos = new FileOutputStream(tagImg); fos.write(stream.toByteArray()); fos.flush(); fos.close(); stream.close(); return tagImg; } }
OnCompressListener接口实现
public interface OnCompressListener { /** * Fired when the compression is started, override to handle in your own code * //压缩开始前 */ void onStart(); /** * Fired when a compression returns successfully, override to handle in your own code * 压缩成功后 */ void onSuccess(File file); /** * Fired when a compression fails to complete, override to handle in your own code * //压缩失败 */ void onError(Throwable e); }OK,以上就是完整的代码。当然了,imageview的属性大家可以自己设置,别忘了添加 scaleType 属性
我试用的是。 android:scaleType="centerCrop"
scaleType属性大家都知道,不了解的可以百度下。