这个Activity
是为了裁剪图片的.使用时需要提供裁剪图片的路径,以及图片裁剪后输出的路径.同时如果图片存在旋转角度也可以提供,Activity
会先将图片的旋转角度处理后再进行裁剪.
inputPath
:被裁剪图片的路径(完整路径)outputPath
:图片裁剪后输出的路径(完整路径,包括图片的格式)degree
:图片需要旋转的角度
在启动Activity
时传递以上数据即可显示并裁剪图片,图片会最终保存到指定的路径.
该Activity
仅仅只是作为一个窗口显示,实际的裁剪工作是由自定义CropView
进行裁剪的.详细请见相关文章.
其中所以有裁剪操作由CropView
完成,但是保存操作需要通过Activity
通知CropView
进行保存.
需要注意的是:
inputPath
)资源时会加载默认示例资源图片.(仅作为demo功能示例)720像素
的缩略图进行裁剪.PNG
,在具体使用该Activity
时需要修改对应的保存格式.格式为PNG时,设置质量无效
)建议使用
PNG
格式,因为当裁剪图片有透明背景存在时,裁剪后的图片依然可以保持图片的透明特性.同时裁剪图片即使是JPG
也是可以正常显示.
直接通过其静态方法调用即可.
//创建图片输出路径
String outputPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/transformBitmap.png";
//参数1:Context
//参数2:源图片路径,源图片路径为null,使用内置示例图片
//参数3:裁剪图片输出路径
//参数4:图片旋转角度(纠正图片的角度)
CropBitmapActivity.startThisActivitySelf(MainActivity.this, null, outputPath, 0);
在此Activity
中需要使用到两个图片辅助功能.其中一个是加载图片的缩略图从而达到减小内存使用的目的.
另外一个是获取图片的旋转角度并旋转图片.
这两个功能是比较实用的,特别是第一个功能.下面给出这两个功能的源码.同时在后面将会有专门的一篇文章用来收集类似的实用的功能性的代码.欢迎关注.
图片缩略图加载的本质是,获取图片的宽高大小,然后按比例加载出小尺寸的图片,从而达到减小图片占用内存的目的.
/** * 加载缩放后的图片 * * @param in 图片流数据 * @param largeSize 图片最大边的长度 * @return 返回缩放加载后的图片, 但图片的长度并不一定是最大边长度的,只是近似这个值 */
public static Bitmap decodeBitmapInScale(InputStream in, int largeSize) {
if (in == null || largeSize <= 0) {
return null;
} else {
BitmapFactory.Options options = new BitmapFactory.Options();
//仅加载图片宽高大小(不加载图片实际二进制数据)
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
//图片原始宽高
float width = options.outWidth;
float height = options.outHeight;
//缩放比例
int sampleSize = 1;
float largeSizeInBmp = width;
//记录最大的边
if (width > height) {
largeSizeInBmp = width;
} else {
largeSizeInBmp = height;
}
//将最大边与预期的边大小进行比较计算缩放比
if (largeSizeInBmp < largeSize) {
//最大边小于预期,则sampleSize为1
sampleSize = 1;
} else {
//最大边大于预期边
sampleSize = (int) (largeSizeInBmp / largeSize + 0.5);
//计算所得缩放值为2的几倍指数,即求 log2(sampleSize)
double powerNum = Math.log(sampleSize) / Math.log(2);
int tempPowerNum = (int) powerNum;
//将所得指数+1,确保尽可能小于指定值
if (powerNum > tempPowerNum) {
tempPowerNum += 1;
}
//反求sampleSize=2^tempPowerNum
sampleSize = (int) Math.pow(2, tempPowerNum);
}
//完整加载图片
options.inJustDecodeBounds = false;
//图片缩放比
options.inSampleSize = sampleSize;
//图片是否可修改
options.inMutable = true;
System.out.println("sampleSize = " + sampleSize + "\nsrcWidth = " + width + "\nsrcHeight = " + height);
return BitmapFactory.decodeStream(in, null, options);
}
}
图片旋转角度是基于图片本身记录的信息.
如果图片信息中不存在旋转角度,这里也是获取不到的
(即使图片本身确实存在旋转的情况).
以上主要是在相机拍照时使用到.部分相机拍照时会自动旋转,实际图片显示的角度与拍摄时的角度是不同的,此时就会记录在相片信息中.
但需要读取相片并重新保存为一个新文件(不作任何修改时),图片信息中的旋转角度会被删除,此时也不会再取到任何的角度旋转信息.
//读取图片属性:旋转的角度
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;
}
//旋转图片
public static Bitmap rotatingBitmap(int angle, Bitmap bitmap) {
//旋转图片动作
Matrix matrix = new Matrix();
matrix.postRotate(angle);
System.out.println("angle2=" + angle);
// 创建新的图片
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizedBitmap;
}
JAVA代码
/** * Created by taro on 16/1/11. */
public class CropBitmapActivity extends Activity implements View.OnClickListener {
private Button mBtnRightRotate;
private Button mBtnConfirm;
private Button mBtnCancel;
private CropView mCropView;
Handler mHandler = null;
ProgressDialog mDialog = null;
Bitmap mPhoto = null;
String mFilePath = null;
String mOutputPath = null;
/** * 启动此Activity * * @param act * @param srcBitmapPath 来源图片的路径 * @param outputPath 裁剪后输出的图片路径 * @param degree 图片旋转了的角度 */
public static void startThisActivitySelf(Activity act, String srcBitmapPath, String outputPath, int degree) {
Intent intent = new Intent(act, CropBitmapActivity.class);
intent.putExtra("inputPath", srcBitmapPath);
intent.putExtra("outputPath", outputPath);
intent.putExtra("degree", degree);
act.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transform_bitmap);
mBtnLeftRotate = (Button) findViewById(R.id.transform_left_rotate_btn);
mBtnRightRotate = (Button) findViewById(R.id.transform_right_rotate_btn);
mBtnConfirm = (Button) findViewById(R.id.transform_confirm_btn);
mBtnCancel = (Button) findViewById(R.id.transform_cancel_btn);
mCropView = (CropView) findViewById(R.id.transform_bitmap_cv);
mBtnLeftRotate.setOnClickListener(this);
mBtnRightRotate.setOnClickListener(this);
mBtnConfirm.setOnClickListener(this);
mBtnCancel.setOnClickListener(this);
//输入地址
mFilePath = getIntent().getStringExtra("inputPath");
//输出地址
mOutputPath = getIntent().getStringExtra("outputPath");
int degree = getIntent().getIntExtra("degree", 0);
InputStream in = null;
//不存在源图片路径时,加载默认的示例图片资源
try {
if (mFilePath == null) {
AssetManager manager = this.getAssets();
in = manager.open("pkq.png");
} else {
in = new FileInputStream(mFilePath);
}
} catch (IOException e) {
Toast.makeText(this, "无法加载图片", Toast.LENGTH_SHORT).show();
return;
}
mPhoto = decodeBitmapInScale(in, 720);
//存在旋转角度,对图片进行旋转
if (degree != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap originalBitmap = Bitmap.createBitmap(mPhoto, 0, 0, mPhoto.getWidth(), mPhoto.getHeight(), matrix, true);
mPhoto.recycle();
mPhoto = originalBitmap;
}
mCropView.setImageBitmap(mPhoto);
mDialog = new ProgressDialog(this);
mDialog.setTitle("正在处理图片...");
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0x1:
mDialog.show();
break;
case 0x2:
mDialog.dismiss();
finish();
break;
case 0x3:
mDialog.dismiss();
break;
case 0x4:
Toast.makeText(CropBitmapActivity.this, msg
.getData().getString("toast"), Toast.LENGTH_LONG).show();
break;
}
}
};
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPhoto != null && !mPhoto.isRecycled()) {
mPhoto.recycle();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.transform_confirm_btn:
//显示加载对话框
mHandler.sendEmptyMessage(0x1);
new Thread() {
@Override
public void run() {
if (mCropView.restoreBitmap(mOutputPath, Bitmap.CompressFormat.PNG, true, 50)) {
setResult(RESULT_OK);
mPhoto.recycle();
String toast = "裁剪图片保存到: " + mOutputPath;
Bundle data = new Bundle();
data.putString("toast", toast);
Message msg = Message.obtain();
msg.what = 0x4;
msg.setData(data);
mHandler.sendMessage(msg);
mHandler.sendEmptyMessageDelayed(0x2, Toast.LENGTH_LONG);
} else {
//仅取消对话框
mHandler.sendEmptyMessage(0x3);
}
}
}.start();
break;
case R.id.transform_cancel_btn:
//取消时需要回收图片资源
mCropView.recycleBitmap();
setResult(RESULT_CANCELED);
finish();
break;
}
}
/** * 加载缩放后的图片 * * @param in 图片流数据 * @param largeSize 图片最大边的长度 * @return 返回缩放加载后的图片, 但图片的长度并不一定是最大边长度的,只是近似这个值 */
public static Bitmap decodeBitmapInScale(InputStream in, int largeSize) {
if (in == null || largeSize <= 0) {
return null;
} else {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
//图片原始宽高
float width = options.outWidth;
float height = options.outHeight;
//缩放比例
int sampleSize = 1;
float largeSizeInBmp = width;
//记录最大的边
if (width > height) {
largeSizeInBmp = width;
} else {
largeSizeInBmp = height;
}
//将最大边与预期的边大小进行比较计算缩放比
if (largeSizeInBmp < largeSize) {
//最大边小于预期,则sampleSize为1
sampleSize = 1;
} else {
//最大边大于预期边
sampleSize = (int) (largeSizeInBmp / largeSize + 0.5);
//计算所得缩放值为2的几倍指数,即求 log2(sampleSize)
double powerNum = Math.log(sampleSize) / Math.log(2);
int tempPowerNum = (int) powerNum;
//将所得指数+1,确保尽可能小于指定值
if (powerNum > tempPowerNum) {
tempPowerNum += 1;
}
//反求sampleSize=2^tempPowerNum
sampleSize = (int) Math.pow(2, tempPowerNum);
}
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
options.inMutable = true;
System.out.println("sampleSize = " + sampleSize + "\nsrcWidth = " + width + "\nsrcHeight = " + height);
return BitmapFactory.decodeStream(in, null, options);
}
}
}
XML文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<LinearLayout android:id="@+id/transform_menu_lin" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" android:weightSum="2">
<Button android:id="@+id/transform_left_rotate_btn" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_weight="1" android:text="左旋" android:textSize="14sp"/>
<Button android:id="@+id/transform_right_rotate_btn" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_weight="1" android:text="右旋" android:textSize="14sp"/>
</LinearLayout>
<LinearLayout android:id="@+id/transform_operation_lin" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:weightSum="2">
<Button android:id="@+id/transform_cancel_btn" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_weight="1" android:text="取消" android:textSize="14sp"/>
<Button android:id="@+id/transform_confirm_btn" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_weight="1" android:text="确定" android:textSize="14sp"/>
</LinearLayout>
<com.henrytaro.ct.ui.CropView android:id="@+id/transform_bitmap_cv" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@id/transform_operation_lin" android:layout_below="@id/transform_menu_lin" android:layout_centerInParent="true"/>
</RelativeLayout>
https://github.com/CrazyTaro/TouchEventHandle
回到目录