包含功能:
包含 获取当前画板的截图、设置画笔样式、获取画笔样式、设置画笔宽度、获取画笔宽度、设置画笔颜色、获取画笔颜色、加载图片、获取图片位图对象、设置图片位图对象,并在画布上绘制图片、撤销上一步操作、重做上一步撤销的操作、清空所有绘图路径,重新绘制位图
自定义视图组件
package com.zx.drawing_board;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;
public class DrawingBoard extends View {
// 上下文对象,用于获取资源和应用程序信息
public Context context;
// 画布对象,用于绘制图形
public Canvas canvas;
// 画笔对象,用于设置绘制样式和颜色
public Paint paint;
// 位图对象,用于在其中进行绘制操作
public Bitmap bitmap;
// 绘制路径对象,记录用户绘制的路径
public Path path;
// 图片的URI地址
public Uri uri;
// 图片位图对象,用于加载图片
private Bitmap mImageBitmap;
// 保存用户绘制路径的栈结构
private Stack paths = new Stack<>();
/**
* 构造函数,创建一个新的FingerPainterView对象
* @param context 上下文对象,用于获取资源和应用程序信息
*/
public DrawingBoard(Context context) {
super(context);
// 执行初始化方法
init(context);
}
/**
* 构造函数,创建一个新的FingerPainterView对象
* @param context 上下文对象,用于获取资源和应用程序信息
* @param attrs 属性集合对象,用于获取视图的自定义属性
*/
public DrawingBoard(Context context, AttributeSet attrs) {
super(context, attrs);
// 执行初始化方法
init(context);
}
/**
* 构造函数,创建一个新的FingerPainterView对象
* @param context 上下文对象,用于获取资源和应用程序信息
* @param attrs 属性集合对象,用于获取视图的自定义属性
* @param defStyle 样式属性,用于设置默认样式
*/
public DrawingBoard(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 执行初始化方法
init(context);
}
/**
* 获取当前画板的截图
* @return 画板的截图
*/
public Bitmap getScreenshot() {
return Bitmap.createBitmap(bitmap);
}
/**
* 初始化方法,设置默认的画笔样式和颜色
* @param context 上下文对象,用于获取资源和应用程序信息
*/
private void init(Context context) {
this.context = context;
// 创建路径对象和画笔对象
path = new Path();
paint = new Paint();
// 默认的画笔样式和颜色
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(20);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setARGB(255,0,0,0);
}
/**
* 设置画笔样式
* @param brush 画笔样式
*/
public void setBrush(Paint.Cap brush) {
paint.setStrokeCap(brush);
}
/**
* 获取画笔样式
* @return 画笔样式
*/
public Paint.Cap getBrush() {
return paint.getStrokeCap();
}
/**
* 设置画笔宽度
* @param width 画笔宽度
*/
public void setBrushWidth(int width) {
paint.setStrokeWidth(width);
}
/**
* 获取画笔宽度
* @return 画笔宽度
*/
public int getBrushWidth() {
return (int) paint.getStrokeWidth();
}
/**
* 设置画笔颜色
* @param colour 画笔颜色
*/
public void setColour(int colour) {
paint.setColor(colour);
}
/**
* 获取画笔颜色
* @return 画笔颜色
*/
public int getColour() {
return paint.getColor();
}
/**
* 加载图片
* @param uri 图片的URI地址
*/
public void load(Uri uri) {
this.uri = uri;
}
/**
* 获取图片位图对象
* @return 图片位图对象
*/
public Bitmap getmImageBitmap() {
return mImageBitmap;
}
/**
* 设置图片位图对象,并在画布上绘制图片
* @param mImageBitmap 图片位图对象
*/
public void setmImageBitmap(Bitmap mImageBitmap) {
this.mImageBitmap = mImageBitmap;
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(mImageBitmap, 0, 0, paint);
}
/**
* 撤销上一步操作
*/
public void undo() {
if (!paths.isEmpty()) {
// 移除最近的路径,并重新绘制位图
paths.pop();
redrawBitmap();
}
}
/**
* 重做上一步撤销的操作
*/
public void redo() {
if (!paths.isEmpty()) {
// 将最近撤销的路径重新添加到绘图路径中,并重新绘制位图
Path lastPath = paths.peek();
paths.push(new Path(lastPath));
redrawBitmap();
}
}
/**
* 清空所有绘图路径,重新绘制位图
*/
public void clear() {
paths.clear();
redrawBitmap();
}
@Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
// 保存父类视图状态
bundle.putParcelable("superState", super.onSaveInstanceState());
try {
// 将位图保存到临时缓存文件中,以克服Binder事务大小限制
File f = File.createTempFile("fingerpaint", ".png", context.getCacheDir());
bitmap.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(f));
// 将临时文件名保存到bundle中
bundle.putString("tempfile", f.getAbsolutePath());
} catch(IOException e) {
Log.e("FingerPainterView", e.toString());
}
return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
try {
// 从bundle中获取缓存文件
File f = new File(bundle.getString("tempfile"));
Bitmap b = BitmapFactory.decodeStream(new FileInputStream(f));
// 需要复制位图以创建可变版本
bitmap = b.copy(b.getConfig(), true);
b.recycle();
f.delete();
} catch(IOException e) {
Log.e("FingerPainterView", e.toString());
}
state = bundle.getParcelable("superState");
}
super.onRestoreInstanceState(state);
}
@Override
protected void onDraw(Canvas canvas) {
// 画布是白色的,并在顶部绘制带有alpha通道的位图
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(bitmap, 0, 0, paint);
// 显示当前的绘图路径
for (Path p : paths) {
canvas.drawPath(p, paint);
}
canvas.drawPath(path, paint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// 在Activity创建后,当视图被膨胀时调用
if(bitmap==null) {
if(uri!=null) {
try {
// 尝试加载提供的uri,并进行缩放以适应我们的画布
InputStream stream = context.getContentResolver().openInputStream(uri);
Bitmap bm = BitmapFactory.decodeStream(stream);
bitmap = Bitmap.createScaledBitmap(bm, Math.max(w, h), Math.max(w, h), false);
stream.close();
bm.recycle();
} catch(IOException e) {
Log.e("FingerPainterView", e.toString());
}
}
else {
// 创建一个正方形位图,以便即使在旋转到横向时也可绘制
bitmap = Bitmap.createBitmap(Math.max(w,h), Math.max(w,h), Bitmap.Config.ARGB_8888);
}
}
canvas = new Canvas(bitmap);
}
/**
* 触摸事件处理方法,用于绘制路径
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 按下手指时,重置路径并移动到指定位置
path.reset();
path.moveTo(x, y);
path.lineTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
// 手指移动时,连线到当前位置
path.lineTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
// 手指抬起时,将路径保存,并重置路径
paths.push(new Path(path));
path.reset();
invalidate();
break;
}
return true;
}
/**
* 重新绘制位图,根据当前的绘图路径
*/
private void redrawBitmap() {
bitmap.eraseColor(Color.WHITE);
for (Path p : paths) {
canvas.drawPath(p, paint);
}
invalidate();
}
}
用法
效果