Android修改头像之拍照、从相册选择、裁剪

您所在的位置:网站首页 原色定义 Android修改头像之拍照、从相册选择、裁剪

Android修改头像之拍照、从相册选择、裁剪

2024-02-02 15:26| 来源: 网络整理| 查看: 265

手写一个修改头像的需求,头像图片支持手机拍照裁剪和从相册选择图片裁剪;

实现效果:

 本节主要内容:

1)头像修改对话框实现;

2)调用系统相机拍照;

3)自定义图片裁剪页面;

4)从系统相册选择图片。

一、头像修改对话框实现

很简单的一个对话框

class HeadChangeDialog(context: Context) : Dialog(context, R.style.BottomDialog), View.OnClickListener { var onHeadChangeListener: OnHeadChangeListener? = null init { this.init() } fun init() { val contentView = LayoutInflater.from(context).inflate(R.layout.dialog_sdk_head_change, null) setContentView(contentView) val params = contentView.layoutParams as ViewGroup.MarginLayoutParams params.width = context.resources.displayMetrics.widthPixels - DensityUtil.dp2px(context, 0f) params.bottomMargin = DensityUtil.dp2px(context, 0f) contentView.layoutParams = params setCanceledOnTouchOutside(true) window?.setGravity(Gravity.BOTTOM) window?.setWindowAnimations(R.style.BottomDialog_Animation) // 设置点击事件 contentView.findViewById(R.id.tv_camera).setOnClickListener(this) contentView.findViewById(R.id.tv_photo).setOnClickListener(this) contentView.findViewById(R.id.tv_cancel).setOnClickListener(this) } override fun onClick(v: View?) { when (v!!.id) { R.id.tv_camera -> { onHeadChangeListener?.onCamera() dismiss() } R.id.tv_photo -> { onHeadChangeListener?.onPhoto() dismiss() } R.id.tv_cancel -> dismiss() } } interface OnHeadChangeListener { fun onCamera() fun onPhoto() } }

二、调用系统相机拍照

1)动态申请权限

private void toCamera() { // 判断是否需要动态申请存储权限 if (!PermissionUtil.checksCameraPermission(this)) { DialogManager.getInstance().showDialog(getString(R.string.camera_tip1), this, () -> { if (PermissionUtil.deniedPermission()) { // 用户已拒绝权限,跳转系统设置页 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", getPackageName(), null)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); } else { // 用户未拒绝权限,弹框动态申请权限 PermissionUtil.requestCameraPermission(this); } }); return; } startCamera(); }

2)调用系统相机拍照

在设置Uri是要注意,Android7.0及以上系统禁止应用向外部公开file://URI ,因此需要FileProvider来向外界传递URI。

public void takePicture(Activity activity, int requestCode) { try { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); takePictureIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); if (FileUtil.existSDCard()) { takeImageFile = new File(Environment.getExternalStorageDirectory(), "/DCIM/camera/"); } else { takeImageFile = Environment.getDataDirectory(); } takeImageFile = createFile(takeImageFile, "IMG_", ".jpg"); if (takeImageFile != null) { // 默认情况下,即不需要指定intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); // 照相机有自己默认的存储路径,拍摄的照片将返回一个缩略图。如果想访问原始图片, // 可以通过dat extra能够得到原始图片位置。即,如果指定了目标uri,data就没有数据, // 如果没有指定uri,则data就返回有数据! Uri uri; if (Build.VERSION.SDK_INT 10f) { isPointer = false; isMain = false; state = ZOOM; midPoint(mid, motionEvent); } break; case MotionEvent.ACTION_POINTER_UP: //次要点松开 isPointer = true; if (isPointer && isMain) { isPointer = false; isMain = false; state = DRAG; oldSpacing = 1f; } break; case MotionEvent.ACTION_DOWN: downX = motionEvent.getX(); downY = motionEvent.getY(); break; case MotionEvent.ACTION_MOVE: Matrix matrix = mCorpImg.getImageMatrix(); if (state == DRAG) { //拖拽 matrix.postTranslate(motionEvent.getX() - downX, motionEvent.getY() - downY); downX = motionEvent.getX(); downY = motionEvent.getY(); } else if (state == ZOOM && !isPointer && !isMain) { //放大缩小 float newSpacing = spacing(motionEvent); float scale = newSpacing / oldSpacing; matrix.postScale(scale, scale, mid.x, mid.y); oldSpacing = newSpacing; } break; case MotionEvent.ACTION_UP: //用户多点触碰释放时,需要同时释放所有点才初始,防止发生偏移。 isMain = true; if (isPointer && isMain) { isPointer = false; isMain = false; state = DRAG; oldSpacing = 1f; } break; } mCorpImg.invalidate(); return true; } }); /** * 多点触控时,计算最先放下的两指距离 * * @param event * @return */ private float spacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); } /** * 多点触控时,计算最先放下的两指中心坐标 * * @param point * @param event */ private void midPoint(PointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); }

4)保存裁剪框区域图片

@Override public void onRightTopMenuClick() { super.onRightTopMenuClick(); Bitmap bitmap = getBitmap(); File file = FileUtil.createImageFile(); file = BitmapUtil.compressImage(bitmap, file); mCropBitmap.recycle(); mCropBitmap = null; Intent intent = new Intent(); intent.putExtra(Constants.IMAGE_PATH, file.getPath()); setResult(RESULT_OK, intent); finish(); } /** * 获取裁剪框内截图 * * @return */ private Bitmap getBitmap() { // 获取截屏 mCropLayout.setDrawingCacheEnabled(true); mCropLayout.buildDrawingCache(); // 计算裁剪框的开始位置 left = (mCropLayout.getWidth() - mCropView.getWidth()) / 2; top = (mCropLayout.getHeight() - mCropView.getHeight()) / 2; // 裁剪框的边框宽度 int borderWidth = DensityUtil.dp2px(getContext(), 2.5f); Bitmap finalBitmap = Bitmap.createBitmap(mCropLayout.getDrawingCache(), left + borderWidth, top + borderWidth, mCropView.getWidth() - 2 * borderWidth, mCropView.getHeight() - 2 * borderWidth); // 释放资源 mCropLayout.destroyDrawingCache(); return finalBitmap; }

四、从系统相册选择图片

1)动态申请权限

private void toChooseImg() { // 判断是否需要动态申请存储权限 if (!PermissionUtil.checksStoragePermission(this)) { DialogManager.getInstance().showDialog(getString(R.string.storage_tip1), this, () -> { if (PermissionUtil.deniedStoragePermission()) { // 用户已拒绝权限,跳转系统设置页 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", getPackageName(), null)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); } else { // 用户未拒绝权限,弹框动态申请权限 PermissionUtil.requestStoragePermission(this); } }); return; } choosePhoto(); }

2)打开系统相册

public void photoAlbum(Activity activity, int requestCode) { Intent intentToPickPic = new Intent(Intent.ACTION_PICK, null); // 单个图片类型:"image/jpeg 、 image/png等的类型",所有类型:"image/*" intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); activity.startActivityForResult(intentToPickPic, requestCode); }

3)获取获取手机相册图片Uri,裁剪图片

@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { switch (requestCode) { case ImagePicker.REQUEST_CODE_PICK: // 获取图片 try { //该uri是上一个Activity返回的 Uri imageUri = data.getData(); LogUtil.i(TAG, "choosePhoto imageUri " + imageUri); if (imageUri != null) { Intent intent = new Intent(mContext, CorpImgActivity.class); intent.putExtra(Constants.IMAGE_URI, imageUri); startActivityForResult(intent, ImagePicker.REQUEST_CODE_CROP); } } catch (Exception e) { e.printStackTrace(); } break; } } super.onActivityResult(requestCode, resultCode, data); }

功能完成,测试手机Android6.0、7.0及以上系统版本的华为、小米、三星、谷歌手机暂未发现问题。

源码:

图片选择工具类

package com.lib.common.image; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.MediaStore; import androidx.core.content.FileProvider; import com.lib.common.util.FileUtil; import com.lib.common.util.LogUtil; import java.io.File; import java.util.Date; import java.util.List; import java.util.Locale; import java.text.SimpleDateFormat; /** * @Author: Jin * @Description: 图片选择工具 * @CreateDate: 2023/4/20 14:50 */ public class ImagePicker { private static final String TAG = "ImagePicker"; public static final int REQUEST_CODE_TAKE = 1001; // 打开照相机 public static final int REQUEST_CODE_CROP = 1002; // 图片裁剪 public static final int REQUEST_CODE_PICK = 1003; // 打开相册 private File takeImageFile; private static ImagePicker mInstance; private ImagePicker() { } public static ImagePicker getInstance() { if (mInstance == null) { synchronized (ImagePicker.class) { if (mInstance == null) { mInstance = new ImagePicker(); } } } return mInstance; } public File getTakeImageFile() { return takeImageFile; } /** * 拍照的方法 */ public void takePicture(Activity activity, int requestCode) { try { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); takePictureIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); if (FileUtil.existSDCard()) { takeImageFile = new File(Environment.getExternalStorageDirectory(), "/DCIM/camera/"); } else { takeImageFile = Environment.getDataDirectory(); } takeImageFile = createFile(takeImageFile, "IMG_", ".jpg"); if (takeImageFile != null) { // 默认情况下,即不需要指定intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); // 照相机有自己默认的存储路径,拍摄的照片将返回一个缩略图。如果想访问原始图片, // 可以通过dat extra能够得到原始图片位置。即,如果指定了目标uri,data就没有数据, // 如果没有指定uri,则data就返回有数据! Uri uri; if (Build.VERSION.SDK_INT 10f) { isPointer = false; isMain = false; state = ZOOM; midPoint(mid, motionEvent); } break; case MotionEvent.ACTION_POINTER_UP: // 次要点松开 isPointer = true; if (isPointer && isMain) { isPointer = false; isMain = false; state = DRAG; oldSpacing = 1f; } break; case MotionEvent.ACTION_DOWN: downX = motionEvent.getX(); downY = motionEvent.getY(); break; case MotionEvent.ACTION_MOVE: Matrix matrix = mCorpImg.getImageMatrix(); if (state == DRAG) { // 拖拽 matrix.postTranslate(motionEvent.getX() - downX, motionEvent.getY() - downY); downX = motionEvent.getX(); downY = motionEvent.getY(); } else if (state == ZOOM && !isPointer && !isMain) { // 放大缩小 float newSpacing = spacing(motionEvent); float scale = newSpacing / oldSpacing; matrix.postScale(scale, scale, mid.x, mid.y); oldSpacing = newSpacing; } break; case MotionEvent.ACTION_UP: // 用户多点触碰释放时,需要同时释放所有点才初始,防止发生偏移。 isMain = true; if (isPointer && isMain) { isPointer = false; isMain = false; state = DRAG; oldSpacing = 1f; } break; } mCorpImg.invalidate(); return true; } /** * 多点触控时,计算最先放下的两指距离 * * @param event * @return */ private float spacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); } /** * 多点触控时,计算最先放下的两指中心坐标 * * @param point * @param event */ private void midPoint(PointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { int layoutWidth = mCropLayout.getWidth(); int layoutHeight = mCropLayout.getHeight(); int imgWidth = mCropBitmap.getWidth(); int imgHeight = mCropBitmap.getHeight(); int selectWidth = mCropView.getWidth(); int selectHeight = mCropView.getHeight(); float scaleNum; // 缩放比例 // 将要裁剪的图片长宽高做对比, 将较小的一方做等比缩放成裁剪框大小 if (imgWidth < imgHeight) { scaleNum = (selectWidth * 1.0f) / (imgWidth * 1.0f); imgHeight = (int) (scaleNum * imgHeight); imgWidth = selectWidth; } else { scaleNum = (selectHeight * 1.0f) / (imgHeight * 1.0f); imgWidth = (int) (scaleNum * imgWidth); imgHeight = selectHeight; } Matrix matrix = new Matrix(); matrix.postScale(scaleNum, scaleNum); // 平移距离 matrix.postTranslate((layoutWidth - imgWidth) / 2, (layoutHeight - imgHeight) / 2); // 设置缩放类型为 矩阵 mCorpImg.setScaleType(ImageView.ScaleType.MATRIX); mCorpImg.setImageMatrix(matrix); mCorpImg.setImageBitmap(mCropBitmap); } } @Override public void onRightTopMenuClick() { super.onRightTopMenuClick(); Bitmap bitmap = getBitmap(); File file = FileUtil.createImageFile(); file = BitmapUtil.compressImage(bitmap, file); mCropBitmap.recycle(); mCropBitmap = null; Intent intent = new Intent(); intent.putExtra(Constants.IMAGE_PATH, file.getPath()); setResult(RESULT_OK, intent); finish(); } /** * 获取裁剪框内截图 * * @return */ private Bitmap getBitmap() { // 获取截屏 mCropLayout.setDrawingCacheEnabled(true); mCropLayout.buildDrawingCache(); // 计算裁剪框的开始位置 left = (mCropLayout.getWidth() - mCropView.getWidth()) / 2; top = (mCropLayout.getHeight() - mCropView.getHeight()) / 2; // 裁剪框的边框宽度 int borderWidth = DensityUtil.dp2px(getContext(), 2.5f); Bitmap finalBitmap = Bitmap.createBitmap(mCropLayout.getDrawingCache(), left + borderWidth, top + borderWidth, mCropView.getWidth() - 2 * borderWidth, mCropView.getHeight() - 2 * borderWidth); // 释放资源 mCropLayout.destroyDrawingCache(); return finalBitmap; } }

裁剪页面布局



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3