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

编程入门 行业动态 更新时间:2024-10-18 20:24:00

Android对图片常用(拍照,图库,<a href=https://www.elefans.com/category/jswz/34/1379206.html style=拖拉,压缩上传)操作"/>

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

本篇博客内容:

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

 @Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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。

@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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();}}};  //记录当前图片的Matrixprivate Matrix beforeMatrix = new Matrix();private Handler handler = new Handler(new Handler.Callback() {@Overridepublic 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)方法。

 //记录当前图片的Matrixprivate Matrix beforeMatrix = new Matrix();//改变状态后的Matrixprivate 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*/@Overridepublic 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~100ByteArrayOutputStream byteArrayOutputStream = null;try {byteArrayOutputStream = new ByteArrayOutputStream();bitmappress(Bitmap.CompressFormat.PNG,quality,byteArrayOutputStream);while (byteArrayOutputStream.toByteArray().length/1024>bitmapSize){if(quality<=10){break;}byteArrayOutputStream.reset();quality-=10;bitmappress(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() {@Overridepublic 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() {@Overridepublic 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>() {@Overridepublic void onResponse(JsonBean jsonBean) {path_tv.setText("bitmap存储在:"+jsonBean.path);}}, new Response.ErrorListener() {@Overridepublic 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(文件上传)案例中

项目代码:

相关知识点:

  • Volley源码分析之自定义MultiPartRequest(文件上传)

更多推荐

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

本文发布于:2024-02-17 10:44:15,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1693716.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:拖拉   常用   图库   上传   操作

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!