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

android选取本地图片及关于图片压缩上传问题

2016-05-10 22:18 567 查看
<span style="font-size:18px;">在一个项目里面,有一个需求是让用户自己选择图片,然后上传到服务器。看似一个很简单的需求,就是选择图片,把图片装好,然后通过网络请求上传到后台,OK。但是事实并非如此,因为我们可以android项目,他是open的,他有更多的可能性,当然你也会遇到更多古灵精怪的问题。</span>
获取图片有3种方法,一是自己用surface控件,利用镜头来获取图片;二是调用系统相机,并且返回拍到的图片;三是直接在利用图库获取本地图片;这里我只使用后面两种方法来获取图片(系统提供了方法,为什么不用呢,还要费那么大劲去开发一个新的已经有了的功能,不重复造轮子)。在这过程中,我遇到了好几个很奇葩和让人难以理解的问题。弄了我大半天时间,现在做一下记录。我所遇到的问题大概有以下:1、利用系统图库获取图片,返回的图片地址,因系统不同而不同;2、利用拍照返回的照片是经过压缩的,分辨率很低,压根看不清楚;3、第一个问题:返回的图片地址不同。为什么这样讲呢,因为我们知道android已经有很多的深度定制的系统,像是小米,华为,魅族,锤子...等等,都对原生的android系统作了修改优化,至少我不知道它们能在底层改了什么东西,我们只有直面底层的返回,去适应它。在这里,我们利用显示intent方式打开本地图库,代码如下Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);//设置动作
<span style="font-size:18px;"><span style="white-space:pre"></span>intent.setType("image/*");//开启Pictures画面Type设定为image
<span style="white-space:pre"></span>startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE1);</span>
这样就可以打开系统的图库来选去需要的图片了。那么,问题就来了。我在魅蓝2上面打开后,系统跳到的是最近的拍照照片,而在小米2s上面也是跳到最近的拍摄的图片集合里面去,在华为honor6 ?上面则是跳到最近编辑过的图片集合里面去了。最后在onactivityresult()回调方法里面利用以下代码得到图片地址:
<span style="font-size:18px;"><span style="white-space:pre"></span>Uri uri = data.getData();
String pathImg = uri.getPath();</span>
魅蓝获取到2获取到的图片路径是:/external/images/media/640543,小米2s的路径是:/storage/sdcard0/DCIM/Camera/IMG_20160217_142658.jpg,华为的类似魅蓝2的路径,这里没有打印出来。在这里我要的是选取图片的完整路径,如果不用不需要完整路径,也是可以获取到图片的,代码如下:
Bitmap bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri)); 这个方法返回来的是一个bitmap,一看到bitmap应该立即想到的是oom,这是一个程序猿又爱又恨的东西啊。现在的手机随便拍个照,都有1000(+)*1000(+)的像素,在android里面一个像素点,用4个字节存放。所以,读入一个1000*1000像素的图片,读入来,占用的字节数有1000*1000*4.这对一个程序来说,几乎是致命的。这样的做法显然不可取。
在上面返回的路径看来,小米2s返回的路径是对的,但是我的代码还要在魅蓝上运行,显然也是不通过的。所以我又找了另外一个办法来获取返回的图片路径:
String picturePath = "";
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(uri, proj, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndex(proj[0]);
picturePath = cursor.getString(columnIndex);
System.out.println("-=-==->>picturePath = " + picturePath);
}
此方法,在华为honor6?上面返回的的cursor为null,魅蓝上是OK的,小米的也是null。这就让人很费解了。同样是显示调用系统的方法,但是返回来的却是截然不同的东西。而我的目标是要不oom和完整的路径。
所以在这里,我用了一个猥琐的方法来绕过这个坑。就是我同时用两种方法来获取两个路径,然后判断哪一个路径是不为null,并且带有图片格式后缀的路径,就拿来用。
这里的用,是压缩图片后,再将压缩图片读进内存来。以下是我的方法:
</pre>
/**
* 根据图片路径,得到压缩过的位图
*
* @param path
* @param width
* @param height
* @return
*/
public Bitmap getPressedBitmap(String path, int width, int height) {
BitmapFactory.Options options = new BitmapFactory.Options();//new一个options
options.inJustDecodeBounds = true;//先设置为true,即不读入图片到内存,先获取图片的信息,比如长宽等信息
Bitmap bitmap = BitmapFactory.decodeFile(path, options);//此句代码是真正的去读取图片的长宽等信息,并且存储在options里面,在后面的代码中我们可以看到options.outWidth 和 options.outHeight得到的是图片的宽和高。这句代码所以不能没有,否则无法压缩图片。这里得到的bitmap是为null
options.inSampleSize = getBitmapSampleSize(options, width, height)
;//根据给定的宽高来压缩图片的比例        options.inJustDecodeBounds = false;//设置为false,是要将图片以一定比例压缩后读入内存中        
<span style="white-space:pre"></span>Bitmap bitmap1 = BitmapFactory.decodeFile(path, options);//
这里得到的bitmap才是不为null
return bitmap1;}    
<pre name="code" class="java">/**
* 根据要去的宽高,压缩图片
*
* @param options
* @param reqWidth
* @param reqHeight
* @return
*/
public int getBitmapSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
int imgWidth = options.outWidth;
int imgHeight = options.outHeight;
int inSimpleSize = 1;
if (imgWidth > imgHeight || imgWidth < imgHeight) {
final int heightRatio = imgWidth / reqWidth;
final int widthRatio = imgHeight / reqHeight;
inSimpleSize = widthRatio < heightRatio ? widthRatio : heightRatio;
}
return inSimpleSize;
}
最后,我将图片选取好,上传到后台,发现图片的像素很低,几乎看不清上传的是什么,这个也是不符合需求的。所以要修改。
我发现把options.inSampleSize = 5;//getBitmapSampleSize(options, width, height)改成这样子,inSampleSize改为你觉得可以看清楚的int就OK了。(这里提醒一下下,也要注意读取进来的bitmap的大小,图片也不能太大,因为上传是耗费流量的,我们一定要站在用户的角度去想问题)
最后,问题实际上解决了,但是理论上还没有,想不通,怎么会我也没有时间去一一对看源码。等找个空时间,再去扒源码。
对于第二个问题:利用拍照返回的照片是经过压缩的,分辨率很低,压根看不清楚。我们用显式intent来打开系统摄像头,然后回传图片信息,代码如下:
bundle = data.getExtras();
Bitmap bitmap = (Bitmap) bundle.get("data");// 获取相机返回的数据,并转换为Bitmap图片格式
可以看到,调用系统相机拍照所返回的图片数据,是放到bundle里面的。我将bitmap拿出来,发现像素也是低到不行,根本没办法看,原来返回的是压缩得不行不行的图片。这当然也不行了。发现拍照的图片也没有保存在本地,不能重复使用那张图片。
既然这样,我就查资料,想着系统应该会有方法设置可以返回原图的,但是我暂时没有找到。然后看到另外一个方法,先把拍照的照片存在指定的位置,然后利用指定位置去获取原图,压缩后,在读取进来。
一开始在启动调用系统相机的时候,指定照片存储的位置。
// 利用系统自带的相机应用:拍照Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);File filePath = getTakePhotoPath();//得到图片文件存储的路径Uri imgUri = null;imgUri = Uri.fromFile(filePath);cacheImgPath = filePath.getAbsolutePath();//将图片的路径保存起来intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);//把路径传给系统,系统会自动存储到你指定的路径下getParent().startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE2);
/*** 返回一个存储拍照的路径** @return path*/private File getTakePhotoPath() {SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");Date date = new Date(System.currentTimeMillis());String fileName = format.format(date);File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);String imgPath = file.getAbsolutePath() + "/test/";File fileDcim = new File(imgPath);if (!fileDcim.exists()) {fileDcim.mkdirs();}File filePicPath = new File(fileDcim, "SBD_" + fileName + ".jpg");if (!filePicPath.exists()) {try {filePicPath.createNewFile();} catch (IOException e) {e.printStackTrace();}}String path = filePicPath.getAbsolutePath();if (path == null) {return null;} else {return filePicPath;}}
在onactivityresult()回调中,利用保存起来的的路径,像上面一样获取本地图片的压缩图就OK了。
当然,在做以上操作时,千万别忘了要添加相应的权限。
以上就是我个人对这个问题的看法和解决方法,如果发现有何不对,或者有更好的方法解决问题,请赐教!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: