Android触摸事件(五)-CropBitmapActivity关于裁剪工具的使用
文件夹
概述
这个Activity是为了裁剪图片的.使用时须要提供裁剪图片的路径,以及图片裁剪后输出的路径.同一时候假设图片存在旋转角度也能够提供,Activity会先将图片的旋转角度处理后再进行裁剪.
传递数据
inputPath:被裁剪图片的路径(完整路径)outputPath:图片裁剪后输出的路径(完整路径,包含图片的格式)degree:图片须要旋转的角度
在启动Activity时传递以上数据就可以显示并裁剪图片,图片会终于保存到指定的路径.
裁剪流程
该Activity仅仅仅仅是作为一个窗体显示,实际的裁剪工作是由自己定义CropView进行裁剪的.具体请见相关文章.
当中所以有裁剪操作由CropView完毕,可是保存操作须要通过Activity通知CropView进行保存.
其他事项
须要注意的是:
- 当不存在源图片(
inputPath)资源时会载入默认演示样例资源图片.(仅作为demo功能演示样例) - 默认载入图片的
720像素的缩略图进行裁剪. - 裁剪对象理论上能够是不论什么图片对象,可是考虑到内存因此建议仅仅使用缩略图.
- 保存时默认使用的格式为
PNG,在具体使用该Activity时须要改动相应的保存格式. - 保存时须要指定图片质量,从0-100,默觉得50(
格式为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 reqSize 预期图片最长边同意的最大值
* @return 返回缩放载入后的图片, 但图片的最大边长度并不一定等于reqSize,仅仅是近似并小于这个值
*/
public static Bitmap decodeBitmapInScale(String filePath, int reqSize) {
if (TextUtils.isEmpty(filePath) || reqSize <= 0) {
return null;
} else {
BitmapFactory.Options options = new BitmapFactory.Options();
//仅载入图片宽高大小(不载入图片实际二进制数据)
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
if (reqSize <= 0) {
throw new RuntimeException("预期边长不可小于0");
}
float bmpWidth = options.outWidth;
float bmpHeight = options.outHeight;
float largeSizeInBmp = 0;
int sampleSize = 1;
//记录最大的边
if (bmpWidth > bmpHeight) {
largeSizeInBmp = bmpWidth;
} else {
largeSizeInBmp = bmpHeight;
}
//将最大边与预期的边大小进行比較计算缩放比
if (largeSizeInBmp < reqSize) {
//最大边小于预期,则sampleSize为1
sampleSize = 1;
} else {
//最大边大于预期边
sampleSize = (int) (largeSizeInBmp / reqSize + 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;
return BitmapFactory.decodeFile(filePath, 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 mBtnLeftRotate;
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;
//不存在源图片路径时,载入默认的演示样例图片资源
if (mFilePath == null) {
mPhoto = decodeBitmapInScale(getResources(), R.raw.pkq, 720);
} else {
mPhoto = decodeBitmapInScale(mFilePath, 720);
}
//存在旋转角度,对图片进行旋转
if (degree != 0) {
//旋转图片
Bitmap originalBitmap = rotatingBitmap(degree, mPhoto);
//回收旧图片
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 filePath 图片路径
* @param reqSize 预期图片最长边同意的最大值
* @return 返回缩放载入后的图片, 但图片的最大边长度并不一定等于reqSize,仅仅是近似并小于这个值
*/
public static Bitmap decodeBitmapInScale(String filePath, int reqSize) {
if (TextUtils.isEmpty(filePath) || reqSize <= 0) {
return null;
} else {
BitmapFactory.Options options = new BitmapFactory.Options();
//仅载入图片宽高大小(不载入图片实际二进制数据)
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
if (reqSize <= 0) {
throw new RuntimeException("预期边长不可小于0");
}
float bmpWidth = options.outWidth;
float bmpHeight = options.outHeight;
float largeSizeInBmp = 0;
int sampleSize = 1;
//记录最大的边
if (bmpWidth > bmpHeight) {
largeSizeInBmp = bmpWidth;
} else {
largeSizeInBmp = bmpHeight;
}
//将最大边与预期的边大小进行比較计算缩放比
if (largeSizeInBmp < reqSize) {
//最大边小于预期,则sampleSize为1
sampleSize = 1;
} else {
//最大边大于预期边
sampleSize = (int) (largeSizeInBmp / reqSize + 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;
return BitmapFactory.decodeFile(filePath, options);
}
}
/**
* 载入缩放后的图片
*
* @param res
* @param resID 资源ID
* @param reqSize 预期图片最长边同意的最大值
* @return 返回缩放载入后的图片, 但图片的最大边长度并不一定等于reqSize,仅仅是近似并小于这个值
*/
public static Bitmap decodeBitmapInScale(Resources res, int resID, int reqSize) {
if (res == null || resID == 0 || reqSize <= 0) {
return null;
} else {
BitmapFactory.Options options = new BitmapFactory.Options();
//仅载入图片宽高大小(不载入图片实际二进制数据)
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resID, options);
if (reqSize <= 0) {
throw new RuntimeException("预期边长不可小于0");
}
float bmpWidth = options.outWidth;
float bmpHeight = options.outHeight;
float largeSizeInBmp = 0;
int sampleSize = 1;
//记录最大的边
if (bmpWidth > bmpHeight) {
largeSizeInBmp = bmpWidth;
} else {
largeSizeInBmp = bmpHeight;
}
//将最大边与预期的边大小进行比較计算缩放比
if (largeSizeInBmp < reqSize) {
//最大边小于预期,则sampleSize为1
sampleSize = 1;
} else {
//最大边大于预期边
sampleSize = (int) (largeSizeInBmp / reqSize + 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;
return BitmapFactory.decodeResource(res, resID, options);
}
}
/**
* 旋转图片
*
* @param angle
* @param bitmap
* @return Bitmap
*/
public static Bitmap rotatingBitmap(int angle, Bitmap bitmap) {
if (bitmap == null || bitmap.isRecycled()) {
return null;
}
//旋转图片 动作
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;
}
}
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>
GitHub地址
演示样例GIF
Android触摸事件(五)-CropBitmapActivity关于裁剪工具的使用的更多相关文章
- Android触摸事件传递机制
简单梳理一下Android触摸事件传递机制的知识点. 一.View与ViewGroup的关系 View和ViewGroup二者的继承关系如下图所示: View是Android中最基本的一种UI组件,它 ...
- iOS 和 Android 触摸事件传递
先看文章,写得很好 ios 触摸事件传递 http://www.cnblogs.com/Quains/p/3369132.html 另外一篇 http://blog.csdn.net/yongyinm ...
- 对于android触摸事件模型的一些理解
body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...
- 初识Android触摸事件传递机制
前言 今天总结的一个知识点是Andorid中View事件传递机制,也是核心知识点,相信很多开发者在面对这个问题时候会觉得困惑,另外,View的另外一个难题滑动冲突,比如在ScrollView中嵌套Li ...
- Android触摸事件的应用
前言 上一篇讲了Android触摸事件的传递机制,具体可以看这里 初识Android触摸事件传递机制.既然知道Android中触摸事件的传递分发,那么它能解决什么样的问题,在我们实际开发中如何应用,这 ...
- Android触摸事件流程剖析
Android中的触摸事件流程就是指MotionEvent如何传递,主要包括两个阶段: onInterceptTouchEvent触摸事件拦截方法传递,从外到里传递 onTouchEvent触摸事件处 ...
- 一个demo让你彻底理解Android触摸事件的并发
注:本文涉及的demo的地址:https://github.com/absfree/TouchDispatch 1. 触摸动作及事件序列 (1)触摸事件的动作 触摸动作一共有三种:ACTION_DOW ...
- 图解Android触摸事件分发
Android中触摸事件传递过程中最重要的是dispatchTouchEvent().onInterceptTouchEvent()和onTouchEvent()方法. View和Activity有d ...
- android自定义控件(9)-Android触摸事件分发机制
触摸事件的传递机制: 首先是最外层的viewgroup接收到事件,然后调用会调用自己的dispatchTouchEvent方法.如果在ACTION_DOWN的时候dispatchTouchEven ...
随机推荐
- legend---六、php脚本变量的生命周期是怎样的
legend---六.php脚本变量的生命周期是怎样的 一.总结 一句话总结:应该是脚本结束变量的生命周期就完了 1.外部js找不到元素是怎么回事? 1 function myDailyTaskFin ...
- NET 高效开发之不可错过的实用工具(第一的当然是ReSharper插件)
工欲善其事,必先利其器,没有好的工具,怎么能高效的开发出高质量的代码呢?本文为 ASP.NET 开发者介绍一些高效实用的工具,包括 SQL 管理,VS插件,内存管理,诊断工具等,涉及开发过程的各个环节 ...
- SQL Server 2008 R2 超详细安装图文教程及问题解决(锐姿公司安装)
问题点: 1.为了sqlserver与mysql 的安全,建议数据库低权限运行.禁止远程访问 1433与 3306端口等. 2.安装提示.net 3.5没有安装 ,在server2012的添加 3. ...
- C/C++(C++封装)
封装 当单一变量无法完成描述需求的时候,结构体类型解决了这一问题.可以将多个类型打包成一体,形成新的类型.这是 c 语言中封装的概念.但是,新类型并不包含,对数据类的操作.所的有操作都是通过函数的方式 ...
- 学习参考《矩阵分析与应用(第二版)张贤达》PDF
要想深入理解机器学习,或者对人工智能的某个领域有所研究,都必须掌握矩阵及其应用. 学习<矩阵分析与应用第2版>时,会发现总结了大量线性代数的知识,主要是给工科生用的.归纳了不少论文中的解法 ...
- C# 对Excel操作时,单元格值的读取
一.Range中Value与Value2的区别 当range("A1:B10")设置为 Currency (货币)和 Date (日期.日期时间)数据类型时,range2将返回对应 ...
- 懒加载js实现和优化
1.懒加载的作用和原理 在我们展示多图片的场景下,类似淘宝或者百度图片,由于图片的数目过多,全部从服务器请求会给用户糟糕的用户体验,为了提升用户体验,我们这里使用懒加载,随着下拉逐步加载. 每个图片的 ...
- 小项目: low版本的 员工信息程序:
### 附加两个文件1 user_info 和worker_info flag = False def logon(): #登录函数 global flag usr = input('Username ...
- 完全背包模板 51Nod 1101
N元钱换为零钱,有多少不同的换法?币值包括1 2 5分,1 2 5角,1 2 5 10 20 50 100元. 例如:5分钱换为零钱,有以下4种换法: 1.5个1分 2.1个2分3个1分 3.2个2分 ...
- 玲珑学院 1052 - See car
1052 - See car Time Limit:2s Memory Limit:64MByte Submissions:594Solved:227 DESCRIPTION You are the ...