一、Android自定义View步骤 :

自定义属性;

选择和设置构造方法;

重写onMeasure()方法;

重写onDraw()方法;

重写onLayout()方法;

重写其他事件的方法(滑动监听等)。

二、代码实现

工具类代码:

package com.smt.saveviewtocream.utils;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.PorterDuff;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.View;

import androidx.annotation.ColorInt;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.OutputStream;

/**

* 描述: Android自定义实现手写签名功能

*/

public class LinePathView extends View {

private Context mContext;

/**

* 笔画X坐标起点

*/

private float mX;

/**

* 笔画Y坐标起点

*/

private float mY;

/**

* 手写画笔

*/

private final Paint mGesturePaint = new Paint();

/**

* 路径

*/

private final Path mPath = new Path();

/**

* 签名画笔

*/

private Canvas cacheCanvas;

/**

* 签名画布

*/

private Bitmap cachebBitmap;

/**

* 是否已经签名

*/

private boolean isTouched = false;

/**

* 画笔宽度 px;

*/

private int mPaintWidth = 10;

/**

* 前景色

*/

private int mPenColor = Color.BLACK;

/**

* 背景色(指最终签名结果文件的背景颜色,默认为透明色)

*/

private int mBackColor = Color.TRANSPARENT;

//签名开始与结束

private Touch touch;

public LinePathView(Context context) {

super(context);

init(context);

}

public LinePathView(Context context, AttributeSet attrs) {

super(context, attrs);

init(context);

}

public LinePathView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

init(context);

}

public void init(Context context) {

this.mContext = context;

//设置抗锯齿

mGesturePaint.setAntiAlias(true);

//设置签名笔画样式

mGesturePaint.setStyle(Paint.Style.STROKE);

//设置笔画宽度

mGesturePaint.setStrokeWidth(mPaintWidth);

//设置签名颜色

mGesturePaint.setColor(mPenColor);

}

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

//创建跟view一样大的bitmap,用来保存签名

cachebBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

cacheCanvas = new Canvas(cachebBitmap);

cacheCanvas.drawColor(mBackColor);

isTouched = false;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

if (touch != null) touch.OnTouch(true);

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

touchDown(event);

break;

case MotionEvent.ACTION_MOVE:

isTouched = true;

if (touch != null) touch.OnTouch(false);

touchMove(event);

break;

case MotionEvent.ACTION_UP:

//将路径画到bitmap中,即一次笔画完成才去更新bitmap,而手势轨迹是实时显示在画板上的。

cacheCanvas.drawPath(mPath, mGesturePaint);

mPath.reset();

break;

}

// 更新绘制

invalidate();

return true;

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//画此次笔画之前的签名

canvas.drawBitmap(cachebBitmap, 0, 0, mGesturePaint);

// 通过画布绘制多点形成的图形

canvas.drawPath(mPath, mGesturePaint);

}

// 手指点下屏幕时调用

private void touchDown(MotionEvent event) {

// 重置绘制路线

mPath.reset();

float x = event.getX();

float y = event.getY();

mX = x;

mY = y;

// mPath绘制的绘制起点

mPath.moveTo(x, y);

}

// 手指在屏幕上滑动时调用

private void touchMove(MotionEvent event) {

final float x = event.getX();

final float y = event.getY();

final float previousX = mX;

final float previousY = mY;

final float dx = Math.abs(x - previousX);

final float dy = Math.abs(y - previousY);

// 两点之间的距离大于等于3时,生成贝塞尔绘制曲线

if (dx >= 3 || dy >= 3) {

// 设置贝塞尔曲线的操作点为起点和终点的一半

float cX = (x + previousX) / 2;

float cY = (y + previousY) / 2;

// 二次贝塞尔,实现平滑曲线;previousX, previousY为操作点,cX, cY为终点

mPath.quadTo(previousX, previousY, cX, cY);

// 第二次执行时,第一次结束调用的坐标值将作为第二次调用的初始坐标值

mX = x;

mY = y;

}

}

/**

* 清除画板

*/

public void clear() {

if (cacheCanvas != null) {

isTouched = false;

//更新画板信息

mGesturePaint.setColor(mPenColor);

cacheCanvas.drawColor(mBackColor, PorterDuff.Mode.CLEAR);

mGesturePaint.setColor(mPenColor);

invalidate();

}

}

public interface Touch {

void OnTouch(boolean isTouch);

}

public Touch getTouch() {

return touch;

}

public void setTouch(Touch touch) {

this.touch = touch;

}

/**

* 保存画板

*

* @param path 保存到路径

*/

public void save(String path) throws IOException {

save(path, false, 0);

}

/**

* 保存画板

*

* @param path 保存到路径

* @param clearBlank 是否清除边缘空白区域

* @param blank 要保留的边缘空白距离

*/

public void save(String path, boolean clearBlank, int blank) throws IOException {

Bitmap bitmap = cachebBitmap;

//BitmapUtil.createScaledBitmapByHeight(srcBitmap, 300);// 压缩图片

if (clearBlank) {

bitmap = clearBlank(bitmap, blank);

}

ByteArrayOutputStream bos = new ByteArrayOutputStream();

bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);

byte[] buffer = bos.toByteArray();

if (buffer != null) {

File file = new File(path);

if (file.exists()) {

file.delete();

}

OutputStream outputStream = new FileOutputStream(file);

outputStream.write(buffer);

outputStream.close();

}

}

/**

* 获取画板的bitmap

*

* @return

*/

public Bitmap getBitMap() {

setDrawingCacheEnabled(true);

buildDrawingCache();

Bitmap bitmap = getDrawingCache();

setDrawingCacheEnabled(false);

return bitmap;

}

/**

* 逐行扫描 清楚边界空白。

*

* @param bp

* @param blank 边距留多少个像素

* @return

*/

private Bitmap clearBlank(Bitmap bp, int blank) {

int HEIGHT = bp.getHeight();

int WIDTH = bp.getWidth();

int top = 0, left = 0, right = 0, bottom = 0;

int[] pixs = new int[WIDTH];

boolean isStop;

//扫描上边距不等于背景颜色的第一个点

for (int y = 0; y < HEIGHT; y++) {

bp.getPixels(pixs, 0, WIDTH, 0, y, WIDTH, 1);

isStop = false;

for (int pix : pixs) {

if (pix != mBackColor) {

top = y;

isStop = true;

break;

}

}

if (isStop) {

break;

}

}

//扫描下边距不等于背景颜色的第一个点

for (int y = HEIGHT - 1; y >= 0; y--) {

bp.getPixels(pixs, 0, WIDTH, 0, y, WIDTH, 1);

isStop = false;

for (int pix : pixs) {

if (pix != mBackColor) {

bottom = y;

isStop = true;

break;

}

}

if (isStop) {

break;

}

}

pixs = new int[HEIGHT];

//扫描左边距不等于背景颜色的第一个点

for (int x = 0; x < WIDTH; x++) {

bp.getPixels(pixs, 0, 1, x, 0, 1, HEIGHT);

isStop = false;

for (int pix : pixs) {

if (pix != mBackColor) {

left = x;

isStop = true;

break;

}

}

if (isStop) {

break;

}

}

//扫描右边距不等于背景颜色的第一个点

for (int x = WIDTH - 1; x > 0; x--) {

bp.getPixels(pixs, 0, 1, x, 0, 1, HEIGHT);

isStop = false;

for (int pix : pixs) {

if (pix != mBackColor) {

right = x;

isStop = true;

break;

}

}

if (isStop) {

break;

}

}

if (blank < 0) {

blank = 0;

}

//计算加上保留空白距离之后的图像大小

left = left - blank > 0 ? left - blank : 0;

top = top - blank > 0 ? top - blank : 0;

right = right + blank > WIDTH - 1 ? WIDTH - 1 : right + blank;

bottom = bottom + blank > HEIGHT - 1 ? HEIGHT - 1 : bottom + blank;

return Bitmap.createBitmap(bp, left, top, right - left, bottom - top);

}

/**

* 设置画笔宽度 默认宽度为10px

*

* @param mPaintWidth

*/

public void setPaintWidth(int mPaintWidth) {

mPaintWidth = mPaintWidth > 0 ? mPaintWidth : 10;

this.mPaintWidth = mPaintWidth;

mGesturePaint.setStrokeWidth(mPaintWidth);

}

public void setBackColor(@ColorInt int backColor) {

mBackColor = backColor;

}

/**

* 设置画笔颜色

*

* @param mPenColor

*/

public void setPenColor(int mPenColor) {

this.mPenColor = mPenColor;

mGesturePaint.setColor(mPenColor);

}

/**

* 是否有签名

*

* @return

*/

public boolean getTouched() {

return isTouched;

}

}

三、功能实现

layout_main.xml:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@android:color/white"

android:fitsSystemWindows="true"

android:orientation="vertical">

android:id="@+id/mPathView"

android:layout_width="match_parent"

android:layout_height="0dp"

android:layout_weight="1"

android:background="#FFFFFF" />

android:layout_width="match_parent"

android:layout_height="@dimen/qb_px_60"

android:orientation="horizontal">

android:id="@+id/mBtnClear"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:layout_weight="1"

android:text="清除" />

android:id="@+id/mBtnSave"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:layout_weight="1"

android:text="保存" />

Maintivity.kt:

import android.graphics.Color

import android.os.Bundle

import android.widget.Toast

import androidx.appcompat.app.AppCompatActivity

import com.smt.saveviewtocream.R

import kotlinx.android.synthetic.main.activity_font_siaze.*

import java.io.IOException

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_font_siaze)

//修改背景、笔宽、颜色

mPathView.setBackColor(Color.WHITE)

mPathView.setPaintWidth(20)

mPathView.setPenColor(Color.BLACK)

mPathView.clear()

//清除

mBtnClear.setOnClickListener {

mPathView.clear()

mPathView.setBackColor(Color.WHITE)

mPathView.setPaintWidth(20)

mPathView.setPenColor(Color.BLACK)

}

//保存

mBtnSave.setOnClickListener {

if (mPathView.touched) {

try {

mPathView.save("/sdcard/qm.png", true, 10)

setResult(100)

Toast.makeText(this@MainActivity, "保存成功~", Toast.LENGTH_SHORT).show()

} catch (e: IOException) {

e.printStackTrace()

}

} else {

Toast.makeText(this@MainActivity, "您没有签名~", Toast.LENGTH_SHORT).show()

}

}

}

}

权限:

android 手写签批_Android自定义实现手写签名功能相关推荐

  1. android 手写签批_Android自定义View——手写签批

    接到一个领导批示保留原笔迹的功能,类似于绘画板,用户打开后可以绘制,点击完成后以图片的形式保存在本地,并且显示绘制后图片,上传服务器,达到保留原笔迹的目的.可以运用于签字.审批等. 效果图: 手写签批 ...

  2. android 手写签批_Android手写签批功能实现(适配Android6

    Android手写签批功能的实现在于三个点,mupdf,偏移量的计算,droidText0.5.jar 实际步骤: 使用muPdf将PDF加载出来 弹出透明的popwindow,popWindow使用 ...

  3. php 手写签批 手机办公_手写签批 打造无纸化办公的完美替代

    无纸办公,低碳环保,是近些年办公市场呈现的趋势,许多企业都开始推广OA办公系统来进行公文流转,传统的纸制办公转变为无纸化办公,极大提升了企业的运作效率. 随着科技发展,办公信息化也在与时俱进,许多原本 ...

  4. android 手写签批_Android手写签名效果

    任何画线的程序,都是先在界面上获取若干不连续的点,然后将这些点连成线. 一些常见的笔型比较好实现,比如说铅笔.钢笔等等,这类笔型的线条的宽度和线条的颜色是固定的,只需要将点连接成固定颜色和固定宽度的线 ...

  5. vue 手写签名_与众不同的手写签批

    随着移动互联网+时代的到来,手机成了我们日常生活中不可缺少的必备用品,它不仅仅是一个通讯工具,更是一台移动电脑.因此越来越多的单位把希望在手机上就能完成业务的处理,但也希望能还原线下办理的效果.因此, ...

  6. Android手写签批功能实现(适配Android6.0及以上)

     Android手写签批功能的实现在于三个点,mupdf,偏移量的计算,droidText0.5.jar 实际步骤: 使用muPdf将PDF加载出来 弹出透明的popwindow,popWindow使 ...

  7. Java 开源开发平台 O2OA V7.1.0 发布,人力资源办公平台及手写签批上线

    O2OA V7.1.0发布,新增了[人力资源办公平台]和版式公文中的[手写签批]功能. [人力资源办公平台]是使用O2OA开发平台设计并开发的一套适合于人力资源管理的办公系统.该平台将人力资源管理的所 ...

  8. h5 实现 画图 手写签批功能

    太久没更新博客了,又不知道写些什么.最近看到支付宝上的手写签批功能,就想着自己用h5实现了. 写完了后发现太简单了,就加上了写以前window 画图软件的功能. 以下为源码 <!DOCTYPE ...

  9. 开源O2OA办公平台:手写签批介绍

    概述 手写签批是建立在O2OA开发平台的公文编辑器组件基础上,允许用户在公文内容上进行手写.签名.批注.文字输入.擦除的一系列功能.手写签批功能可以直观的展现每位处理人对公文内容的批注,有效的提高办公 ...

最新文章

  1. 《C#高级编程》既不高级,也不基础,东拼西凑的味道十足。。。
  2. java中表示根号三_Java命名规范
  3. 计算两个日期相差的天数
  4. linux日志服务是哪个,『学了就忘』Linux日志管理 — 2.日志服务rsyslogd
  5. Html5做webapp中界面适配的问题总结
  6. 【计算机视觉】计算机视觉、模式识别、机器学习常用牛人主页链接
  7. 笨办法学 Python · 续 练习 5:`cat`
  8. Spring : @Value注解
  9. Nginx学习总结(5)——Nginx基本配置备忘
  10. html5有本地存储吗,HTML5的本地存储
  11. C++编程技术之 异常处理(上)
  12. 关于记录每天”要完成的事项“和”未完成事项“,(尤其是周末时,对未完成事项的记录一定要全)-----工作方式
  13. 2019蓝桥杯决赛Java_2019年蓝桥杯省赛总结
  14. Visual C++鼠标画线操作
  15. 手机连接WiFi无法上网,原来是这两个原因,快速解决上网
  16. 钉钉视频下载地瓜网络钉钉视频下载器
  17. Regression 中的 R方
  18. 多线程批量获取腾讯云磁盘分区状态
  19. JDBC如何防止SQL注入
  20. html中的函数怎么显示变量,css - 原生变量及使用函数 var()

热门文章

  1. Anchor-free系列——FoveaBox: Beyond Anchor-based Object Detector
  2. Cesium学习四:使用entity绘制polygon
  3. bzoj2121 字符串游戏
  4. (笔记)andro studio ——ProgressDialog
  5. 7-14 电话聊天狂人 (25分)
  6. jpinyin 将城市名汉字转化为拼音
  7. 爬虫03 —— 正则表达式
  8. Java环境配置JDK1.8u、JDK11u安装(附jdk1.8u64位jdk11安装包)
  9. 首字母排序侧边索引栏:WaveSideBar的简单使用
  10. 在线文档分享平台技术实现探讨