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();
  }
}


相关文章
|
4月前
|
Android开发
基于android-11.0.0_r39,系统应用的手动签名方法和过程
本文介绍了基于Android 11.0.0_r39版本进行系统应用手动签名的方法和解决签名过程中遇到的错误,包括处理`no conscrypt_openjdk_jni-linux-x86_64`和`RegisterNatives failed`的问题。
208 2
|
2月前
|
Android开发
Android开发表情emoji功能开发
本文介绍了一种在Android应用中实现emoji表情功能的方法,通过将图片与表情字符对应,实现在`TextView`中的正常显示。示例代码展示了如何使用自定义适配器加载emoji表情,并在编辑框中输入或删除表情。项目包含完整的源码结构,可作为开发参考。视频演示和源码详情见文章内链接。
72 4
Android开发表情emoji功能开发
|
2月前
|
安全 Android开发 iOS开发
Android vs iOS:探索移动操作系统的设计与功能差异###
【10月更文挑战第20天】 本文深入分析了Android和iOS两个主流移动操作系统在设计哲学、用户体验、技术架构等方面的显著差异。通过对比,揭示了这两种系统各自的独特优势与局限性,并探讨了它们如何塑造了我们的数字生活方式。无论你是开发者还是普通用户,理解这些差异都有助于更好地选择和使用你的移动设备。 ###
54 3
|
4月前
|
安全 Java Android开发
【Android P】OTA升级包定制,移除不需要更新的分区,重新打包签名
如何解压OTA升级包、编辑升级包内容(例如移除不需要更新的分区)、重新打包、签名以及验证OTA文件的过程。
354 2
【Android P】OTA升级包定制,移除不需要更新的分区,重新打包签名
|
4月前
|
编解码 测试技术 Android开发
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
本文详细介绍了如何利用CameraX库实现高质量的照片及视频拍摄功能,包括添加依赖、初始化、权限请求、配置预览与捕获等关键步骤。此外,还特别针对不同分辨率和帧率的视频拍摄提供了性能优化策略,确保应用既高效又稳定。
406 1
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
|
3月前
|
Android开发 开发者
Android平台无纸化同屏如何实现实时录像功能
Android平台无纸化同屏,如果需要本地录像的话,实现难度不大,只要复用之前开发的录像模块的就可以,对我们来说,同屏采集这块,只是数据源不同而已,如果是自采集的其他数据,我们一样可以编码录像。
|
4月前
|
Java Android开发 Windows
使用keytool查看Android APK签名
本文介绍了如何使用Windows命令行工具和keytool查看APK的签名信息,并提供了使用AOSP环境中的signapk.jar工具对APK进行系统签名的方法。
411 0
使用keytool查看Android APK签名
|
4月前
|
图形学 Android开发
小功能⭐️Unity调用Android常用事件
小功能⭐️Unity调用Android常用事件
|
4月前
|
Android开发 数据安全/隐私保护
Android Studio创建JKS签名遇到的坑
Android Studio创建JKS签名遇到的坑
164 1
|
5月前
|
Android开发 Java 数据安全/隐私保护
「移动端」Android平台签名证书(.keystore)生成指南
发布Android APK需签名证书,步骤如下: 1. 安装JRE,如[JRE8](https://www.oracle.com/technetwork/java/javase/downloads/index.html),并添加到环境变量。 2. 使用`keytool -genkey`命令生成证书,例如: ``` keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore ``` 输入相关个人信息及密码。
825 0
「移动端」Android平台签名证书(.keystore)生成指南