文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android实现读取相机(相册)图片并进行剪裁

2022-06-06 09:57

关注

我们先说一下思路,在android系统中就自带了图片剪切的应用,所以,我们只需要将我们获取到的相片传给图片剪切应用,再将剪切好的相片返回到我们自己的界面显示就ok了

在开发一些APP的过程中,我们可能涉及到头像的处理,比如从手机或者相册获取头像,剪裁成自己需要的头像,设置或上传头像等。网上一些相关的资料也是多不胜数,但在实际应用中往往会存在各种问题,没有一个完美的解决方案。由于近期项目的需求,就研究了一下,目前看来还没有什么问题。

这里我们只讨论获取、剪裁与设置,上传流程根据自己的业务需求添加。先上一张流程图:

这图是用Google Drive的绘图工具绘制的,不得不赞叹Google可以把在线编辑工具做得如此强大。好吧,我就是Google的脑残粉!回到主题,这是我设计的思路,接下来进行详细分析:

1、获得图片的途径无非就两种,第一是相机拍摄,第二是从本地相册获取。

2、我在SD卡上创建了一个文件夹,里面有两个Uri,一个是用于保存拍照时获得的原始图片,一个是保存剪裁后的图片。之前我考虑过用同一个Uri来保存图片,但是在实践中遇到一个问题,当拍照后不进行剪裁,那么下次从SD卡拿到就是拍照保存的大图,不仅丢失了之前剪裁的图片,还会因为加载大图导致内存崩溃。基于此考虑,我选择了两个Uri来分别保存图片。

3、相机拍摄时,我们使用Intent调用系统相机,并将设置输出设置到SDCard\xx\photo_file.jpg,以下是代码片段:


//调用系统相机
Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//将拍照结果保存至photo_file的Uri中,不保留在相册中
intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imagePhotoUri);
startActivityForResult(intentCamera, PHOTO_REQUEST_CAREMA);

在回调时,我们需要对photo_file.jpg调用系统工具进行剪裁,并设置输出设置到SDCard\xx\crop_file.jpg,以下是代码片段:


case PHOTO_REQUEST_CAREMA:
  if (resultCode == RESULT_OK) {
    //从相机拍摄保存的Uri中取出图片,调用系统剪裁工具
    if (imagePhotoUri != null) {
      CropUtils.cropImageUri(this, imagePhotoUri, imageUri, ibUserIcon.getWidth(), ibUserIcon.getHeight(), PHOTO_REQUEST_CUT);
    } else {
      ToastUtils.show(this, "没有得到拍照图片");
    }
  } else if (resultCode == RESULT_CANCELED) {
    ToastUtils.show(this, "取消拍照");
  } else {
    ToastUtils.show(this, "拍照失败");
  }
  break;

//调用系统的剪裁处理图片并保存至imageUri中
public static void cropImageUri(Activity activity, Uri orgUri, Uri desUri, int width, int height, int requestCode) {
  Intent intent = new Intent("com.android.camera.action.CROP");
  intent.setDataAndType(orgUri, "image
  paint.setAntiAlias(true);
  paint.setFilterBitmap(true);
  paint.setDither(true);
  // 保证是方形,并且从中心画
  int width = bitmap.getWidth();
  int height = bitmap.getHeight();
  int w;
  int deltaX = 0;
  int deltaY = 0;
  if (width <= height) {
    w = width;
    deltaY = height - w;
  } else {
    w = height;
    deltaX = width - w;
  }
  final Rect rect = new Rect(deltaX, deltaY, w, w);
  final RectF rectF = new RectF(rect);
  paint.setAntiAlias(true);
  canvas.drawARGB(0, 0, 0, 0);
  // 圆形,所有只用一个
  int radius = (int) (Math.sqrt(w * w * 2.0d) / 2);
  canvas.drawRoundRect(rectF, radius, radius, paint);
  paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
  canvas.drawBitmap(bitmap, rect, rect, paint);
  return output;
}

4、相册获取时,这也是最难的地方。Android 4.4以下的版本,从相册获取的图片Uri能够完美调用系统剪裁工具,或者直接从选取相册是带入剪裁图片的Intent,而且效果非常完美。但是在Android 4.4及其以上的版本,获取到的Uri根本无法调用系统剪裁工具,会直接导致程序崩溃。我也是研究了很久,才发现两者的Uri有很大的区别,Google官方文档中让开发者使用Intent.ACTION_GET_CONTENT代替以前的Action,并且就算你仍然使用以前的Action,都会返回一种新型的Uri,我个人猜测是因为Google把所有的内容获取分享做成一个统一的Uri,如有不对,请指正!想通这一点后,问题就变得简单了,我把这种新型的Uri重新封装一次,得到以为"file:\\..."标准的绝对路劲,传入系统剪裁工具中,果然成功了,只是这个封装过程及其艰难,查阅了很多资料,终于还是拿到了。下面说下具体步骤:

第一、调用系统相册,以下是代码片段:


//调用系统相册
  Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT);
  photoPickerIntent.setType("image
private static String getDataColumn(Context context, Uri uri,String selection, String[] selectionArgs) {
  Cursor cursor = null;
  final String column = "_data";
  final String[] projection = {column};
  try {
    cursor = context.getContentResolver().query(uri, projection,selection, selectionArgs, null);
    if (cursor != null && cursor.moveToFirst()) {
      final int column_index = cursor.getColumnIndexOrThrow(column);
      return cursor.getString(column_index);
    }
  } finally {
    if (cursor != null)
      cursor.close();
  }
  return null;
}

private static boolean isExternalStorageDocument(Uri uri) {
  return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

private static boolean isDownloadsDocument(Uri uri) {
  return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

private static boolean isMediaDocument(Uri uri) {
  return "com.android.providers.media.documents".equals(uri.getAuthority());
}

后续的系统剪裁工具调用跟拍照获取步骤一致,请参见上的代码。

5、所有步骤完成,在Nexus 5设备中的最新系统中测试通过,在小米、三星等一些设备中表现也很完美。如果在你的设备上存在缺陷,一定要跟帖给我反馈,谢谢!

文章结尾附上一个网友的完整示例,给了我很多的参考


package com.only.android.app;
import java.io.File;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import com.only.android.R;
public class CopyOfImageScaleActivity extends Activity implements View.OnClickListener {
  
  private Button selectImageBtn;
  private ImageView imageView;
  private File sdcardTempFile;
  private AlertDialog dialog;
  private int crop = 180;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.imagescale);
    selectImageBtn = (Button) findViewById(R.id.selectImageBtn);
    imageView = (ImageView) findViewById(R.id.imageView);
    selectImageBtn.setOnClickListener(this);
    sdcardTempFile = new File("/mnt/sdcard/", "tmp_pic_" + SystemClock.currentThreadTimeMillis() + ".jpg");
  }
  @Override
  public void onClick(View v) {
    if (v == selectImageBtn) {
      if (dialog == null) {
        dialog = new AlertDialog.Builder(this).setItems(new String[] { "相机", "相册" }, new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
            if (which == 0) {
              Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
              intent.putExtra("output", Uri.fromFile(sdcardTempFile));
              intent.putExtra("crop", "true");
              intent.putExtra("aspectX", 1);// 裁剪框比例
              intent.putExtra("aspectY", 1);
              intent.putExtra("outputX", crop);// 输出图片大小
              intent.putExtra("outputY", crop);
              startActivityForResult(intent, 101);
            } else {
              Intent intent = new Intent("android.intent.action.PICK");
              intent.setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
              intent.putExtra("output", Uri.fromFile(sdcardTempFile));
              intent.putExtra("crop", "true");
              intent.putExtra("aspectX", 1);// 裁剪框比例
              intent.putExtra("aspectY", 1);
              intent.putExtra("outputX", crop);// 输出图片大小
              intent.putExtra("outputY", crop);
              startActivityForResult(intent, 100);
            }
          }
        }).create();
      }
      if (!dialog.isShowing()) {
        dialog.show();
      }
    }
  }
  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (resultCode == RESULT_OK) {
      Bitmap bmp = BitmapFactory.decodeFile(sdcardTempFile.getAbsolutePath());
      imageView.setImageBitmap(bmp);
    }
  }
}

最后再啰嗦一句,功能虽然已经实现了,但是实际代码还是可以进一步优化的,感兴趣的童鞋们可以改进下。

您可能感兴趣的文章:基于Android实现保存图片到本地并可以在相册中显示出来android获取相册图片和路径的实现方法android照相、相册获取图片剪裁报错的解决方法解决Android从相册中获取图片出错图片却无法裁剪问题的方法android中打开相机、打开相册进行图片的获取示例Android实现保存图片到本地并在相册中显示Android实现长按图片保存至相册功能Android获取本地相册图片和拍照获取图片的实现方法Android 图片存储到指定路径和相册的方法Android实现选择相册图片并显示功能


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯