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

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

实现效果:

 本节主要内容:

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<TextView>(R.id.tv_camera).setOnClickListener(this)
        contentView.findViewById<TextView>(R.id.tv_photo).setOnClickListener(this)
        contentView.findViewById<TextView>(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 <= Build.VERSION_CODES.M) {
				uri = Uri.fromFile(takeImageFile);
			} else {
				/**
				 * 7.0 调用系统相机拍照不再允许使用Uri方式,应该替换为FileProvider
				 * 并且这样可以解决MIUI系统上拍照返回size为0的情况
				 */
				uri = FileProvider.getUriForFile(activity, ProviderUtil.getFileProviderName(activity), takeImageFile);
				//加入uri权限 要不三星手机不能拍照
				List<ResolveInfo> resInfoList = activity.getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
				for (ResolveInfo resolveInfo : resInfoList) {
					String packageName = resolveInfo.activityInfo.packageName;
					activity.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
				}
			}

			LogUtil.i(TAG, ProviderUtil.getFileProviderName(activity));
			takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
		}
		activity.startActivityForResult(takePictureIntent, requestCode);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

3)自定义FileProvider

FileProvider是ContentProvider的一个子类,用于应用程序之间私有文件的传递。自Android 7.0后系统禁止应用向外部公开file://URI ,因此需要FileProvider来向外界传递URI,传递的形式是content : //Uri,使用时需要在清单文件中注册。

// 自定义一个Provider,以免和引入的项目的provider冲突
public class ImagePickerProvider extends FileProvider {
}

注册清单文件

<provider
	android:name=".image.ImagePickerProvider"
	android:authorities="${applicationId}.provider"
	android:exported="false"
	android:grantUriPermissions="true">
	<meta-data
		android:name="android.support.FILE_PROVIDER_PATHS"
		android:resource="@xml/provider_paths"/>
</provider>

创建provider_paths.xml文件

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

4)获取相机拍照图片路径,裁剪图片

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
	if (resultCode == RESULT_OK) {
		switch (requestCode) {
			case ImagePicker.REQUEST_CODE_TAKE:
				String imagePath = ImagePicker.getInstance().getTakeImageFile().getAbsolutePath();
				LogUtil.i(TAG, "startCamera imagePath " + imagePath);
				if (!TextUtils.isEmpty(imagePath)) {
					Intent intent = new Intent(mContext, CorpImgActivity.class);
					intent.putExtra(Constants.IMAGE_PATH, imagePath);
					startActivityForResult(intent, ImagePicker.REQUEST_CODE_CROP);
				}
				break;
		}
	}
	super.onActivityResult(requestCode, resultCode, data);
}

三、自定义图片裁剪页面

图片裁剪页面功能实现:

1)显示图片

private void initData() {
	Bundle bundle = getIntent().getExtras();
	if (bundle != null && bundle.containsKey(Constants.IMAGE_URI)) {
		Uri imageUri = (Uri) bundle.get(Constants.IMAGE_URI);
		if (imageUri != null) {
			try {
				// 要裁剪的图片
				mCropBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
				mCorpImg.setImageBitmap(mCropBitmap);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
	}

	if (bundle != null && bundle.containsKey(Constants.IMAGE_PATH)) {
		String imagePath = (String) bundle.get(Constants.IMAGE_PATH);
		if (imagePath != null) {
			try {
				// 要裁剪的图片
				mCropBitmap = BitmapFactory.decodeStream(new FileInputStream(imagePath));
				// 旋转图片
				int degree = BitmapUtil.getBitmapDegree(imagePath);
				LogUtil.i(TAG, "degree " + degree);
				if (degree != 0)
					mCropBitmap = BitmapUtil.rotateBitmapByDegree(mCropBitmap, degree);
				mCorpImg.setImageBitmap(mCropBitmap);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
}

2)实现拖拽图片

mCorpImg.setOnTouchListener(new View.OnTouchListener() {
	@Override
	public boolean onTouch(View view, MotionEvent motionEvent) {
		switch (motionEvent.getAction() ) {
			case MotionEvent.ACTION_DOWN:
				downX = motionEvent.getX();
				downY = motionEvent.getY();
				break;
			case MotionEvent.ACTION_MOVE:
				Matrix matrix = mCorpImg.getImageMatrix();
				//指定移动距离
				matrix.postTranslate(motionEvent.getX() - downX, motionEvent.getY() - downY);
				downX = motionEvent.getX();
				downY = motionEvent.getY();
				break;
			case MotionEvent.ACTION_UP:
				break;
		}
		mCorpImg.invalidate();
		return true;
	}
});

3)放大缩小图片

mCorpImg.setOnTouchListener(new View.OnTouchListener() {
	@Override
	public boolean onTouch(View view, MotionEvent motionEvent) {
		switch (motionEvent.getAction() & motionEvent.getActionMasked()) {
			case MotionEvent.ACTION_POINTER_DOWN:
				//次要点被点击
				oldSpacing = spacing(motionEvent);
				if (oldSpacing > 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 <= Build.VERSION_CODES.M) {
                    uri = Uri.fromFile(takeImageFile);
                } else {
                    /**
                     * 7.0 调用系统相机拍照不再允许使用Uri方式,应该替换为FileProvider
                     * 并且这样可以解决MIUI系统上拍照返回size为0的情况
                     */
                    uri = FileProvider.getUriForFile(activity, ProviderUtil.getFileProviderName(activity), takeImageFile);
                    //加入uri权限 要不三星手机不能拍照
                    List<ResolveInfo> resInfoList = activity.getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
                    for (ResolveInfo resolveInfo : resInfoList) {
                        String packageName = resolveInfo.activityInfo.packageName;
                        activity.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    }
                }

                LogUtil.i(TAG, ProviderUtil.getFileProviderName(activity));
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
            }
            activity.startActivityForResult(takePictureIntent, requestCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据系统时间、前缀、后缀产生一个文件
     */
    public static File createFile(File folder, String prefix, String suffix) {
        if (!folder.exists() || !folder.isDirectory()) folder.mkdirs();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA);
        String filename = prefix + dateFormat.format(new Date(System.currentTimeMillis())) + suffix;
        return new File(folder, filename);
    }

    /**
     * 打开相册
     */
    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);
    }
}

图片裁剪页面

package com.lib.common.image;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.net.Uri;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import com.lib.common.R;
import com.lib.common.constant.Constants;
import com.lib.common.ui.base.BaseActivity;
import com.lib.common.util.BitmapUtil;
import com.lib.common.util.DensityUtil;
import com.lib.common.util.FileUtil;
import com.lib.common.util.LogUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class CorpImgActivity extends BaseActivity implements View.OnTouchListener {
    private static final String TAG = "CorpImgActivity";
    private View mCropView;
    private ImageView mCorpImg;
    private RelativeLayout mCropLayout;
    private Bitmap mCropBitmap;

    private int top, left;
    private float downX, downY;
    private final int ZOOM = 1, DRAG = 0;
    private float oldSpacing = 1f;
    private int state;

    /**
     * 记录缩放时两指中间点坐标
     */
    private PointF mid = new PointF();
    private boolean isMain, isPointer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setActivityTitle(getString(R.string.crop_img));
        setRightTopMenuShow(true);
        setRightTopTitle(getString(R.string.save));
        initView();
        initData();
    }

    @Override
    public int setLayoutId() {
        return R.layout.activity_corp_img;
    }

    private void initView() {
        mCropView = findViewById(R.id.crop_handler_select);
        mCorpImg = findViewById(R.id.crop_handler_img);
        mCropLayout = findViewById(R.id.crop_handler_layout);
        //添加缩放功能
        mCorpImg.setOnTouchListener(this);
    }

    private void initData() {
        Bundle bundle = getIntent().getExtras();
        if (bundle != null && bundle.containsKey(Constants.IMAGE_URI)) {
            Uri imageUri = (Uri) bundle.get(Constants.IMAGE_URI);
            if (imageUri != null) {
                try {
                    // 要裁剪的图片
                    mCropBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                    mCorpImg.setImageBitmap(mCropBitmap);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }

        if (bundle != null && bundle.containsKey(Constants.IMAGE_PATH)) {
            String imagePath = (String) bundle.get(Constants.IMAGE_PATH);
            if (imagePath != null) {
                try {
                    // 要裁剪的图片
                    mCropBitmap = BitmapFactory.decodeStream(new FileInputStream(imagePath));
                    // 旋转图片
                    int degree = BitmapUtil.getBitmapDegree(imagePath);
                    LogUtil.i(TAG, "degree " + degree);
                    if (degree != 0)
                        mCropBitmap = BitmapUtil.rotateBitmapByDegree(mCropBitmap, degree);
                    mCorpImg.setImageBitmap(mCropBitmap);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        switch (motionEvent.getAction() & motionEvent.getActionMasked()) {
            case MotionEvent.ACTION_POINTER_DOWN:
                // 次要点被点击
                oldSpacing = spacing(motionEvent);
                if (oldSpacing > 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;
    }
}

裁剪页面布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black"
    android:orientation="vertical">

    <RelativeLayout
        android:id="@+id/crop_handler_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/crop_handler_img"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <View
            android:id="@+id/crop_handler_select"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_centerInParent="true"
            android:background="@drawable/bg_crop" />
    </RelativeLayout>
</LinearLayout>
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sziitjin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值