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

举报
再见孙悟空_ 发表于 2022/01/12 22:26:12 2022/01/12
1.4k+ 0 0
【摘要】 android 电子签名  手写签名 功能实现 这个手写的效果 就是一个 重写的的自定义的view  代码如下: package com.example.hand.views; import java.util.ArrayList;import java.util.List; import a...

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 state
     	private List<TimedPoint> mPoints;
     	private boolean mIsEmpty;
     	private float mLastTouchX;
     	private float mLastTouchY;
     	private float mLastVelocity;
     	private float mLastWidth;
     	private RectF mDirtyRect;
     	// Configurable parameters
     	private 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 parameters
     		try {
      			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 parameters
      		mPaint.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 view
      		mDirtyRect = 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();
      	}
     	@Override
     	public 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;
      	}
     	@Override
     	protected 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 xMin
     		for (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 yMin
     		for (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 xMax
     		for (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 yMax
     		for (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

文章来源: wukong.blog.csdn.net,作者:再见孙悟空_,版权归原作者所有,如需转载,请联系作者。

原文链接:wukong.blog.csdn.net/article/details/77993757

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。