您的位置:首页 > 移动开发 > Android开发

Android对图片常用(拍照,图库,拖拉,压缩上传)操作

2016-11-13 21:47 597 查看
本篇博客内容:

android app拍照功能

部分手机上拍照图片旋转问题

android图库获取图片功能

查看图片,进行缩放,拖拉功能

压缩图片,上传功能

Android 拍照功能:

一般的拍照功能是调用系统中相机来实现,即通过Intent来调用其他运用程序(相机运用程序)来实现。众所周知,通过Intent调用手机上的其他运用程序,都应该先检查手机上是否装有能Intent开启的其他程序。代码如下:

//MediaStore.ACTION_IMAGE_CAPTURE开启手机中相机
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//检查手机上是否装有Intent对应开启的程序
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, requestCode);
}
}


从代码可知,调用开启相机是通过startActivityForResult(), 那返回的是什么呢?

一个压缩后的Bitmap,还是一个图片的path. ?以上代码产生的是一个系统压缩后返回的Bitmap。在onActivityResult()中Intent#Bundle,Bundle中data(key)对应的值(value).

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
mImageView.setImageBitmap(imageBitmap);
}
}


一个系统压缩后的Bitmap实际作用不大,适合做一个图标。 若是需要按尺寸来加载图片,然后将图片压缩上传,利用以上代码是完全行不通的。 这时候,便需要知道相机拍照产生的原始图片的Path,根据path来实现项目需求。

实际开发中,会指定图片的路径,即图片产生到指定文件下,且指定图片格式为.png 。将图片存储路径通过Intent#putExtra()告诉系统。

private String imagePath1;
/**
* 打开相机
*/
public void openCamera(int requestCode) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getPackageManager()) != null) {
File bitmapCacheFile = BitmapUtils.getBitmapDiskFile(BaseApplication.getAppContext());
if (bitmapCacheFile != null) {
imagePath1 = bitmapCacheFile.getAbsolutePath();
//指定拍照存储路径
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(bitmapCacheFile));
startActivityForResult(intent, requestCode);
}
}
}

/**
* 获得存储bitmap的文件
* getExternalFilesDir()提供的是私有的目录,在app卸载后会被删除
*
* @param context
* @param
* @return
*/
public static File getBitmapDiskFile(Context context) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {

cachePath = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES).getPath();
} else {
cachePath = context.getFilesDir().getPath();
}
return new File(cachePath + File.separator + getBitmapFileName());
}

public static final String bitmapFormat = ".png";

/**
* 生成bitmap的文件名:日期,md5加密
*
* @return
*/
public static String getBitmapFileName() {
StringBuilder stringBuilder = new StringBuilder();
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
String currentDate = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
mDigest.update(currentDate.getBytes("utf-8"));
byte[] b = mDigest.digest();
for (int i = 0; i < b.length; ++i) {
String hex = Integer.toHexString(0xFF & b[i]);
if (hex.length() == 1) {
stringBuilder.append('0');
}
stringBuilder.append(hex);
}
} catch (Exception e) {
e.printStackTrace();
}
String fileName = stringBuilder.toString() + bitmapFormat;
return fileName;
}


向sd-card写入信息,权限是少不了的:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


在onActivityResult中获取拍照成功的标示,这里系统将不会返回有任何数据,这时候需要自己开启异步线程去按适屏计算加载图片:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CAREMA) {
if (resultCode == RESULT_OK) {
//开启工作线程去加载,拍照产生的图片
new Thread(loadBitmapRunnable).start();
}
}
}

/**
* 根据path路径,找到file,从而生成bitmap
*/
private Runnable loadBitmapRunnable = new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
try {
Thread.sleep(500);
Bitmap bitmap = BitmapUtils.decodeFileCreateBitmap(imagePath1
, iv.getWidth(), iv.getHeight());
handler.obtainMessage(CAREMA, bitmap).sendToTarget();
} catch (Exception e) {
e.printStackTrace();
}
}
};

public synchronized static Bitmap decodeFileCreateBitmap(String path, int targetWith, int targerHeight) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = calculateScaleSize(options, targetWith, targerHeight);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
return getNormalBitamp(bitmap, path);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
* 采用向上取整的方式,计算压缩尺寸
*
* @param options
* @param targetWith
* @param targerHeight
* @return
*/
private static int calculateScaleSize(BitmapFactory.Options options, int targetWith, int targerHeight) {
int simpleSize;
if (targetWith > 0&&targerHeight>0) {
int scaleWith = (int) Math.ceil((options.outWidth * 1.0f) / targetWith);
int scaleHeight = (int) Math.ceil((options.outHeight * 1.0f) / targerHeight);
simpleSize = Math.max(scaleWith, scaleHeight);
} else {
simpleSize = 1;
}
return simpleSize;
}


高效和快速加载图片,请参考 Android高效加载Bitmap

部分手机上回遇到图片旋转问题,这时候需要使用到ExifInterface这类,手动回复图片原本方向:

/**
* 根据存储的bitamp中旋转角度,来创建正常的bitamp
*
* @param bitmap
* @param path
* @return
*/
public static Bitmap getNormalBitamp(Bitmap bitmap, String path) {
int rotate = getBitmapRotate(path);
Bitmap normalBitmap=null;
switch (rotate) {
case 90:
case 180:
case 270:
try {
Matrix matrix = new Matrix();
matrix.postRotate(rotate);
normalBitmap = bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth()
, bitmap.getHeight(), matrix, true);
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
} catch (Exception e) {
e.printStackTrace();
normalBitmap=bitmap;
}
break;
default:
normalBitmap=bitmap;
break;
}
return normalBitmap;
}

/**
* ExifInterface :这个类为jpeg文件记录一些image 的标记
* 这里,获取图片的旋转角度
*
* @param path
* @return
*/
public static int getBitmapRotate(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 (Exception e) {
e.printStackTrace();
}
return degree;
}


最后创建一个主线程捆绑的Handler,进行加载适屏处理后的bitmap:

private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == CAREMA) {
if (msg.obj instanceof Bitmap) {
iv.setImageBitmap((Bitmap) msg.obj);
}
}
return false;
}
});


拍照效果如下:

1.选择拍照功能:



2.相机运用进行拍照:



3.加载拍照产生的图片:



android图库获取图片功能

获取图库中相片,也是通过Intent来开启图库运用来实现的。

/**
* 打开图库
*/
public void openMapStorage(int requestCode) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, requestCode);
}
}


在 onActivityResult中接收图库返回的信息,这里是返回一个Uri。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PHOTO) {
if (resultCode == RESULT_OK) {
uri = data.getData();
new Thread(loadUriCreateBitmapRunnable).start();
}
}
}


根据Uri来查询图库的数据库,找到图片路径,从而加载图片:

/**
* 根据Uri,查询到path,找到对应的file,生成bitmap
*/
private Runnable loadUriCreateBitmapRunnable = new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Cursor cursor = null;
try {
Thread.sleep(500);
if (uri != null) {
cursor = getContentResolver().query(uri,
new String[]{MediaStore.Images.Media.DATA}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
imagePath1 = cursor.getString(
cursor.getColumnIndex(MediaStore.Images.Media.DATA));
if (imagePath1 != null) {
Bitmap bitmap = BitmapUtils.decodeFileCreateBitmap(imagePath1,
iv.getWidth(), iv.getHeight());
handler.obtainMessage(CAREMA, bitmap).sendToTarget();

}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
};


最后创建一个主线程捆绑的Handler,进行加载图库选中的bitmap:

private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == CAREMA) {
if (msg.obj instanceof Bitmap) {
iv.setImageBitmap((Bitmap) msg.obj);
}
}
return false;
}
});


效果如下:

1.在图库中选中图片:



2.加载选中的图片 :



android 查看图片,进行缩放,拖拉功能

1.首先加载适屏的图片,通过设置Matrix 来,放置到中间:

private Runnable loadBitmapRunnable = new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
try {
Thread.sleep(500);
Bitmap bitmap = BitmapUtils.decodeFileCreateBitmap(path
, toucher_iv.getWidth(), toucher_iv.getHeight());
handler.obtainMessage(0, bitmap).sendToTarget();
} catch (Exception e) {
e.printStackTrace();
}
}
};
//记录当前图片的Matrix
private Matrix beforeMatrix = new Matrix();

private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 0) {
bitmap = (Bitmap) msg.obj;

if (bitmap == null) {
return false;
}
float y = ((getPhoneMetrics(BaseApplication.getAppContext()).heightPixels -
bitmap.getHeight()) / 2);
if (y > 0) {
beforeMatrix.setTranslate(0, y);
toucher_iv.setImageMatrix(beforeMatrix);
}
toucher_iv.setImageBitmap(bitmap);
}
return false;
}
});


2.进行设置缩放,和拖拉的功能代码:

先实现 View.OnTouchListener类,复写onTouch(View v, MotionEvent event)方法。

//记录当前图片的Matrix
private Matrix beforeMatrix = new Matrix();
//改变状态后的Matrix
private Matrix changeMatrix = new Matrix();
//记录当前图片处于的状态
private int currentState = 0;//当前状态
private final static int MOVE = 1;//拖动
private final static int SCALLE = 2;//缩放
//记录两个坐标(float类型)
private PointF before_PointF = new PointF();
//记录下一开两个手指间的距离:
private float before_Distance;
//记录下开始时,两个手指间的中心点
private PointF midPointf;

/**
* 1.拖动分析:
* 在原本Matrix基础上添加移动的x,y距离,实现拖动
* 做法:先记录开始的Matrix1,
* 然后计算移动的x,y,
* 创建一个以Matrix1为基础的新的Matrix2,添加移动的x,y
* 图片设置新的Matrix2
* <p/>
* <p/>
* 2.缩放分析:
* 先记录中心点(缩放,旋转都需要用到),原本的Matrix,原本手指间距离,通过距离比率来设置
*
* @param v
* @param event
* @return
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
//手指压下屏幕
case MotionEvent.ACTION_DOWN:
currentState = MOVE;
beforeMatrix.set(toucher_iv.getImageMatrix());
before_PointF.set(event.getX(), event.getY());
break;
//手指在滑动
case MotionEvent.ACTION_MOVE:
if (currentState == MOVE) {//拖动状态
float distance_x = event.getX() - before_PointF.x;
float ditance_y = event.getY() - before_PointF.y;
changeMatrix.set(beforeMatrix);
changeMatrix.postTranslate(distance_x, ditance_y);
} else if (currentState == SCALLE) {//缩放状态
float endDistance = distance(event);
if (endDistance > 10f) {
float scale = endDistance / before_Distance;
changeMatrix.set(beforeMatrix);
changeMatrix.postScale(scale, scale, midPointf.x, midPointf.y);
}
}
break;
//手指离开触摸
case MotionEvent.ACTION_UP:
currentState = 0;
break;
//多一个手指在触摸
case MotionEvent.ACTION_POINTER_DOWN:
currentState = SCALLE;
//开始时,两个手指间距离
before_Distance = distance(event);
if (before_Distance > 10f) {
midPointf = mid(event);
beforeMatrix.set(toucher_iv.getImageMatrix());
}
break;
//还有手指在触摸
case MotionEvent.ACTION_POINTER_UP:
currentState = 0;
break;
}
toucher_iv.setImageMatrix(changeMatrix);
return true;
}

/**
* 计算两个手指间的距离
*/
private float distance(MotionEvent event) {
float dx = event.getX(1) - event.getX(0);
float dy = event.getY(1) - event.getY(0);
/** 使用勾股定理返回两点之间的距离 */
return (float) Math.sqrt(dx * dx + dy * dy);
}

/**
* 计算两个手指间的中间点
*/
private PointF mid(MotionEvent event) {
float midX = (event.getX(1) + event.getX(0)) / 2;
float midY = (event.getY(1) + event.getY(0)) / 2;
return new PointF(midX, midY);
}


效果如下:

这里没有制作gif来显示效果,感兴趣的可以下载项目来体验效果:



android 压缩图片

现在的手机拍照产生的相片都了几M,甚至可能是5M多,上传到服务器是不会上传那么大的图片的。这边需要用到压缩。

压缩图片一般通过Bitmap#compress()来实现的。 一般都是通过for循环来计算最小压缩体积:

这里是将Bitmap按最小体积量来压缩成byte[]来上传的。

public static byte[] bitmapCompressToByteArray(Bitmap bitmap, int bitmapSize, boolean isRecycle) {

int quality = 100;//范围0~100
ByteArrayOutputStream byteArrayOutputStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG,quality,byteArrayOutputStream);
while (byteArrayOutputStream.toByteArray().length/1024>bitmapSize){
if(quality<=10){
break;
}
byteArrayOutputStream.reset();
quality-=10;
bitmap.compress(Bitmap.CompressFormat.PNG,quality,byteArrayOutputStream);
}
byte[] b = byteArrayOutputStream.toByteArray();
if (isRecycle) {
bitmap.recycle();
}

return  b;
} catch (Exception e) {
e.printStackTrace();
return  null;
} finally {
try {
if (byteArrayOutputStream != null) {
byteArrayOutputStream.close();
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
}


经过测试发现,若是一个几M的图片进行这样的压缩处理,时间花费是很久的,甚至可能是几分钟时间。

这里的的处理方式是:按上传的体积量来计算出图片的分辨率,按这个分辨率来加载适合的图片,然后将这个图片再来编码成byte[]

/**
* 将bitmap编码成byte[]
*/
private Runnable bitmapEndecodeByteRunnable = new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
try {
//这里是250*250大小的图片
byte[] b = BitmapUtils.bitmapCompressToByteArray(
BitmapUtils.decodeFileCreateBitmap(imagePath1
, 250, 250), 200, true);
handler.obtainMessage(FILEUPLOAD, b).sendToTarget();
} catch (Exception e) {
e.printStackTrace();
}
}
};


最后将图片对应的byte[]通过volley中自定义的MultiPartRequest上传:

private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == FILEUPLOAD) {
byte[] b = (byte[]) msg.obj;
if (b != null) {
sendFileUploadRequest(b);
}
}
return false;
}
});

/**
* 将bitmap编码成byte[],然后上传到服务器
*
* @param bitmap
*/
public void sendFileUploadRequest(byte[] bitmap) {
System.out.print("开始上传");
MultiPartRequest<JsonBean> request = new MultiPartRequest<>(Request.Method.POST, "http://192.168.1.102:8080/SSMProject/file/fileUpload", JsonBean.class, new Response.Listener<JsonBean>() {
@Override
public void onResponse(JsonBean jsonBean) {
path_tv.setText("bitmap存储在:"+jsonBean.path);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(MultiPartRequestActivity.this, volleyError.getMessage(), Toast.LENGTH_LONG).show();
}
});
request.addFile(bitmap);

request.setRetryPolicy(new DefaultRetryPolicy(50000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
request.setTag(TAG);
VolleySingleton.getInstance().addToRequestQueue(request);
}


效果图在 Volley源码分析之自定义MultiPartRequest(文件上传)案例中

项目代码:http://download.csdn.net/detail/hexingen/9681762

相关知识点:

Volley源码分析之自定义MultiPartRequest(文件上传)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: