欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android图片或拍照选择图片功能实例代码

程序员文章站 2022-06-30 11:24:00
前言 一般公司都有更换用户头像功能,需要从图库中选择图片或者拍照,基本还会对图片进行裁剪。最近抽空就做了一些简单的封装,方便以后使用。主要是用了建造者模式,链式调用,...

前言

一般公司都有更换用户头像功能,需要从图库中选择图片或者拍照,基本还会对图片进行裁剪。最近抽空就做了一些简单的封装,方便以后使用。主要是用了建造者模式,链式调用,方便简单。可以自定义图片路径,附带裁剪和简单压缩功能。使用实例如下:

chooseimagetask.getinstance()
    .createbuilder(this)
    .setfilename("图片名称")//有默认的
    .setfilepath("图片路径")//有默认的
    .setiscrop(false)//裁剪
    .setiscompress(true)//压缩
    .setonselectlistener(this)//监听回调结果
    .settype(chooseimagetask.type_gallery)//类型
    .perform();

相册图片

/**
  * 从系统图库里面选择
  *
  * @param activity
  * @param builder
  */
 private void takeimagefromgallery(activity activity, builder builder) {
  onselectlistener monselectlistener = builder.monselectlistener;
  intent intent = new intent(intent.action_pick, mediastore.images.media.external_content_uri);
  componentname componentname = intent.resolveactivity(activity.getpackagemanager());
  if (componentname != null) {
   activity.startactivityforresult(intent, builder.mtype);
  } else {
   if (monselectlistener != null) {
    monselectlistener.onerror("takeimagefromgallery---> activity is illegal");
   }
  }
 }

说明:componentname componentname = intent.resolveactivity(activity.getpackagemanager())主要用来校验当前跳转的activity,后面几个跳转同样也加上了。

图库图片

/**
  * 从图片类型文件中选择图片
  *
  * @param activity
  */
 private void takeimagefromalbum(activity activity, builder builder) {
  onselectlistener monselectlistener = builder.monselectlistener;
  intent intent = new intent(intent.action_open_document);//api19之后
//  intent intent = new intent(intent.action_get_content);//api19之前
  intent.settype("image/*");
  componentname componentname = intent.resolveactivity(activity.getpackagemanager());
  if (componentname != null) {
   activity.startactivityforresult(intent, builder.mtype);
  } else {
   if (monselectlistener != null) {
    monselectlistener.onerror("takeimagefromalbum---> activity is illegal");
   }
  }
 }

注意:action在不同的android版本中有所变化

拍照

拍照比较特殊的是因为android7.0之后,对于uri的读取采用了fileprovider的方式,所以要特殊处理。在res文件夹下创建xml文件夹,xml文件夹下面再创建拍照图片的存放路径,名称可以随便起,但是要记得取的时候要一致。

 /**
  * 拍照
  *
  * @param activity
  */
 private void takephoto(activity activity, chooseimagetask.builder builder) {
  chooseimagetask.onselectlistener monselectlistener = builder.monselectlistener;
  intent takepictureintent = new intent(mediastore.action_image_capture);
  //校验activity是否存在
  if (takepictureintent.resolveactivity(activity.getpackagemanager()) != null) {
   //判断是否自定义路径并且是否合法
   uri fileuri = uriutils.geturi(activity, new file(builder.mfilepath, builder.mfilename));
   takepictureintent.putextra(mediastore.extra_output, fileuri);
   activity.startactivityforresult(takepictureintent, builder.mtype);
  } else {
   if (monselectlistener != null) {
    monselectlistener.onerror("takephoto---> activity is illegal");
   }
  }
 }
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
 <external-path
  name="external"
  path="." />
</paths>

然后需要在manifest中引用当前的路径,如下:

<provider
   android:name="android.support.v4.content.fileprovider"
   android:authorities="${applicationid}.fileprovider"
   android:exported="false"
   android:granturipermissions="true">
   <meta-data
    android:name="android.support.file_provider_paths"
    android:resource="@xml/choose_image" />
  </provider>

取拍照的uri

 /**
  * 根据文件获取uri
  *
  * @param context
  * @param file
  * @return
  */
 public static uri geturi(context context, file file) {
  uri uri;
  if (build.version.sdk_int >= build.version_codes.n) {
   uri = fileprovider.geturiforfile(context,
     fileprovider的路径 , file);
  } else {
   uri = uri.fromfile(file);
  }
  //这里尽量还是要保证uri不要为空,否则报空指针异常
  return uri;
 }

注意:  android:authorities="${applicationid}.fileprovider"里面一定要填写build.gradle里面的applicationid值,不能填写包名,对于applicationid不了解的可以自行查看。

裁剪图片

/**
  * 图片类型的裁剪
  *
  * @param activity
  * @param uri
  * @param outputuri
  */
 public void handlecropimage(activity activity, uri uri, uri outputuri) {
  //打开系统自带的裁剪图片的intent
  intent intent = new intent("com.android.camera.action.crop");
  if (build.version.sdk_int >= build.version_codes.n) {
   //添加这一句表示对目标应用临时授权该uri所代表的文件
   intent.addflags(intent.flag_grant_read_uri_permission);
   intent.addflags(intent.flag_grant_write_uri_permission);
  }
  intent.setdataandtype(uri, "image/*");
  intent.putextra("scale", true);
  // 设置裁剪区域的宽高比例
  intent.putextra("aspectx", 1);
  intent.putextra("aspecty", 1);
  // 设置裁剪区域的宽度和高度
  intent.putextra("outputx", 350);
  intent.putextra("outputy", 350);
  // 人脸识别
  intent.putextra("nofacedetection", true);
  // 图片输出格式
  intent.putextra("outputformat", bitmap.compressformat.jpeg.tostring());
  // 若为false则表示不返回数据
  intent.putextra("return-data", false);
  //输出图片到指定位置
  intent.putextra(mediastore.extra_output, outputuri);
  activity.startactivityforresult(intent, chooseimagetask.type_crop);
 }

注意: intent.putextra("return-data", false);如果返回值为true的话,直接返回bitmap,为了统一压缩之后通过回调的形式返回,所以返回值为false,输出成outputuri。

图片旋转角度处理

有些手机拍照或者选取图片的时候会出现图片有旋转角度问题,所以要根据旋转的角度来重新生成新的图片,符合要求。

/**
  * 读取图片的旋转的角度
  *
  * @param path 图片绝对路径
  * @return 图片的旋转角度
  */
 public static int getbitmapdegree(string path) {
  int degree = 0;
  try {
   // 从指定路径下读取图片,并获取其exif信息
   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;
 }
/**
  * 旋转图片,使图片保持正确的方向。
  *
  * @param bitmap 原始图片
  * @param degrees 原始图片的角度
  * @return bitmap 旋转后的图片
  */
 public static bitmap rotatebitmap(bitmap bitmap, int degrees) {
  if (degrees == 0 || null == bitmap) {
   return bitmap;
  }
  matrix matrix = new matrix();
  matrix.setrotate(degrees, bitmap.getwidth() / 2, bitmap.getheight() / 2);
  bitmap bmp = bitmap.createbitmap(bitmap, 0, 0, bitmap.getwidth(), bitmap.getheight(), matrix, true);
  bitmap.recycle();
  return bmp;
 }

回调处理

通过在activity的onactivityresult中处理关于图片选择结果的回调,然后再根据类型处理不同的结果

 /**
  * 代理activity的返回值过程然后
  *
  * @param requestcode
  * @param resultcode
  * @param data
  */
 public void handleresult(int requestcode, int resultcode, @nullable intent data, builder builder) {
  if (resultcode != activity.result_ok) {
   return;
  }

  switch (requestcode) {
   case type_photo:// 拍照
    handlephoto(builder);
    break;
   case type_album://
    //跳转到裁剪页面
    handlegallery(data, builder);
    break;
   case type_gallery:// 图库选择图片
    //跳转到裁剪页面
    handlegallery(data, builder);
    break;
   case type_crop:
    handlecropresult(builder);
    break;
  }
 }

图片压缩

通过循环的方式压缩选取的图片

/**
  * 质量压缩方法
  *
  * @param image
  * @return
  */
 public static bitmap compressimage(bitmap image) {

  bytearrayoutputstream baos = new bytearrayoutputstream();
  image.compress(bitmap.compressformat.jpeg, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
  int options = 100;
  while (baos.tobytearray().length / 1024 > 100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
   baos.reset();//重置baos即清空baos
   //第一个参数 :图片格式 ,第二个参数: 图片质量,100为最高,0为最差 ,第三个参数:保存压缩后的数据的流
   image.compress(bitmap.compressformat.jpeg, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
   options -= 10;//每次都减少10
  }
  bytearrayinputstream isbm = new bytearrayinputstream(baos.tobytearray());//把压缩后的数据baos存放到bytearrayinputstream中
  bitmap bitmap = bitmapfactory.decodestream(isbm, null, null);//把bytearrayinputstream数据生成图片
  return bitmap;
 }

总结

大致过程如上所示,但是一定要android6.0之后申请动态权限,全部功能已经写了demo,已经上传github,如需要请移步github,如遇到问题请评论留言。图片或拍照选择图片 (本地下载

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。