前言
Android 屏幕手写签名的原理就是把手机屏幕当作画板,把用户手指当作画笔,手指在屏幕上在屏幕上划来划去,屏幕就会显示手指的移动轨迹,就像画笔在画板上写字一样。实现手写签名需要结合绘图的路径工具 Path ,在有按下动作时调用 Path 对象的 moveTo 方法,将路径起始点移动到触摸点;在有移动操作时调用 Path 对象的 quadTo 方法,将记录本次触摸点与上次触摸点之间的路径;在有移动操作与提起动作时调用 Canvas 对象的 drawPath 方法,将本次触摸绘制在画布上。
效果图
手写签名.jpg功能
- 空白画板手写
- 实现笔锋效果
- 支持橡皮擦,撤回/恢复,清空画布功能
- 支持画笔颜色大小设置
- 支持传入初始图片
- 支持画布大小设置,文字区域裁剪
- 主题颜色设置
- 支持传入初始显示图片
代码
- 布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rl_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:clipToPadding="true"
android:fitsSystemWindows="true"
android:orientation="vertical">
<include
android:id="@+id/actionbar"
layout="@layout/sign_actionbar"
android:layout_width="match_parent"
android:layout_height="@dimen/sign_actionbar_height"
android:layout_alignParentTop="true" />
<com.hkt.handwritten.view.PaintView
android:id="@+id/paint_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/divider"
android:layout_below="@id/actionbar"
android:layout_margin="12dp"
android:background="@drawable/shape_dash_bg"/>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_above="@+id/setting"
android:background="@color/sign_border_gray" />
<LinearLayout
android:id="@+id/setting"
android:layout_width="match_parent"
android:layout_height="@dimen/y105"
android:layout_alignParentBottom="true"
android:background="@drawable/bottom_bg_shape"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/btn_hand"
android:layout_width="@dimen/x38"
android:layout_height="@dimen/y38"
android:layout_alignParentLeft="true"
android:layout_marginRight="@dimen/x40"
android:scaleType="centerCrop"
android:layout_weight="1"
android:background="@drawable/sign_bg_btn_clicked"
android:src="@mipmap/sign_ic_hand"
android:visibility="gone" />
<ImageView
android:id="@+id/btn_undo"
android:layout_width="@dimen/x78"
android:layout_height="@dimen/y78"
android:scaleType="centerCrop"
android:layout_marginRight="@dimen/x40"
android:layout_toLeftOf="@+id/btn_redo"
android:padding="@dimen/x12"
android:background="@drawable/sign_bg_btn_clicked"
android:src="@mipmap/sign_ic_undo" />
<ImageView
android:id="@+id/btn_redo"
android:layout_width="@dimen/x78"
android:layout_height="@dimen/y78"
android:scaleType="centerCrop"
android:layout_marginRight="@dimen/x40"
android:layout_toLeftOf="@+id/btn_clear"
android:background="@drawable/sign_bg_btn_clicked"
android:padding="@dimen/x12"
android:src="@mipmap/sign_ic_redo" />
<ImageView
android:id="@+id/btn_pen"
android:layout_width="@dimen/x78"
android:layout_height="@dimen/y78"
android:scaleType="centerCrop"
android:layout_marginRight="@dimen/x40"
android:layout_toLeftOf="@+id/btn_setting"
android:padding="@dimen/x12"
android:background="@drawable/sign_bg_btn_clicked"
android:src="@mipmap/sign_ic_pen" />
<ImageView
android:id="@+id/btn_clear"
android:layout_width="@dimen/x78"
android:layout_height="@dimen/y78"
android:layout_marginRight="@dimen/x40"
android:layout_toLeftOf="@+id/btn_pen"
android:padding="@dimen/x12"
android:scaleType="centerCrop"
android:background="@drawable/sign_bg_btn_clicked"
android:src="@mipmap/sign_ic_clear" />
<com.hkt.handwritten.view.CircleView
android:id="@+id/btn_setting"
android:layout_width="@dimen/x78"
android:layout_height="@dimen/y78"
android:layout_alignParentRight="true"
android:padding="@dimen/x12"
app:showOutBorder="false"
app:sizeLevel="2" />
</LinearLayout>
</RelativeLayout>
- 核心代码 PaintView.java
/**
* Created on 2021/12/1 11:58
*
* @author Gong Youqiang
*/
public class PaintView extends View {
public static final int TYPE_PEN = 0;
public static final int TYPE_ERASER = 1;
private Paint mPaint;
private Canvas mCanvas;
private Bitmap mBitmap;
private int strokeWidth;
private BasePen mStokeBrushPen;
/**
* 是否允许写字
*/
private boolean isFingerEnable = true;
/**
* 是否橡皮擦模式
*/
private boolean isEraser = false;
/**
* 是否有绘制
*/
private boolean hasDraw = false;
/**
* 画笔轨迹记录
*/
private StepOperator mStepOperation;
private StepCallback mCallback;
/**
* 是否可以撤销
*/
private boolean mCanUndo;
/**
* 是否可以恢复
*/
private boolean mCanRedo;
private int mWidth;
private int mHeight;
private boolean isDrawing = false;//是否正在绘制
private int toolType = 0; //记录手写笔类型:触控笔/手指
private Eraser eraser;
public PaintView(Context context) {
this(context, null);
}
public PaintView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PaintView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 初始化画板
*
* @param width 画板宽度
* @param height 画板高度
* @param path 初始图片路径
*/
public void init(int width, int height, String path) {
this.mWidth = width;
this.mHeight = height;
mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_4444);
mStokeBrushPen = new SteelPen();
initPaint();
initCanvas();
mStepOperation = new StepOperator();
if (!TextUtils.isEmpty(path)) {
Bitmap bitmap = BitmapFactory.decodeFile(path);
resize(bitmap, mWidth, mHeight);
} else {
mStepOperation.addBitmap(mBitmap);
}
//橡皮擦
eraser = new Eraser(getResources().getDimensionPixelSize(R.dimen.sign_eraser_size));
}
/**
* 初始画笔设置
*/
private void initPaint() {
strokeWidth = DisplayUtil.dip2px(getContext(), PaintSettingWindow.PEN_SIZES[PenConfig.PAINT_SIZE_LEVEL]);
mPaint = new Paint();
mPaint.setColor(PenConfig.PAINT_COLOR);
mPaint.setStrokeWidth(strokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAlpha(0xFF);
mPaint.setAntiAlias(true);
mPaint.setStrokeMiter(1.0f);
mStokeBrushPen.setPaint(mPaint);
}
private void initCanvas() {
mCanvas = new Canvas(mBitmap);
//设置画布的背景色为透明
mCanvas.drawColor(Color.TRANSPARENT);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
if (!isEraser) {
mStokeBrushPen.draw(canvas);
}
super.onDraw(canvas);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
toolType = event.getToolType(event.getActionIndex());
if (!isFingerEnable && toolType != MotionEvent.TOOL_TYPE_STYLUS) {
return false;
}
if (isEraser) {
eraser.handleEraserEvent(event, mCanvas);
} else {
mStokeBrushPen.onTouchEvent(event, mCanvas);
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
isDrawing = false;
break;
case MotionEvent.ACTION_MOVE:
hasDraw = true;
mCanUndo = true;
isDrawing = true;
break;
case MotionEvent.ACTION_CANCEL:
isDrawing = false;
break;
case MotionEvent.ACTION_UP:
if (mStepOperation != null && isDrawing) {
mStepOperation.addBitmap(mBitmap);
}
mCanUndo = !mStepOperation.currentIsFirst();
mCanRedo = !mStepOperation.currentIsLast();
if (mCallback != null) {
mCallback.onOperateStatusChanged();
}
isDrawing = false;
break;
default:
break;
}
invalidate();
return true;
}
/**
* @return 判断是否有绘制内容在画布上
*/
public boolean isEmpty() {
return !hasDraw;
}
/**
* 撤销
*/
public void undo() {
if (mStepOperation == null || !mCanUndo) {
return;
}
if (!mStepOperation.currentIsFirst()) {
mCanUndo = true;
mStepOperation.undo(mBitmap);
hasDraw = true;
invalidate();
if (mStepOperation.currentIsFirst()) {
mCanUndo = false;
hasDraw = false;
}
} else {
mCanUndo = false;
hasDraw = false;
}
if (!mStepOperation.currentIsLast()) {
mCanRedo = true;
}
if (mCallback != null) {
mCallback.onOperateStatusChanged();
}
}
/**
* 恢复
*/
public void redo() {
if (mStepOperation == null || !mCanRedo) {
return;
}
if (!mStepOperation.currentIsLast()) {
mCanRedo = true;
mStepOperation.redo(mBitmap);
hasDraw = true;
invalidate();
if (mStepOperation.currentIsLast()) {
mCanRedo = false;
}
} else {
mCanRedo = false;
}
if (!mStepOperation.currentIsFirst()) {
mCanUndo = true;
}
if (mCallback != null) {
mCallback.onOperateStatusChanged();
}
}
/**
* 清除画布,记得清除点的集合
*/
public void reset() {
mBitmap.eraseColor(Color.TRANSPARENT);
hasDraw = false;
mStokeBrushPen.clear();
if (mStepOperation != null) {
mStepOperation.reset();
mStepOperation.addBitmap(mBitmap);
}
mCanRedo = false;
mCanUndo = false;
if (mCallback != null) {
mCallback.onOperateStatusChanged();
}
invalidate();
}
public void release() {
destroyDrawingCache();
if (mBitmap != null) {
mBitmap.recycle();
mBitmap = null;
}
if (mStepOperation != null) {
mStepOperation.freeBitmaps();
mStepOperation = null;
}
}
public interface StepCallback {
/**
* 操作变更
*/
void onOperateStatusChanged();
}
public void setStepCallback(StepCallback callback) {
this.mCallback = callback;
}
/**
* 设置画笔样式
*
* @param penType
*/
public void setPenType(int penType) {
isEraser = false;
switch (penType) {
case TYPE_PEN:
mStokeBrushPen = new SteelPen();
break;
case TYPE_ERASER:
isEraser = true;
break;
}
//设置
if (mStokeBrushPen.isNullPaint()) {
mStokeBrushPen.setPaint(mPaint);
}
invalidate();
}
/**
* 设置画笔大小
*
* @param width 大小
*/
public void setPaintWidth(int width) {
if (mPaint != null) {
mPaint.setStrokeWidth(DisplayUtil.dip2px(getContext(), width));
// eraser.setPaintWidth(DisplayUtil.dip2px(getContext(), width));
mStokeBrushPen.setPaint(mPaint);
invalidate();
}
}
/**
* 设置画笔颜色
*
* @param color 颜色
*/
public void setPaintColor(int color) {
if (mPaint != null) {
mPaint.setColor(color);
mStokeBrushPen.setPaint(mPaint);
invalidate();
}
}
/**
* 构建Bitmap
*
* @return 所绘制的bitmap
*/
public Bitmap buildAreaBitmap(boolean isCrop) {
if (!hasDraw) {
return null;
}
Bitmap result;
if (isCrop) {
result = BitmapUtil.clearBlank(mBitmap, 50, Color.TRANSPARENT);
} else {
result = mBitmap;
}
destroyDrawingCache();
return result;
}
public boolean isFingerEnable() {
return isFingerEnable;
}
public void setFingerEnable(boolean fingerEnable) {
isFingerEnable = fingerEnable;
}
public boolean isEraser() {
return isEraser;
}
public boolean canUndo() {
return mCanUndo;
}
public boolean canRedo() {
return mCanRedo;
}
public Bitmap getBitmap() {
return mBitmap;
}
/**
* 图片大小调整适配画布宽高
*
* @param bitmap 源图
* @param width 新宽度
* @param height 新高度
*/
public void resize(Bitmap bitmap, int width, int height) {
if (mBitmap != null) {
if (width >= this.mWidth) {
height = width * mBitmap.getHeight() / mBitmap.getWidth();
}
this.mWidth = width;
this.mHeight = height;
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
restoreLastBitmap(bitmap, mBitmap);
initCanvas();
if (mStepOperation != null) {
mStepOperation.addBitmap(mBitmap);
}
invalidate();
}
}
/**
* 恢复最后画的bitmap
*
* @param srcBitmap 最后的bitmap
* @param newBitmap 新bitmap
*/
private void restoreLastBitmap(Bitmap srcBitmap, Bitmap newBitmap) {
try {
if (srcBitmap == null || srcBitmap.isRecycled()) {
return;
}
srcBitmap = BitmapUtil.zoomImg(srcBitmap, newBitmap.getWidth());
//缩放后如果还是超出新图宽高,继续缩放
if (srcBitmap.getWidth() > newBitmap.getWidth() || srcBitmap.getHeight() > newBitmap.getHeight()) {
srcBitmap = BitmapUtil.zoomImage(srcBitmap, newBitmap.getWidth(), newBitmap.getHeight());
}
//保存所有的像素的数组,图片宽×高
int[] pixels = new int[srcBitmap.getWidth() * srcBitmap.getHeight()];
srcBitmap.getPixels(pixels, 0, srcBitmap.getWidth(), 0, 0, srcBitmap.getWidth(), srcBitmap.getHeight());
newBitmap.setPixels(pixels, 0, srcBitmap.getWidth(), 0, 0,
srcBitmap.getWidth(), srcBitmap.getHeight());
} catch (OutOfMemoryError e) {
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = onMeasureR(0, widthMeasureSpec);
int height = onMeasureR(1, heightMeasureSpec);
setMeasuredDimension(width, height);
}
/**
* 计算控件宽高
*/
public int onMeasureR(int attr, int oldMeasure) {
int newSize = 0;
int mode = MeasureSpec.getMode(oldMeasure);
int oldSize = MeasureSpec.getSize(oldMeasure);
switch (mode) {
case MeasureSpec.EXACTLY:
newSize = oldSize;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
float value = mWidth;
if (attr == 0) {
if (mBitmap != null) {
value = mBitmap.getWidth();
}
// 控件的宽度
newSize = (int) (getPaddingLeft() + value + getPaddingRight());
} else if (attr == 1) {
value = mHeight;
if (mBitmap != null) {
value = mBitmap.getHeight();
}
// 控件的高度
newSize = (int) (getPaddingTop() + value + getPaddingBottom());
}
break;
default:
break;
}
return newSize;
}
public Bitmap getLastBitmap() {
return mBitmap;
}
}
- 核心代码 SteelPen.java
/**
* Created on 2021/12/1 11:38
* 钢笔
* @author Gong Youqiang
*/
public class SteelPen extends BasePen {
@Override
protected void doPreDraw(Canvas canvas) {
for (int i = 1; i < mHWPointList.size(); i++) {
ControllerPoint point = mHWPointList.get(i);
drawToPoint(canvas, point, mPaint);
mCurPoint = point;
}
}
@Override
protected void doMove(double curDis) {
int steps = 1 + (int) curDis / STEP_FACTOR;
double step = 1.0 / steps;
for (double t = 0; t < 1.0; t += step) {
ControllerPoint point = mBezier.getPoint(t);
mHWPointList.add(point);
}
}
@Override
protected void doDraw(Canvas canvas, ControllerPoint point, Paint paint) {
drawLine(canvas, mCurPoint.x, mCurPoint.y, mCurPoint.width, point.x,
point.y, point.width, paint);
}
/**
* 绘制方法,实现笔锋效果
*/
private void drawLine(Canvas canvas, double x0, double y0, double w0, double x1, double y1, double w1, Paint paint) {
//求两个数字的平方根 x的平方+y的平方在开方记得X的平方+y的平方=1,这就是一个园
double curDis = Math.hypot(x0 - x1, y0 - y1);
int steps;
//绘制的笔的宽度是多少,绘制多少个椭圆
if (paint.getStrokeWidth() < 6) {
steps = 1 + (int) (curDis / 2);
} else if (paint.getStrokeWidth() > 60) {
steps = 1 + (int) (curDis / 4);
} else {
steps = 1 + (int) (curDis / 3);
}
double deltaX = (x1 - x0) / steps;
double deltaY = (y1 - y0) / steps;
double deltaW = (w1 - w0) / steps;
double x = x0;
double y = y0;
double w = w0;
for (int i = 0; i < steps; i++) {
RectF oval = new RectF();
float top = (float) (y - w / 2.0f);
float left = (float) (x - w / 4.0f);
float right = (float) (x + w / 4.0f);
float bottom = (float) (y + w / 2.0f);
oval.set(left, top, right, bottom);
//最基本的实现,通过点控制线,绘制椭圆
canvas.drawOval(oval, paint);
x += deltaX;
y += deltaY;
w += deltaW;
}
}
}
- 使用
public class HandWrittenBoardActivity extends BaseActivity implements View.OnClickListener, PaintView.StepCallback {
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private ImageView mHandView; //切换 滚动/手写
private ImageView mUndoView;
private ImageView mRedoView;
private ImageView mPenView;
private ImageView mClearView;
private CircleView mSettingView;
private PaintView mPaintView;
private ProgressDialog mSaveProgressDlg;
private static final int MSG_SAVE_SUCCESS = 1;
private static final int MSG_SAVE_FAILED = 2;
private String mSavePath;
private boolean hasSize = false;
private float mWidth;
private float mHeight;
private float widthRate = 1.0f;
private float heightRate = 1.0f;
private int bgColor;
private boolean isCrop;
private String format;
private PaintSettingWindow settingWindow;
private static String[] PERMISSIONS_STORAGE = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE" };
@Override
protected int getLayout() {
return R.layout.activity_hand_written_board;
}
@Override
protected void initView() {
verifyStoragePermissions(this);
View mCancelView = findViewById(R.id.tv_cancel);
View mOkView = findViewById(R.id.tv_ok);
mPaintView = findViewById(R.id.paint_view);
mHandView = findViewById(R.id.btn_hand);
mUndoView = findViewById(R.id.btn_undo);
mRedoView = findViewById(R.id.btn_redo);
mPenView = findViewById(R.id.btn_pen);
mClearView = findViewById(R.id.btn_clear);
mSettingView = findViewById(R.id.btn_setting);
mUndoView.setOnClickListener(this);
mRedoView.setOnClickListener(this);
mPenView.setOnClickListener(this);
mClearView.setOnClickListener(this);
mSettingView.setOnClickListener(this);
mHandView.setOnClickListener(this);
mCancelView.setOnClickListener(this);
mOkView.setOnClickListener(this);
mPenView.setSelected(true);
mUndoView.setEnabled(false);
mRedoView.setEnabled(false);
mClearView.setEnabled(!mPaintView.isEmpty());
mPaintView.setStepCallback(this);
PenConfig.PAINT_SIZE_LEVEL = PenConfig.getPaintTextLevel(this);
PenConfig.PAINT_COLOR = PenConfig.getPaintColor(this);
mSettingView.setPaintColor(PenConfig.PAINT_COLOR);
mSettingView.setRadiusLevel(PenConfig.PAINT_SIZE_LEVEL);
setThemeColor(PenConfig.THEME_COLOR);
BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, Color.WHITE);
BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_pen, Color.WHITE);
BitmapUtil.setImage(mRedoView, R.mipmap.sign_ic_redo, mPaintView.canRedo() ? Color.WHITE : Color.LTGRAY);
BitmapUtil.setImage(mUndoView, R.mipmap.sign_ic_undo, mPaintView.canUndo() ? Color.WHITE : Color.LTGRAY);
BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, !mPaintView.isEmpty() ? Color.WHITE : Color.LTGRAY);
}
/**
* 获取画布默认宽度
*
* @return
*/
private int getResizeWidth() {
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && dm.widthPixels < dm.heightPixels) {
return (int) (dm.heightPixels * widthRate);
}
return (int) (dm.widthPixels * widthRate);
}
/**
* 获取画布默认高度
*
* @return
*/
private int getResizeHeight() {
int toolBarHeight = getResources().getDimensionPixelSize(R.dimen.sign_grid_toolbar_height);
int actionbarHeight = getResources().getDimensionPixelSize(R.dimen.sign_actionbar_height);
int statusBarHeight = StatusBarCompat.getStatusBarHeight(this);
int otherHeight = toolBarHeight + actionbarHeight + statusBarHeight;
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && dm.widthPixels < dm.heightPixels) {
return (int) ((dm.widthPixels - otherHeight) * heightRate);
}
return (int) ((dm.heightPixels - otherHeight) * heightRate);
}
@Override
protected void initData() {
isCrop = getIntent().getBooleanExtra("crop", false);
format = getIntent().getStringExtra("format");
bgColor = getIntent().getIntExtra("background", Color.TRANSPARENT);
String mInitPath = getIntent().getStringExtra("image");
float bitmapWidth = getIntent().getFloatExtra("width", 1.0f);
float bitmapHeight = getIntent().getFloatExtra("height", 1.0f);
if (bitmapWidth > 0 && bitmapWidth <= 1.0f) {
widthRate = bitmapWidth;
mWidth = getResizeWidth();
} else {
hasSize = true;
mWidth = bitmapWidth;
}
if (bitmapHeight > 0 && bitmapHeight <= 1.0f) {
heightRate = bitmapHeight;
mHeight = getResizeHeight();
} else {
hasSize = true;
mHeight = bitmapHeight;
}
//初始画板设置
if (!hasSize && !TextUtils.isEmpty(mInitPath)) {
Bitmap bitmap = BitmapFactory.decodeFile(mInitPath);
mWidth = bitmap.getWidth();
mHeight = bitmap.getHeight();
hasSize = true;
}
mPaintView.init((int) mWidth, (int) mHeight, mInitPath);
if (bgColor != Color.TRANSPARENT) {
mPaintView.setBackgroundColor(bgColor);
}
}
/**
* 横竖屏切换
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (settingWindow != null) {
settingWindow.dismiss();
}
int resizeWidth = getResizeWidth();
int resizeHeight = getResizeHeight();
if (mPaintView != null && !hasSize) {
mPaintView.resize(mPaintView.getLastBitmap(), resizeWidth, resizeHeight);
}
}
public static void verifyStoragePermissions(Activity activity) {
try {
//检测是否有写的权限
int permission = ActivityCompat.checkSelfPermission(activity,
"android.permission.WRITE_EXTERNAL_STORAGE");
if (permission != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
int i = v.getId();
if (i == R.id.btn_setting) {
showPaintSettingWindow();
} else if (i == R.id.btn_hand) {
//切换是否允许写字
mPaintView.setFingerEnable(!mPaintView.isFingerEnable());
if (mPaintView.isFingerEnable()) {
BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_hand, PenConfig.THEME_COLOR);
} else {
BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_drag, PenConfig.THEME_COLOR);
}
} else if (i == R.id.btn_clear) {
mPaintView.reset();
} else if (i == R.id.btn_undo) {
mPaintView.undo();
} else if (i == R.id.btn_redo) {
mPaintView.redo();
} else if (i == R.id.btn_pen) {
if (!mPaintView.isEraser()) {
mPaintView.setPenType(PaintView.TYPE_ERASER);
BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_eraser, Color.WHITE);
} else {
mPaintView.setPenType(PaintView.TYPE_PEN);
BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_pen, Color.WHITE);
}
} else if (i == R.id.tv_ok) {
save();
} else if (i == R.id.tv_cancel) {
if (!mPaintView.isEmpty()) {
showQuitTip();
} else {
setResult(RESULT_CANCELED);
finish();
}
}
}
@Override
protected void onDestroy() {
if (mPaintView != null) {
mPaintView.release();
}
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
super.onDestroy();
}
/**
* 弹出画笔设置
*/
private void showPaintSettingWindow() {
settingWindow = new PaintSettingWindow(this);
settingWindow.setSettingListener(new PaintSettingWindow.OnSettingListener() {
@Override
public void onColorSetting(int color) {
mPaintView.setPaintColor(color);
mSettingView.setPaintColor(color);
}
@Override
public void onSizeSetting(int index) {
mSettingView.setRadiusLevel(index);
mPaintView.setPaintWidth(PaintSettingWindow.PEN_SIZES[index]);
}
});
View contentView = settingWindow.getContentView();
//需要先测量,PopupWindow还未弹出时,宽高为0
contentView.measure(SystemUtil.makeDropDownMeasureSpec(settingWindow.getWidth()),
SystemUtil.makeDropDownMeasureSpec(settingWindow.getHeight()));
int padding = DisplayUtil.dip2px(this, 45);
settingWindow.popAtTopRight();
settingWindow.showAsDropDown(mSettingView, -540, -2 * padding - settingWindow.getContentView().getMeasuredHeight());
}
private void initSaveProgressDlg() {
mSaveProgressDlg = new ProgressDialog(this);
mSaveProgressDlg.setMessage("正在保存,请稍候...");
mSaveProgressDlg.setCancelable(false);
}
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAVE_FAILED:
mSaveProgressDlg.dismiss();
Toast.makeText(getApplicationContext(), "保存失败", Toast.LENGTH_SHORT).show();
break;
case MSG_SAVE_SUCCESS:
mSaveProgressDlg.dismiss();
Intent intent = new Intent();
intent.putExtra(PenConfig.SAVE_PATH, mSavePath);
setResult(RESULT_OK, intent);
break;
default:
break;
}
}
};
/**
* 保存
*/
private void save() {
if (mPaintView.isEmpty()) {
Toast.makeText(getApplicationContext(), "没有写入任何文字", Toast.LENGTH_SHORT).show();
return;
}
//先检查是否有存储权限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getApplicationContext(), "没有读写存储的权限", Toast.LENGTH_SHORT).show();
return;
}
if (mSaveProgressDlg == null) {
initSaveProgressDlg();
}
mSaveProgressDlg.show();
new Thread(() -> {
try {
Bitmap result = mPaintView.buildAreaBitmap(isCrop);
if (PenConfig.FORMAT_JPG.equals(format) && bgColor == Color.TRANSPARENT) {
bgColor = Color.WHITE;
}
if (bgColor != Color.TRANSPARENT) {
result = BitmapUtil.drawBgToBitmap(result, bgColor);
}
if (result == null) {
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
return;
}
mSavePath = BitmapUtil.saveImage(HandWrittenBoardActivity.this, result, 100, format);
if (mSavePath != null) {
mHandler.obtainMessage(MSG_SAVE_SUCCESS).sendToTarget();
} else {
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
}
} catch (Exception e) {
}
}).start();
}
/**
* 画布有操作
*/
@Override
public void onOperateStatusChanged() {
mUndoView.setEnabled(mPaintView.canUndo());
mRedoView.setEnabled(mPaintView.canRedo());
mClearView.setEnabled(!mPaintView.isEmpty());
BitmapUtil.setImage(mRedoView, R.mipmap.sign_ic_redo, mPaintView.canRedo() ? Color.WHITE : Color.LTGRAY);
BitmapUtil.setImage(mUndoView, R.mipmap.sign_ic_undo, mPaintView.canUndo() ? Color.WHITE : Color.LTGRAY);
BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, !mPaintView.isEmpty() ? Color.WHITE : Color.LTGRAY);
}
@Override
public void onBackPressed() {
if (!mPaintView.isEmpty()) {
showQuitTip();
} else {
setResult(RESULT_CANCELED);
finish();
}
}
/**
* 弹出退出提示
*/
private void showQuitTip() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示")
.setMessage("当前文字未保存,是否退出?")
.setNegativeButton("取消", null)
.setPositiveButton("确定", (dialog, which) -> {
setResult(RESULT_CANCELED);
finish();
});
builder.show();
}
}
网友评论