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

简介: android 电子签名 手写签名 功能实现

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

image.png

这个手写的效果 就是一个 重写的的自定义的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();
  }
}


相关文章
|
20天前
|
存储 Java API
Android 浅度解析:mk预置AAR、SO文件、APP包和签名
Android 浅度解析:mk预置AAR、SO文件、APP包和签名
76 0
|
1月前
|
算法 Android开发
Android签名算法的原理
Android签名算法的原理
20 0
|
4天前
|
Android开发
Android 获取签名信息
Android 获取签名信息
7 0
|
4天前
|
安全 Android开发
修改Android系统的签名
修改Android系统的签名
15 0
|
4天前
|
安全 Android开发
Android 系统签名
Android 系统签名
14 0
|
5天前
|
Android开发
Android SystemUI去掉拖动亮度条QSPanel界面隐藏功能
Android SystemUI去掉拖动亮度条QSPanel界面隐藏功能
8 0
|
6天前
|
安全 Android开发 数据安全/隐私保护
Android 如何获取系统签名 并使用系统签名
Android 如何获取系统签名 并使用系统签名
14 2
|
20天前
|
存储 安全 Ubuntu
Android 生成平台应用签名keystore文件
Android 生成平台应用签名keystore文件
9 0
|
20天前
|
Java Android开发
Android Mediatek 应用层重置USB设备功能
Android Mediatek 应用层重置USB设备功能
13 0
|
20天前
|
Linux Android开发
Android 内核关闭CAN 串口设备回显功能
Android 内核关闭CAN 串口设备回显功能
9 0