前言:做了好久的安卓开发了,一直想写点东西分享下。但是又总觉得自己学的还不够好,说出来有可能会误导人,所以一直都没有发. 最近在项目中遇到了最多的问题就是关于图片的问题,应该算是比较简单的了,拿出来跟大家分享下。(第一次写博客,希望给位大神能够多提意见^_^)
最近做的项目需要上传手机相册的图片或者通过相机拍照上传,但是以前从网上找的方法需要在拍照或选择完图片后再进行截取,不太符合现在的需求,于是从网上找了一些方法,但是莫名其妙的出了各种问题,于是各种百度,从网上找了一些方法,拿出来分享下,并方便自己以后的使用。
/**
* 开启相机
* @param actionCode 请求码
* /
private void getImageFromCamera(int actionCode) {
//这种方法是我们最常见的方法,但是这种方法获取到的图片时是进行压缩后的图片,有可能不是我们想要的
//可能压缩后的文件会非常模糊
Intent getImageByCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(getImageByCamera , actionCode);
}
上面的那种方法可能获得的压缩图片不是我们想要,可能我们会想要高清大图,这时我们可以使用下面的方法,在
Intent getImageByCamera = new Intent(“android.media.action.IMAGE_CAPTURE”);
之后我们直接将文件先保存到指定的路径filepath,然后直接在
onActivityResult(int requestCode, int resultCode, Intent data)
中把filepath传递过去就行了。方法如下
//定义一个成员变量,用于保存相机拍照的图片
private String capturePath = null;
/**
* 开启相机
* @param actionCode 请求码
*/
protected void getImageFromCamera(int actionCode) {
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
Intent getImageByCamera = new Intent("android.media.action.IMAGE_CAPTURE");
//获取保存的路径
String out_file_path = getSDPath();
File dir = new File(out_file_path);
//给成员变量赋值
capturePath = getSDPath() + "/" + System.currentTimeMillis() + ".jpg";
if (!dir.exists()) {
dir.mkdirs();
}
getImageByCamera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(capturePath)));
getImageByCamera.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
startActivityForResult(getImageByCamera, actionCode);
} else {
Toast.makeText(getApplicationContext(), "请确认已经插入SD卡", Toast.LENGTH_LONG).show();
}
}
/**
* 获取存储路径,可以写在FileUtils中
*/
public String getSDPath() {
File sdDir = null;
boolean sdCardExist = Environment.getExternalStorageState()
.equals(android.os.Environment.MEDIA_MOUNTED);//判断sd卡是否存在
if (sdCardExist) {
sdDir = Environment.getExternalStorageDirectory();//获取跟目录
}
return sdDir.toString();
}
/**
* 获取相册的图片
*/
protected void getImageFromAlbum(int actionCode) {
//就是利用Intent调用系统的相册
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");//相片类型
startActivityForResult(intent, actionCode);
}
好了做好以上的工作以后就可以选择图片或者拍摄图片了,当确认好了以后就会再次跳到当前的页面来,这时我们在onActivityResult()方法中进行数据的处理就行了。
1.接收从相机中返回的数据
<1>当开启相机使用的方法为简单的Intent直接调用时,我们在onActivityResult()方法中需要对返回的Intent进行判断处理。原因是因为android把拍摄的图片封装到bundle中传递回来,但是根据不同的机器获得相片的方式不太一样,可能有的相机能够通过 inten.getData()获取到uri然后再根据uri获取数据的路径,在封装成bitmap,但有时候有的相机获取到的是null的,这时候我们该怎么办呢?其实这时候我们就应该从bundle中获取数据,通过(Bitmap) bundle.get(“data”)获取到相机图片的bitmap数据。
为了能够同时适应上述两种情况,我们这时候就应该在获取图片时做判断了。我们可以在响应的时候做一个判断:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_PICK_IMAGE) {
Uri uri = data.getData();
//to do find the path of pic by uri
} else if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA ) {
Uri uri = data.getData();
if(uri == null){
//use bundle to get data
Bundle bundle = data.getExtras();
if (bundle != null) {
//可以通过获取到的这个Bitmap对象设置到ImageView控件中,让他显示出来
Bitmap photo = (Bitmap) bundle.get("data"); //get bitmap
//spath :生成图片取个名字和路径包含类型
saveImage(photo, spath);
} else {
Toast.makeText(getApplicationContext(), "err****", Toast.LENGTH_LONG).show();
return;
}
}else{
//to do find the path of pic by uri
}
}
}
public static boolean saveImage(Bitmap photo, String spath) {
try {
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(spath, false));
photo.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.flush();
bos.close();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
//以上方法引用的是网上的一段代码
<2>好了说完了直接简单的使用Intent方法调用相机,就该说说我们的重头戏了,那就是不要缩高清无码的大图了^_^。
这个时候在onActivityResult方法中就不需要进行对Intent进行判断处理了,只需将获取到的Bitmap对象进行处理,因为当前的地址肯定不会是空的。废话不多说,直接上代码。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Bitmap bm = null;
if (requestCode == REQUEST_CODE_PICK_IMAGE) {
} else if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA) {
//通过图片路径直接获取Bitmap对象(注意这个时候的Bitmap对象有可能会很大需要进行压缩处理)
bm = getBitmap(capturePath);
//对Bitmap对象进行压缩处理
bm = BitmapCompressUtils.imageZoom(bm, 310.00);
if (bm==null){
//设置默认的图片
}else{
img.setImageBitmap(bm);
}
//将bitmap放至数组中,意在bitmap的大小(与实际读取的原文件要大)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] b = baos.toByteArray();
//将字节换成KB
double mid = b.length / 1024;
tv.setText("存储路径" + capturePath + "\n图片大小" + mid);
}
}
/**
* 通过路径获取Bitmap对象
*
* @param path
* @return
*/
public static Bitmap getBitmap(String path) {
Bitmap bm = null;
InputStream is = null;
try {
File outFilePath = new File(path);
//判断如果当前的文件不存在时,创建该文件一般不会不存在
if (!outFilePath.exists()) {
boolean flag = false;
try {
flag = outFilePath.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("---创建文件结果----" + flag);
}
is = new FileInputStream(outFilePath);
bm = BitmapFactory.decodeStream(is);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return bm;
}
然后就是压缩图片的工具类
首先需要说明的是,对图片压缩处理的方法大体上分为三种,我现在用的是最耗时的一个不太建议使用。另外的几种先介绍下,日后再附上代码。
第一种是BitmapFactory和BitmapFactory.Options。
首先,BitmapFactory.Options有几个Fields很有用:
inJustDecodeBounds:If set to true, the decoder will return null (no bitmap), but the out…
也就是说,当inJustDecodeBounds设成true时,bitmap并不加载到内存,这样效率很高哦。而这时,你可以获得bitmap的高、宽等信息。
outHeight:The resulting height of the bitmap, set independent of the state of inJustDecodeBounds.
outWidth:The resulting width of the bitmap, set independent of the state of inJustDecodeBounds.
看到了吧,上面3个变量是相关联的哦。
inSampleSize : If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
这就是用来做缩放比的。这里有个技巧:
inSampleSize=(outHeight/Height+outWidth/Width)/2
实践证明,这样缩放出来的图片还是很好的。
最后用BitmapFactory.decodeFile(path, options)生成。
由于只是对bitmap加载到内存一次,所以效率比较高。解析速度快。
第二种是使用Bitmap加Matrix来缩放。
首先要获得原bitmap,再从原bitmap的基础上生成新图片。这样效率很低。
第三种是用2.2新加的类ThumbnailUtils来做。
让我们新看看这个类,从API中来看,此类就三个静态方法:createVideoThumbnail、extractThumbnail(Bitmap source, int width, int height, int options)、extractThumbnail(Bitmap source, int width, int height)。
是上面我们用到的BitmapFactory.Options和Matrix等经过人家一阵加工而成。效率好像比第二种方法高一点点。
/**
* Created by l_zp on 2016/1/20.
* 这是一个将图片进行压缩的工具类
*/
public class BitmapCompressUtils {
/**
* 压缩图片
*
* @param bitMap 要压缩的bitmap对象
* @param maxSize 压缩的大小(kb)不是很准确大约比输入值大于100k是因为比例决定的
* @return
*/
public static Bitmap imageZoom(Bitmap bitMap, double maxSize) {
if (bitMap != null) {
//将bitmap放至数组中,意在bitmap的大小(与实际读取的原文件要大)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitMap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] b = baos.toByteArray();
//将字节换成KB
double mid = b.length / 1024;
//判断bitmap占用空间是否大于允许最大空间 如果大于则压缩 小于则不压缩
if (mid > maxSize) {
//获取bitmap大小 是允许最大大小的多少倍
double i = mid / maxSize;
//开始压缩 此处用到平方根 将宽带和高度压缩掉对应的平方根倍 (1.保持刻度和高度和原bitmap比率一致,压缩后也达到了最大大小占用空间的大小)
bitMap = zoomImage(bitMap, bitMap.getWidth() / Math.sqrt(i),
bitMap.getHeight() / Math.sqrt(i));
}
}
return bitMap;
}
/***
* 图片的缩放方法
*
* @param bgimage :源图片资源
* @param newWidth :缩放后宽度
* @param newHeight :缩放后高度
* @return
*/
public static Bitmap zoomImage(Bitmap bgimage, double newWidth,
double newHeight) {
// 获取这个图片的宽和高
float width = bgimage.getWidth();
float height = bgimage.getHeight();
// 创建操作图片用的matrix对象
Matrix matrix = new Matrix();
// 计算宽高缩放率
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// 缩放图片动作
matrix.postScale(scaleWidth, scaleHeight);
Bitmap bitmap = Bitmap.createBitmap(bgimage, 0, 0, (int) width,
(int) height, matrix, true);
return bitmap;
}
另外附上从Uri中获取绝对路径的工具类
/**
* Created by l_zp on 2016/3/10.
*
* 文件工具类
*/
public class FileUtils {
/**
* 从Uri中获取绝对路径
*
* @param context
* @param uri
* @return the file path or null
*/
public static String getRealFilePath( final Context context, final Uri uri ) {
if ( null == uri ) return null;
final String scheme = uri.getScheme();
String data = null;
if ( scheme == null )
data = uri.getPath();
else if ( ContentResolver.SCHEME_FILE.equals( scheme ) ) {
data = uri.getPath();
} else if ( ContentResolver.SCHEME_CONTENT.equals( scheme ) ) {
Cursor cursor = context.getContentResolver().query( uri, new String[] { MediaStore.Images.ImageColumns.DATA }, null, null, null );
if ( null != cursor ) {
if ( cursor.moveToFirst() ) {
int index = cursor.getColumnIndex( MediaStore.Images.ImageColumns.DATA );
if ( index > -1 ) {
data = cursor.getString( index );
}
}
cursor.close();
}
}
return data;
}
}
2.获取从相册中返回的数据
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Bitmap bm = null;
if (requestCode == REQUEST_CODE_PICK_IMAGE) {
//这里判断data是否为空是为了防止用户开启完相册后,没有选择就返回
if (data != null) {
//外界的程序访问ContentProvider所提供数据 可以通过ContentResolver接口
Uri originalUri = data.getData();
//to do find the path of pic by uri
try {
//显得到bitmap图片这里开始的第二部分,获取图片的路径:
String path = FileUtils.getRealFilePath(MainActivity.this, originalUri);
bm = getBitmap(path);
System.out.println("----像素密度值---" + bm.getDensity());
bm = BitmapCompressUtils.imageZoom(bm, 210.00);
img.setImageBitmap(bm);
//将bitmap放至数组中,意在bitmap的大小(与实际读取的原文件要大)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] b = baos.toByteArray();
//将字节换成KB
double mid = b.length / 1024;
tv.setText("存储路径" + path + "\n图片大小" + mid);
} catch (Exception e) {
e.printStackTrace();
}
}
} else if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA) {
}
}
参考http://blog.csdn.net/beyond0525/article/details/8939984感谢前辈