包含功能:
包含 获取当前画板的截图、设置画笔样式、获取画笔样式、设置画笔宽度、获取画笔宽度、设置画笔颜色、获取画笔颜色、加载图片、获取图片位图对象、设置图片位图对象,并在画布上绘制图片、撤销上一步操作、重做上一步撤销的操作、清空所有绘图路径,重新绘制位图
自定义视图组件
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<Path> 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();}@Overridepublic 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;}@Overridepublic 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);}@Overrideprotected 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);}@Overrideprotected 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);}/*** 触摸事件处理方法,用于绘制路径*/@Overridepublic 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();}
}
用法
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.zx.drawing_board.DrawingBoardandroid:id="@+id/fp"android:layout_width="match_parent"android:layout_height="match_parent"/></RelativeLayout>
效果