对图片的操作,移动、缩放,边界限制
监听用户手势,提取用户操作
(1)移动: 分别计算X,Y轴的结束与初始之间移动偏移的量
(2)缩放:(结束两指间距离×伸缩比例)/ 初始两指间距离,scaleX,scaleY放大多少倍
(3)涂鸦:重写onDraw()方法,画布(canvas)结合画笔(Paint)构造Path实现,监听手势起始点坐标与结束坐标,使用path类的quadTo()方法绘制曲线
(4)保存:保存图片Insert到MediaStore.Images.Media下面,广播通知系统相册图库刷新数据
通过 Matrix 矩阵 方式操作图片
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.droidparts.annotation.inject.InjectView;
private static final String TAG = "mylog_showPic";
@InjectView(id = R.id.iv_pic,click = true)
ImageView ivPic;
private static final int NONE = 0;
private static final int DRAG = 1; //一个手指
private static final int MOVE = 3; //移动
private static final int ZOOM = 2; //两个手指
private int mode = NONE;
private Matrix matrix = new Matrix(); //移动的矩阵
private Matrix saveMatrix = new Matrix();
private PointF startPoint = new PointF(); //第一个按下手指的点
private PointF midPoint = new PointF(); // 两个按下手指触摸点的中点
private float distance = 1f; //两个手指触摸点之间的距离
ImageView图片控件上的手势监听
ivPic.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return true;
}
});
ivPic.setOnTouchListener((view, motionEvent) -> {
ImageView imageView = (ImageView) view;
imageView.setScaleType(ImageView.ScaleType.MATRIX); //注意:操作之前需设置ScaleType为matrix
// final int x = (int) motionEvent.getRawX(); //触摸点到屏幕左边的距离
// final int y = (int) motionEvent.getRawY(); //触摸点到屏幕上边的距离
// Log.i(TAG,"触摸点离屏幕左边的距离:"+ x +"屏幕上方的距离:"+ y);
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"单点按下");
//单点按下
mode = DRAG;
//设置要操作的矩阵为该图片的图片矩阵
matrix.set(imageView.getImageMatrix());
saveMatrix.set(matrix);
startPoint.set(motionEvent.getX(),motionEvent.getY());
break;
case MotionEvent.ACTION_POINTER_DOWN:
//多点按下
Log.i(TAG,"多点按下");
//计算两指间距离
distance = distance(motionEvent);
if (distance > 10f){
saveMatrix.set(matrix);
//计算两指间中点的坐标
midPoint = midPoint(motionEvent);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_MOVE:
//手指滑动
if (mode == DRAG || mode == MOVE){
Log.i(TAG,"移动");
mode = MOVE;
//获取当前图片矩阵
matrix.set(saveMatrix);
//分别在x、y轴上将图片矩阵移动相应的距离
matrix.postTranslate(motionEvent.getX() - startPoint.x,motionEvent.getY() - startPoint.y);
//仅设置左右移动
//matrix.postTranslate(motionEvent.getX() - startPoint.x,0);
}else if(mode == ZOOM){
Log.i(TAG,"缩放");
float newDistance = distance(motionEvent);
if (newDistance > 10f){
//获取当前图片矩阵
matrix.set(saveMatrix);
//放大或缩小的倍数(缩放后手指间的距离/缩放前手指间的距离)
float scale = newDistance/distance;
//以两指中点为中心将当前图片矩阵放大或缩小scale倍
matrix.postScale(scale,scale,midPoint.x,midPoint.y);
}
}
break;
case MotionEvent.ACTION_UP:
if (mode == DRAG){
//是单点按下抬起时候,关闭图片
dismiss();
}else if (mode == MOVE){
//解决鸿蒙系统手机点击图片不能关闭的问题
if (isHarmonyOs() || matrix == saveMatrix){
//是鸿蒙系统且矩阵未变化
dismiss();
}
}
mode = NONE;
break;
case MotionEvent.ACTION_POINTER_UP:
//手指抬起
mode = NONE;
break;
default:
break;
}
if (mode == ZOOM){
float[] values = new float[9];
matrix.getValues(values);
float x = ivPic.getDrawable().getBounds().width() * values[0];
float y = ivPic.getDrawable().getBounds().height() * values[0];
Log.i(TAG,"当前图片矩阵大小:"+x+"____"+y);
if (x < 400){
Log.i(TAG,"不能缩小了");
imageView.setImageMatrix(saveMatrix);
}else{
//将操作后的图片矩阵赋值给imageView
imageView.setImageMatrix(matrix);
}
}
else if (mode == DRAG || mode == MOVE){
float[] values = new float[9];
matrix.getValues(values);
float parentWidth = context.getResources().getDisplayMetrics().widthPixels;
float x = ivPic.getDrawable().getBounds().width() * values[0];
Log.i(TAG,"当前图片左边位置:"+values[Matrix.MTRANS_X]+"___图片宽度"+x);
if (values[Matrix.MTRANS_X]< parentWidth && values[Matrix.MTRANS_X] > x*(-1)){
//将操作后的图片矩阵赋值给imageView
imageView.setImageMatrix(matrix);
}else{
Log.i(TAG,"不能移动了");
imageView.setImageMatrix(saveMatrix);
}
}
return true;
});
判断是否是鸿蒙系统的手机
/**
* 是否是鸿蒙系统
* @return
*/
public static boolean isHarmonyOs() {
try {
Class> buildExClass = Class.forName("com.huawei.system.BuildEx");
Object osBrand = buildExClass.getMethod("getOsBrand").invoke(buildExClass);
return "Harmony".equalsIgnoreCase(osBrand.toString());
} catch (Throwable x) {
return false;
}
}
计算两指间距离
private float distance(MotionEvent motionEvent){
float dx = motionEvent.getX(0) - motionEvent.getX(1);
float dy = motionEvent.getY(0) - motionEvent.getY(1);
return (float) Math.sqrt(dx * dx + dy * dy);
}
计算两指间中点的坐标
private PointF midPoint(MotionEvent motionEvent){
float midx = (motionEvent.getX(0) + motionEvent.getX(1)) / 2;
float midy = (motionEvent.getY(0) + motionEvent.getY(1)) / 2;
return new PointF(midx,midy);
}
控件布局,注意根据情况设置scaleType
打开图片的方法,可自定义
注意:再次打开照片时,可重新设置ScaleType恢复原始图片大小,不然就会显示上一次图片关闭前缩放操作的图片
public void showAtLocation(View parent, String path) {
//恢复到原始图片显示大小和位置
ivPic.setScaleType(ImageView.ScaleType.FIT_CENTER);
if (TextUtil.isEmpty(path)) {
return;
}
if (!PublicUtils.isImage(path)) {
return;
}
if (path.startsWith("http://")) {
ImageLoader.getInstance().displayImage(path, ivPic, ImageLoaderUtil.getInstence().getRectOptions());
} else {
ImageLoader.getInstance().displayImage("file://" + path, ivPic, ImageLoaderUtil.getInstence().getRectOptions());
}
super.showAtLocation(parent, Gravity.CENTER, 0, 0);
}
涂鸦和保存到本地的功能,可以参考我自己使用kotlin语言写的小demo
简易相册demo
本项目实现一个简易的相册功能,一共三个页面,第一个,拍照页,显示在左下角,第二个,图片展示页,图片显示3列,以文件修改时间降序,竖直排列,可上下滑动;第三个,当前选择图片的操作页,可移动,缩放,涂鸦,保存。