android 电子签名  手写签名 功能实现

这个手写的效果 就是一个 重写的的自定义的view  代码如下:

package com.example.hand.views;import java.util.ArrayList;
import java.util.List;import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;import com.example.hand.R;
import com.example.hand.utils.Bezier;
import com.example.hand.utils.ControlTimedPoints;
import com.example.hand.utils.TimedPoint;public class SignatureView extends View {// View stateprivate List<TimedPoint> mPoints;private boolean mIsEmpty;private float mLastTouchX;private float mLastTouchY;private float mLastVelocity;private float mLastWidth;private RectF mDirtyRect;// Configurable parametersprivate int mMinWidth;private int mMaxWidth;private float mVelocityFilterWeight;private OnSignedListener mOnSignedListener;private Paint mPaint = new Paint();private Path mPath = new Path();private Bitmap mSignatureBitmap = null;private Canvas mSignatureBitmapCanvas = null;public SignatureView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SignatureView, 0, 0);// Configurable parameterstry {mMinWidth = a.getDimensionPixelSize(R.styleable.SignatureView_minWidth, convertDpToPx(3));mMaxWidth = a.getDimensionPixelSize(R.styleable.SignatureView_maxWidth, convertDpToPx(7));mVelocityFilterWeight = a.getFloat(R.styleable.SignatureView_velocityFilterWeight, 0.9f);mPaint.setColor(a.getColor(R.styleable.SignatureView_penColor, Color.BLACK));} finally {a.recycle();}// Fixed parametersmPaint.setAntiAlias(true);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeCap(Paint.Cap.ROUND);mPaint.setStrokeJoin(Paint.Join.ROUND);// Dirty rectangle to update only the changed portion of the viewmDirtyRect = new RectF();clear();}/*** Set the pen color from a given resource. If the resource is not found,* {@link android.graphics.Color#BLACK} is assumed.* * @param colorRes*            the color resource.*/public void setPenColorRes(int colorRes) {try {setPenColor(getResources().getColor(colorRes));} catch (Resources.NotFoundException ex) {setPenColor(getResources().getColor(Color.BLACK));}}/*** Set the pen color from a given color.* * @param color*            the color.*/public void setPenColor(int color) {mPaint.setColor(color);}/*** Set the minimum width of the stroke in pixel.* * @param minWidth*            the width in dp.*/public void setMinWidth(float minWidth) {mMinWidth = convertDpToPx(minWidth);}/*** Set the maximum width of the stroke in pixel.* * @param maxWidth*            the width in dp.*/public void setMaxWidth(float maxWidth) {mMaxWidth = convertDpToPx(maxWidth);}/*** Set the velocity filter weight.* * @param velocityFilterWeight*            the weight.*/public void setVelocityFilterWeight(float velocityFilterWeight) {mVelocityFilterWeight = velocityFilterWeight;}public void clear() {mPoints = new ArrayList<TimedPoint>();mLastVelocity = 0;mLastWidth = (mMinWidth + mMaxWidth) / 2;mPath.reset();if (mSignatureBitmap != null) {mSignatureBitmap = null;ensureSignatureBitmap();}setIsEmpty(true);invalidate();}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (!isEnabled())return false;float eventX = event.getX();float eventY = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:getParent().requestDisallowInterceptTouchEvent(true);mPoints.clear();mPath.moveTo(eventX, eventY);mLastTouchX = eventX;mLastTouchY = eventY;addPoint(new TimedPoint(eventX, eventY));case MotionEvent.ACTION_MOVE:resetDirtyRect(eventX, eventY);addPoint(new TimedPoint(eventX, eventY));break;case MotionEvent.ACTION_UP:resetDirtyRect(eventX, eventY);addPoint(new TimedPoint(eventX, eventY));getParent().requestDisallowInterceptTouchEvent(true);setIsEmpty(false);break;default:return false;}// invalidate();invalidate((int) (mDirtyRect.left - mMaxWidth), (int) (mDirtyRect.top - mMaxWidth),(int) (mDirtyRect.right + mMaxWidth), (int) (mDirtyRect.bottom + mMaxWidth));return true;}@Overrideprotected void onDraw(Canvas canvas) {if (mSignatureBitmap != null) {canvas.drawBitmap(mSignatureBitmap, 0, 0, mPaint);}}public void setOnSignedListener(OnSignedListener listener) {mOnSignedListener = listener;}public boolean isEmpty() {return mIsEmpty;}public Bitmap getSignatureBitmap() {Bitmap originalBitmap = getTransparentSignatureBitmap();Bitmap whiteBgBitmap = Bitmap.createBitmap(originalBitmap.getWidth(), originalBitmap.getHeight(),Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(whiteBgBitmap);canvas.drawColor(Color.WHITE);canvas.drawBitmap(originalBitmap, 0, 0, null);return whiteBgBitmap;}public void setSignatureBitmap(Bitmap signature) {clear();ensureSignatureBitmap();RectF tempSrc = new RectF();RectF tempDst = new RectF();int dWidth = signature.getWidth();int dHeight = signature.getHeight();int vWidth = getWidth();int vHeight = getHeight();// Generate the required transform.tempSrc.set(0, 0, dWidth, dHeight);tempDst.set(0, 0, vWidth, vHeight);Matrix drawMatrix = new Matrix();drawMatrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.CENTER);Canvas canvas = new Canvas(mSignatureBitmap);canvas.drawBitmap(signature, drawMatrix, null);setIsEmpty(false);invalidate();}public Bitmap getTransparentSignatureBitmap() {ensureSignatureBitmap();return mSignatureBitmap;}public Bitmap getTransparentSignatureBitmap(boolean trimBlankSpace) {if (!trimBlankSpace) {return getTransparentSignatureBitmap();}ensureSignatureBitmap();int imgHeight = mSignatureBitmap.getHeight();int imgWidth = mSignatureBitmap.getWidth();int backgroundColor = Color.TRANSPARENT;int xMin = Integer.MAX_VALUE, xMax = Integer.MIN_VALUE, yMin = Integer.MAX_VALUE, yMax = Integer.MIN_VALUE;boolean foundPixel = false;// Find xMinfor (int x = 0; x < imgWidth; x++) {boolean stop = false;for (int y = 0; y < imgHeight; y++) {if (mSignatureBitmap.getPixel(x, y) != backgroundColor) {xMin = x;stop = true;foundPixel = true;break;}}if (stop)break;}// Image is empty...if (!foundPixel)return null;// Find yMinfor (int y = 0; y < imgHeight; y++) {boolean stop = false;for (int x = xMin; x < imgWidth; x++) {if (mSignatureBitmap.getPixel(x, y) != backgroundColor) {yMin = y;stop = true;break;}}if (stop)break;}// Find xMaxfor (int x = imgWidth - 1; x >= xMin; x--) {boolean stop = false;for (int y = yMin; y < imgHeight; y++) {if (mSignatureBitmap.getPixel(x, y) != backgroundColor) {xMax = x;stop = true;break;}}if (stop)break;}// Find yMaxfor (int y = imgHeight - 1; y >= yMin; y--) {boolean stop = false;for (int x = xMin; x <= xMax; x++) {if (mSignatureBitmap.getPixel(x, y) != backgroundColor) {yMax = y;stop = true;break;}}if (stop)break;}return Bitmap.createBitmap(mSignatureBitmap, xMin, yMin, xMax - xMin, yMax - yMin);}private void addPoint(TimedPoint newPoint) {mPoints.add(newPoint);if (mPoints.size() > 2) {// To reduce the initial lag make it work with 3 mPoints// by copying the first point to the beginning.if (mPoints.size() == 3)mPoints.add(0, mPoints.get(0));ControlTimedPoints tmp = calculateCurveControlPoints(mPoints.get(0), mPoints.get(1), mPoints.get(2));TimedPoint c2 = tmp.c2;tmp = calculateCurveControlPoints(mPoints.get(1), mPoints.get(2), mPoints.get(3));TimedPoint c3 = tmp.c1;Bezier curve = new Bezier(mPoints.get(1), c2, c3, mPoints.get(2));TimedPoint startPoint = curve.startPoint;TimedPoint endPoint = curve.endPoint;float velocity = endPoint.velocityFrom(startPoint);velocity = Float.isNaN(velocity) ? 0.0f : velocity;velocity = mVelocityFilterWeight * velocity + (1 - mVelocityFilterWeight) * mLastVelocity;// The new width is a function of the velocity. Higher velocities// correspond to thinner strokes.float newWidth = strokeWidth(velocity);// The Bezier's width starts out as last curve's final width, and// gradually changes to the stroke width just calculated. The new// width calculation is based on the velocity between the Bezier's// start and end mPoints.addBezier(curve, mLastWidth, newWidth);mLastVelocity = velocity;mLastWidth = newWidth;// Remove the first element from the list,// so that we always have no more than 4 mPoints in mPoints array.mPoints.remove(0);}}private void addBezier(Bezier curve, float startWidth, float endWidth) {ensureSignatureBitmap();float originalWidth = mPaint.getStrokeWidth();float widthDelta = endWidth - startWidth;float drawSteps = (float) Math.floor(curve.length());for (int i = 0; i < drawSteps; i++) {// Calculate the Bezier (x, y) coordinate for this step.float t = ((float) i) / drawSteps;float tt = t * t;float ttt = tt * t;float u = 1 - t;float uu = u * u;float uuu = uu * u;float x = uuu * curve.startPoint.x;x += 3 * uu * t * curve.control1.x;x += 3 * u * tt * curve.control2.x;x += ttt * curve.endPoint.x;float y = uuu * curve.startPoint.y;y += 3 * uu * t * curve.control1.y;y += 3 * u * tt * curve.control2.y;y += ttt * curve.endPoint.y;// Set the incremental stroke width and draw.mPaint.setStrokeWidth(startWidth + ttt * widthDelta);mSignatureBitmapCanvas.drawPoint(x, y, mPaint);expandDirtyRect(x, y);}mPaint.setStrokeWidth(originalWidth);}private ControlTimedPoints calculateCurveControlPoints(TimedPoint s1, TimedPoint s2, TimedPoint s3) {float dx1 = s1.x - s2.x;float dy1 = s1.y - s2.y;float dx2 = s2.x - s3.x;float dy2 = s2.y - s3.y;TimedPoint m1 = new TimedPoint((s1.x + s2.x) / 2.0f, (s1.y + s2.y) / 2.0f);TimedPoint m2 = new TimedPoint((s2.x + s3.x) / 2.0f, (s2.y + s3.y) / 2.0f);float l1 = (float) Math.sqrt(dx1 * dx1 + dy1 * dy1);float l2 = (float) Math.sqrt(dx2 * dx2 + dy2 * dy2);float dxm = (m1.x - m2.x);float dym = (m1.y - m2.y);float k = l2 / (l1 + l2);TimedPoint cm = new TimedPoint(m2.x + dxm * k, m2.y + dym * k);float tx = s2.x - cm.x;float ty = s2.y - cm.y;return new ControlTimedPoints(new TimedPoint(m1.x + tx, m1.y + ty), new TimedPoint(m2.x + tx, m2.y + ty));}private float strokeWidth(float velocity) {return Math.max(mMaxWidth / (velocity + 1), mMinWidth);}/*** Called when replaying history to ensure the dirty region includes all* mPoints.* * @param historicalX*            the previous x coordinate.* @param historicalY*            the previous y coordinate.*/private void expandDirtyRect(float historicalX, float historicalY) {if (historicalX < mDirtyRect.left) {mDirtyRect.left = historicalX;} else if (historicalX > mDirtyRect.right) {mDirtyRect.right = historicalX;}if (historicalY < mDirtyRect.top) {mDirtyRect.top = historicalY;} else if (historicalY > mDirtyRect.bottom) {mDirtyRect.bottom = historicalY;}}/*** Resets the dirty region when the motion event occurs.* * @param eventX*            the event x coordinate.* @param eventY*            the event y coordinate.*/private void resetDirtyRect(float eventX, float eventY) {// The mLastTouchX and mLastTouchY were set when the ACTION_DOWN motion// event occurred.mDirtyRect.left = Math.min(mLastTouchX, eventX);mDirtyRect.right = Math.max(mLastTouchX, eventX);mDirtyRect.top = Math.min(mLastTouchY, eventY);mDirtyRect.bottom = Math.max(mLastTouchY, eventY);}private void setIsEmpty(boolean newValue) {mIsEmpty = newValue;if (mOnSignedListener != null) {if (mIsEmpty) {mOnSignedListener.onClear();} else {mOnSignedListener.onSigned();}}}private void ensureSignatureBitmap() {if (mSignatureBitmap == null) {mSignatureBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);mSignatureBitmapCanvas = new Canvas(mSignatureBitmap);}}private int convertDpToPx(float dp) {return Math.round(dp * (getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));}public interface OnSignedListener {public void onSigned();public void onClear();}
}

Demo源码下载链接

最后我集成到项目里面的效果图 如上图

如果大家有其他问题 欢迎加入我的qq群讨论  开发一群:454430053开发二群:537532956

android 电子签名 手写签名 功能实现相关推荐

  1. Android 电子签名/手写签名 保存到相册详解

    ps:因公司推崇线上信息办公化 设计到客户签名 将客户签好的名字上传到服务器 因此 写了一个demo 废话不多哔哔 上效果图: 这里我运用的是自定义view //权限<uses-permissi ...

  2. android 手写签批_Android自定义实现手写签名功能

    一.Android自定义View步骤 : 自定义属性: 选择和设置构造方法: 重写onMeasure()方法: 重写onDraw()方法: 重写onLayout()方法: 重写其他事件的方法(滑动监听 ...

  3. Android手写签名功能(包含画米字格,人名和书写轨迹)

    本文主要介绍Android手写签名的功能实现,效果如下图 1.根据人名的个数绘制人的名称 这个逻辑分几个步骤:首先创建画笔,然后根据一个字,创建一个字的矩形框,然后根据矩形框获取到画这个字的宽高. / ...

  4. java实现手写签名,Android实现手写签名

    本文实例为大家分享了Android手写签名的实现方法,产品要求用户可以在app上签协议..所以得弄个手写签名版,参考了一些资料自己写了个PaintView去继承View,实现签名功能. package ...

  5. Android电子手写签名

    前言:最近一直在做h5,尼玛!!!前端真伤不起啊,由于刚接触,害怕兼容性啥的,导致我一个页面写了一天,泪崩啦-- 好吧,觉得得去撸一撸android才能平复的不安的小内心,刚好看到有一个这样的需求,需 ...

  6. html5 移动端手写签名,H5移动端项目实现手写签名功能 vue实现手写签名

    vue 移动端实现手写签名效果,功能很完美,保存时保存为base64格式. 刚好项目用到此功能,就网上找了一下,清理了无用代码,简单方便,因为项目中多个地方需要使用,所以我将它整理为组件,通过ref和 ...

  7. H5移动端项目实现手写签名功能 vue实现手写签名

    vue 移动端实现手写签名效果,功能很完美,保存时保存为base64格式. base64转file文件格式 vue中将base64转file文件格式 刚好项目用到此功能,就网上找了一下,清理了无用代码 ...

  8. vue canvas 方法无效_vue实现手机app手写签名功能

    我曾经在一个文书交互相关的项目中遇到过这样的一个需求,当用户接收到管理员发送的文书时,用户需要在这个文书上签字签收,签名需要跟原有的文书进行合并,做到模仿线下真实签收的电子签名.这签收没有调用APP特 ...

  9. Android屏幕手写签名的实现-详细篇

    老规矩先看一下效果图 [实现思路] (1)创建画笔 private void init(Context context) {this.mContext = context;//设置抗锯齿mGestur ...

最新文章

  1. PCIPCIE MSI中断
  2. php crc16校验算法,PHP串口通信中计算crc16校验码
  3. JVM_05 执行引擎(Execution Engine)
  4. java swt.jar_Eclipse中的swt需要的jar包
  5. Gitter - 高颜值GitHub小程序客户端诞生记
  6. Android--控件属性汇总
  7. “对比Excel”系列再添新成员,手把手教你用Python实现报表自动化!
  8. 基于JavaWEB+MySQL的宾馆管理系统设计与实现
  9. selected和checked区别
  10. 组队开发最后冲刺周第三次会议
  11. 微信小程序——婚礼邀请函页面
  12. Kotlin学习笔记(八)by的作用,属性委托和类的委托,和Lazy的关系
  13. 如何通过omnipeek抓取sniffer log
  14. 美通企业日报 | 科技和能源行业最受中国公众信任;英特尔携手大华发展智能视觉技术...
  15. 单体测试与结合测试区别
  16. java 访客模式,每日一课 | Java 8中的instanceof运算符和访客模式替换
  17. 五十道编程小题目 --- 50 有五个学生,每个学生有3门课的成绩,计算出平均成绩,况原有的数据和计算出的平均分数存放在磁盘文件stud中java
  18. 止步89岁!宣布证明黎曼猜想后,数学大师阿蒂亚爵士突然逝世...
  19. 基于PHP+MySQL的学生信息管理系统
  20. python怎么把字体变大_idle怎样设置代码字体大小 颜色 又快又好

热门文章

  1. c语言输出时出现了【Error】Id returned 1 exit status是什么原因
  2. 【Matlab/Simulink】PMSM模块
  3. 封神台靶场 kali训练营第二关WP
  4. 关于计算机这个行业,中级技工证书有什么意义吗?
  5. DNA甲基化在重头甲基转移酶远古丢失后数百万年的进化持久性
  6. 后台只有一个接口有跨域问题
  7. 高速下载苹果官方文档(百度云盘)
  8. OkGo第三方框架的上传与下载+Glide图片加载器
  9. Kettle学习之记录集连接
  10. 电商API_拼多多商品详细