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







更多相关文章

  1. 国内一些优秀Android(安卓)开发者信息
  2. Android开源网站一网打尽
  3. Android开发:设置widget大小为 4x1
  4. android开发之res下的menu (xml+代码的形式)
  5. Android(安卓)方法数超过64k限制的解决办法
  6. Android(安卓)开发第二弹:通讯录
  7. android ndk 开发
  8. 国外Android面试题
  9. Android手机开发:将按钮置于底部

随机推荐

  1. Android事件分发机制完全解析,带你从源码
  2. Android(安卓)XML�O置屏幕方向(android:sc
  3. Android(安卓)Matrix详解
  4. Android(安卓)NDK初识
  5. 怎么让 Android(安卓)程序一直后台运行,像
  6. Android(安卓)imageView图片按比例缩放
  7. 深入理解Android消息处理系统——Looper
  8. 简析Android对Linux内核的改动
  9. 详解 Android(安卓)的 Activity 组件
  10. Android布局之LinearLayout与RelativeLay