android使用luban压缩图片

之前写了调用系统相机拍照和本地选择照片,今天我们就来使用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属性大家都知道,不了解的可以百度下。


多说一句话:需要阿胶糕 的可以  + 微信18703691242
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值