Skip to content

Instantly share code, notes, and snippets.

@msdx
Last active September 21, 2017 15:23
Show Gist options
  • Save msdx/673bd418dc29002b21ba to your computer and use it in GitHub Desktop.
Save msdx/673bd418dc29002b21ba to your computer and use it in GitHub Desktop.
大图裁剪
package com.parkingwang.app.account.vehicle.takephoto;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.ExifInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.os.AsyncTaskCompat;
import android.view.View;
import android.widget.TextView;
import com.alibaba.fastjson.util.IOUtils;
import com.github.yoojia.fast.view.AutoView;
import com.github.yoojia.fast.view.ViewFinder;
import com.parkingwang.app.R;
import com.parkingwang.app.support.BugReport;
import com.parkingwang.app.support.MessageProxy;
import com.parkingwang.widget.BaseActivity;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 裁剪图片
*
* @author Geek_Soledad ([email protected])
* @version 2015-12-30 3.3
* @since 2015-12-30 3.3
*/
public class ClipImageActivity extends BaseActivity implements View.OnClickListener {
@AutoView(viewId = R.id.clip_image_view)
private ClipImageView mClipImageView;
@AutoView(viewId = R.id.crop_border)
private Viewfinder mCropBorder;
@AutoView(viewId = R.id.cancel)
private TextView mCancel;
@AutoView(viewId = R.id.clip)
private TextView mClip;
private String mOutput;
private String mInputPath;
private int mMaxWidth;
// 图片被旋转的角度
private int mDegree;
// 大图被设置之前的缩放比例
private int mSampleSize;
private int mSourceWidth;
private int mSourceHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_clip_image);
ViewFinder.inject(this);
updateUIForCallingActivity();
mCancel.setOnClickListener(this);
mClip.setOnClickListener(this);
final Intent data = getIntent();
mOutput = PhotoActionHelper.getOutputPath(data);
mInputPath = PhotoActionHelper.getInputPath(data);
mMaxWidth = PhotoActionHelper.getMaxOutputWidth(data);
setImageAndClipParams();
}
private void setImageAndClipParams() {
mClipImageView.post(new Runnable() {
@Override
public void run() {
final Rect border = mCropBorder.getBorder();
mClipImageView.setClipBorder(border);
mClipImageView.setMaxOutputWidth(mMaxWidth);
mDegree = readPictureDegree(mInputPath);
final boolean isRotate = (mDegree == 90 || mDegree == 270);
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mInputPath,options);
mSourceWidth = options.outWidth;
mSourceHeight = options.outHeight;
// 如果图片被旋转,则宽高度置换
int w = isRotate ? options.outHeight : options.outWidth;
// 裁剪是宽高比例3:2,只考虑宽度情况,这里按border宽度的两倍来计算缩放。
mSampleSize = findBestSampleSize(w, border.width());
options.inJustDecodeBounds = false;
options.inSampleSize = mSampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565;
final Bitmap source = BitmapFactory.decodeFile(mInputPath, options);
// 解决图片被旋转的问题
Bitmap target;
if (mDegree == 0) {
target = source;
} else {
final Matrix matrix = new Matrix();
matrix.postRotate(mDegree);
target = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, false);
if (target != source && !source.isRecycled()) {
source.recycle();
}
}
mClipImageView.setImageBitmap(target);
}
});
}
/**
* 计算最好的采样大小。
* @param currentWidth 当前宽度
* @param limitWidth 限定宽度
* @return sampleSize
*/
private int findBestSampleSize(int currentWidth, int limitWidth) {
int sampleSize = 1;
for (int width = currentWidth / 2; width > limitWidth; width /= 2) {
sampleSize *= 2;
}
return sampleSize;
}
/**
* 读取图片属性:旋转的角度
*
* @param path 图片绝对路径
* @return degree旋转的角度
*/
public static int readPictureDegree(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
private void updateUIForCallingActivity() {
ComponentName name = getCallingActivity();
if (name != null && TakePhotoActivity.class.getName().endsWith(name.getClassName())) {
mCancel.setText(R.string.name_retake);
mClip.setText(R.string.name_use_photo);
}
}
@Override
public void onClick(View v) {
final int id = v.getId();
switch (id) {
case R.id.cancel:
onBackPressed();
break;
case R.id.clip:
clipImage();
break;
default:// do nothing
}
}
private void clipImage() {
if (mOutput != null) {
showLoading(R.string.msg_clipping_image);
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mOutput);
Bitmap bitmap = getClippedBitmap();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
if (!bitmap.isRecycled()) {
bitmap.recycle();
}
setResult(Activity.RESULT_OK, getIntent());
} catch (Exception e) {
BugReport.report(e);
MessageProxy.showError(ClipImageActivity.this, R.string.msg_could_not_save_photo);
} finally {
IOUtils.close(fos);
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
showLoading(false);
finish();
}
};
AsyncTaskCompat.executeParallel(task);
} else {
finish();
}
}
private Bitmap getClippedBitmap() {
if (mSampleSize <= 1) {
return mClipImageView.clip();
}
// 获取缩放位移后的矩阵值
final float[] matrixValues = mClipImageView.getClipMatrixValues();
final float scale = matrixValues[Matrix.MSCALE_X];
final float transX = matrixValues[Matrix.MTRANS_X];
final float transY = matrixValues[Matrix.MTRANS_Y];
// 获取在显示的图片中裁剪的位置
final Rect border = mCropBorder.getBorder();
final float cropX = ((-transX + border.left) / scale) * mSampleSize;
final float cropY = ((-transY + border.top) / scale) * mSampleSize;
final float cropWidth = (border.width() / scale) * mSampleSize;
final float cropHeight = (border.height() / scale) * mSampleSize;
// 获取在旋转之前的裁剪位置
final RectF srcRect = new RectF(cropX, cropY, cropX + cropWidth, cropY + cropHeight);
final Rect clipRect = getRealRect(srcRect);
final BitmapFactory.Options ops = new BitmapFactory.Options();
final Matrix outputMatrix = new Matrix();
outputMatrix.setRotate(mDegree);
// 如果裁剪之后的图片宽高仍然太大,则进行缩小
if (mMaxWidth > 0 && cropWidth > mMaxWidth) {
final int sampleSize = findBestSampleSize((int) cropWidth, mMaxWidth);
ops.inSampleSize = sampleSize;
final float outputScale = mMaxWidth / (cropWidth / sampleSize);
outputMatrix.postScale(outputScale, outputScale);
}
// 裁剪
BitmapRegionDecoder decoder = null;
try {
decoder = BitmapRegionDecoder.newInstance(mInputPath, false);
final Bitmap source = decoder.decodeRegion(clipRect, ops);
recycleImageViewBitmap();
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), outputMatrix, false);
} catch (Exception e) {
BugReport.report(e);
return mClipImageView.clip();
} finally {
if (decoder != null && !decoder.isRecycled()) {
decoder.recycle();
}
}
}
private Rect getRealRect(RectF srcRect) {
final int reverseDegree = 360 - mDegree;// 矩形应该旋转的角度与图片需要旋转的角度方向相反
switch (reverseDegree) {
case 90:
return new Rect((int) (mSourceWidth - srcRect.bottom), (int) srcRect.left,
(int) (mSourceWidth - srcRect.top), (int) srcRect.right);
case 180:
return new Rect((int) (mSourceHeight - srcRect.right), (int) (mSourceWidth - srcRect.bottom),
(int) (mSourceHeight - srcRect.left), (int) (mSourceWidth - srcRect.top));
case 270:
return new Rect((int) srcRect.top, (int) (mSourceHeight - srcRect.right),
(int) srcRect.bottom, (int) (mSourceHeight - srcRect.left));
default:
return new Rect((int) srcRect.left, (int) srcRect.top, (int) srcRect.right, (int) srcRect.bottom);
}
}
private void recycleImageViewBitmap() {
mClipImageView.post(new Runnable() {
@Override
public void run() {
mClipImageView.setImageBitmap(null);
}
});
}
@Override
public void onBackPressed() {
setResult(Activity.RESULT_CANCELED, getIntent());
super.onBackPressed();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment