最近工作需要研究了一下安卓的涂鸦,实现了单指移动,双指缩放,涂鸦,保存的功能。上代码!
首先是页面布局,用于显示图片和涂鸦。
<ImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix" />
<View
android:id="@+id/drawing_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp" />
在onCreate中对页面的view进行实例,并将原始图片显示在imageView中,创建与原始图片大小相匹配的Mutable Bitmap和画布
imageView = findViewById(R.id.image_view);
drawingView = findViewById(R.id.drawing_view);
// 设置触摸事件监听器
imageView.setOnTouchListener(this);
drawingView.setOnTouchListener(this);
// 加载原始图片
originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
// 创建与原始图片大小相匹配的Mutable Bitmap
mutableBitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true);
bitmapBackup0 = originalBitmap.copy(Bitmap.Config.ARGB_8888, true);//打开原始图像时,即备份
// 创建画布并与Mutable Bitmap关联
drawingCanvas = new Canvas(mutableBitmap);
初始化画笔
// 初始化画笔
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5f);
在ImageView中显示原始图片,根据当前视图比例进行变化matrix,使图片完整显示
imageView.setImageBitmap(originalBitmap);
img_w = originalBitmap.getWidth();
img_h = originalBitmap.getHeight();
Log.d(TAG, "onCreate: img_w = "+img_w+" img_h = "+img_h);
view_w = 1080;//视图 宽
view_h = 2124;//视图 高
// int view_w = imageView.getWidth();
// int view_h = imageView.getHeight();
Log.d(TAG, "onCreate: view_w = "+view_w+" view_h = "+view_h);
// if(img_w<img_h)
// {
init_ratio = (1080*(1.0f))/(img_w*(1.0f));
Log.d(TAG, "aaaaaaaaaaaaaaaaaaaaa init_ratio = "+init_ratio);
matrix.reset();
matrix.postScale(init_ratio,init_ratio);
current_disY = 300;//为了不挡住上面的主菜单栏
matrix.postTranslate(0 ,current_disY);
imageView.setImageMatrix(matrix);
onTouch处理触摸事件,包括缩放、移动、涂鸦。涂鸦时根据缩放、移动后的X,Y坐标变化而变化,否则会无法根据实际触点画线
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "onTouch: event.getPointerCount() = "+event.getPointerCount());
Log.d(TAG, "onTouch: event.getAction() = "+event.getAction());
if (event.getPointerCount() == 2) {
two_pointer_down = true;
isDrawLine = false;
Log.d(TAG, "onTouch: ~~~~~~~~~~~~~~~~~~~~~~~");
//处理双指缩放
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if(distance1 ==0)
{
distance1 = getDistance(event);
}
current_ratio = getDistance(event)/distance1;
Log.d(TAG, "onTouch: distance1 = "+distance1);
distance1 = getDistance(event);
Log.d(TAG, "onTouch: current_ratio = "+current_ratio);
total_ratio = current_ratio*total_ratio;
Log.d(TAG, "onTouch: total_ratio = "+total_ratio);
float centerX = (event.getX(0)+event.getX(1))/2;
float centerY = (event.getY(0)+event.getY(1))/2;
Log.d(TAG, "onTouch: centerX = "+centerX);
Log.d(TAG, "onTouch: centerY = "+centerY);
// disX = centerX/total_ratio;
// disY = centerY/total_ratio;
matrix.reset();
matrix.postScale(init_ratio*total_ratio,init_ratio*total_ratio);
float translateX = 0f;
float translateY = 0f;
translateX = (current_disX*current_ratio)+centerX*(1-current_ratio);
translateY = (current_disY*current_ratio)+centerY*(1-current_ratio);
Log.d(TAG, "onTouch: translateX = "+translateX);
Log.d(TAG, "onTouch: translateY = "+translateY);
matrix.postTranslate(translateX, translateY);
// matrix.postTranslate((disX+current_disX)+centerX*(total_ratio/2), (disY+current_disY)+centerY*(total_ratio/2));
imageView.setImageMatrix(matrix);
//
current_disX = translateX;
current_disY = translateY;
Log.d(TAG, "onTouch: getDistance = "+getDistance(event));
break;
default:
break;
}
} else if (event.getPointerCount() == 1) {
if (two_pointer_down == false) {
if (move == true) {
//处理单指移动
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
currentX = event.getX();
currentY = event.getY();
Log.d(TAG, "onTouch: currentX = " + currentX + " currentY = " + currentY);
break;
case MotionEvent.ACTION_MOVE:
nextX = event.getX();
nextY = event.getY();
Log.d(TAG, "onTouch: nextX = " + nextX + " nextY" + nextY);
disX = nextX - currentX;
disY = nextY - currentY;
Log.d(TAG, "onTouch: disX = " + disX + " disY" + disY);
matrix.reset();
matrix.postScale(init_ratio * total_ratio, init_ratio * total_ratio);
matrix.postTranslate(disX + current_disX, disY + current_disY);
imageView.setImageMatrix(matrix);
current_disX = disX + current_disX;
current_disY = disY + current_disY;
currentX = nextX;
currentY = nextY;
break;
default:
break;
}
} else {
// 处理单指涂鸦
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isDrawLine = true;
startPoint.set((event.getX() - current_disX) / (init_ratio * total_ratio), (event.getY() - current_disY) / (init_ratio * total_ratio));
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onTouch: isDrawLine = true");
float x = (event.getX() - current_disX) / (init_ratio * total_ratio);
float y = (event.getY() - current_disY) / (init_ratio * total_ratio);
drawingCanvas.drawLine(startPoint.x, startPoint.y, x, y, paint);
imageView.setImageBitmap(mutableBitmap);
startPoint.set(x, y);
break;
default:
break;
}
}
}
}
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
distance1 = 0;
Log.d(TAG, "onTouch: 0 = distance1 = "+distance1);
if(two_pointer_down == true) {
two_pointer_down = false;//当双指触摸都弹起的时候,才能进行单指操作
}
if(isDrawLine == true){
bitmap_backup(mutableBitmap);
isDrawLine = false;
}
break;
default:
break;
}
return true;
}
获取量触摸点间的距离,用于缩放
private float getDistance(MotionEvent event) {
float dx = event.getX(0) - event.getX(1);
float dy = event.getY(0) - event.getY(1);
return (float) Math.sqrt(dx * dx + dy * dy);
}
保存图片,就直接搞个button点击保存即可,调用如下参考代码(保存地址根据实际情况修改,这里添加随机数,防止覆盖之前保存的图片)
private void saveImage() {
int randomNumber = getRandomNumber(1, 999999);
save_path = "/storage/emulated/0/DCIM/Doodle/"+"final_image"+randomNumber+".jpg";
Log.d(TAG, "saveImage: "+save_path);
FileOutputStream outputStream;
try {
outputStream = new FileOutputStream(save_path);
mutableBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.close();
Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
//保存图片后,需要将对应图片插入到图库
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(save_path))));
TextView save_image_position = findViewById(R.id.save_image_position);
save_image_position.setText("图片位置:"+save_path);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show();
}
}
以上差不多就是涂鸦的全部代码,因为是从整个工程里面抠出来的,可能会有些变量为定义啥的,注意甄别一下。然后需要保存和从手机内存里打开图片需要在Mainfest.xml中添加一下权限,添加了权限也要在手机设置里确认一下app是否有权限访问内存,否则可能会有预期之外的错误发生。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
本代码GitHub 链接 Android Studio工程,无需拆封,可直接食用https://github.com/0MakkaPakka0/MyDoodleC.git
之前有一位大佬专门写的涂鸦app,功能特别齐全,体验感俱佳,有github工程。链接如下奉上android图片涂鸦,具有设置画笔,撤销,缩放移动等功能(二)_forward123_的博客-CSDN博客