本文主要是介绍安卓-电子签名signature,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1.先上效果图:
2.首先需要绘制自定义的view,用于电子签名:
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.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;import com.xiaoying.mysignature.R;import java.util.ArrayList;
import java.util.List;/*** 绘制SignaturePad的View 用于签名*/
public class SignaturePad extends View {//视图状态private List<TimedPoint> mPoints;private boolean mIsEmpty;private float mLastTouchX;private float mLastTouchY;private float mLastVelocity;private float mLastWidth;private RectF mDirtyRect;private final SvgBuilder mSvgBuilder = new SvgBuilder();//隐藏状态private List<TimedPoint> mPointsCache = new ArrayList<>();private ControlTimedPoints mControlTimedPointsCached = new ControlTimedPoints();private Bezier mBezierCached = new Bezier();//可以配置的参数private int mMinWidth;private int mMaxWidth;private float mVelocityFilterWeight;private OnSignedListener mOnSignedListener;private boolean mClearOnDoubleClick;//单次点击值private long mFirstClick;private int mCountClick;private static final int DOUBLE_CLICK_DELAY_MS = 200;//默认的属性值private final int DEFAULT_ATTR_PEN_MIN_WIDTH_PX = 3;private final int DEFAULT_ATTR_PEN_MAX_WIDTH_PX = 7;private final int DEFAULT_ATTR_PEN_COLOR = Color.BLACK;private final float DEFAULT_ATTR_VELOCITY_FILTER_WEIGHT = 0.9f;private final boolean DEFAULT_ATTR_CLEAR_ON_DOUBLE_CLICK = false;private Paint mPaint = new Paint();private Bitmap mSignatureBitmap = null;private Canvas mSignatureBitmapCanvas = null;public SignaturePad(Context context, AttributeSet attrs) {super(context, attrs);TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.SignaturePad,0, 0);//可以配置的参数try {mMinWidth = a.getDimensionPixelSize(R.styleable.SignaturePad_penMinWidth, convertDpToPx(DEFAULT_ATTR_PEN_MIN_WIDTH_PX));mMaxWidth = a.getDimensionPixelSize(R.styleable.SignaturePad_penMaxWidth, convertDpToPx(DEFAULT_ATTR_PEN_MAX_WIDTH_PX));mPaint.setColor(a.getColor(R.styleable.SignaturePad_penColor, DEFAULT_ATTR_PEN_COLOR));mVelocityFilterWeight = a.getFloat(R.styleable.SignaturePad_velocityFilterWeight, DEFAULT_ATTR_VELOCITY_FILTER_WEIGHT);mClearOnDoubleClick = a.getBoolean(R.styleable.SignaturePad_clearOnDoubleClick, DEFAULT_ATTR_CLEAR_ON_DOUBLE_CLICK);} finally {a.recycle();}//固定的配置参数mPaint.setAntiAlias(true);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeCap(Paint.Cap.ROUND);mPaint.setStrokeJoin(Paint.Join.ROUND);//更新矩形内的部分视图mDirtyRect = new RectF();clear();}/*** 从资源中设置电子笔显示的颜色,默认为黑色** @param colorRes*/public void setPenColorRes(int colorRes) {try {setPenColor(getResources().getColor(colorRes));} catch (Resources.NotFoundException ex) {setPenColor(Color.parseColor("#000000"));}}/*** 设置给定的电子笔显示的颜色*/public void setPenColor(int color) {mPaint.setColor(color);}/*** 设置像素中笔画的最小宽度** @param minWidth 单位 dp*/public void setMinWidth(float minWidth) {mMinWidth = convertDpToPx(minWidth);}/*** 设置像素中笔画的最大宽度** @param maxWidth 单位 dp*/public void setMaxWidth(float maxWidth) {mMaxWidth = convertDpToPx(maxWidth);}/*** 设置速度过滤器的权重*/public void setVelocityFilterWeight(float velocityFilterWeight) {mVelocityFilterWeight = velocityFilterWeight;}public void clear() {mSvgBuilder.clear();mPoints = new ArrayList<>();mLastVelocity = 0;mLastWidth = (mMinWidth + mMaxWidth) / 2;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();if (isDoubleClick()) break;mLastTouchX = eventX;mLastTouchY = eventY;addPoint(getNewPoint(eventX, eventY));if (mOnSignedListener != null) mOnSignedListener.onStartSigning();case MotionEvent.ACTION_MOVE:resetDirtyRect(eventX, eventY);addPoint(getNewPoint(eventX, eventY));break;case MotionEvent.ACTION_UP:resetDirtyRect(eventX, eventY);addPoint(getNewPoint(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 String getSignatureSvg() {int width = getTransparentSignatureBitmap().getWidth();int height = getTransparentSignatureBitmap().getHeight();return mSvgBuilder.build(width, height);}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(final Bitmap signature) {//制定viewif (ViewCompat.isLaidOut(this)) {clear();ensureSignatureBitmap();RectF tempSrc = new RectF();RectF tempDst = new RectF();int dWidth = signature.getWidth();int dHeight = signature.getHeight();int vWidth = getWidth();int vHeight = getHeight();//生成可替换的变量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();} else {getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {//移除布局侦听器ViewTreeObserverCompat.removeOnGlobalLayoutListener(getViewTreeObserver(), this);//电子签名图setSignatureBitmap(signature);}});}}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;//x轴最小值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;}//电子图片为空if (!foundPixel)return null;//y轴最小值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;}//x轴最大值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;}//y轴最大值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 boolean isDoubleClick() {if (mClearOnDoubleClick) {if (mFirstClick != 0 && System.currentTimeMillis() - mFirstClick > DOUBLE_CLICK_DELAY_MS) {mCountClick = 0;}mCountClick++;if (mCountClick == 1) {mFirstClick = System.currentTimeMillis();} else if (mCountClick == 2) {long lastClick = System.currentTimeMillis();if (lastClick - mFirstClick < DOUBLE_CLICK_DELAY_MS) {this.clear();return true;}}}return false;}private TimedPoint getNewPoint(float x, float y) {int mCacheSize = mPointsCache.size();TimedPoint timedPoint;if (mCacheSize == 0) {//缓存为空时创建一个对象timedPoint = new TimedPoint();} else {//从缓存中获取timedPoint = mPointsCache.remove(mCacheSize - 1);}return timedPoint.set(x, y);}private void recyclePoint(TimedPoint point) {mPointsCache.add(point);}private void addPoint(TimedPoint newPoint) {mPoints.add(newPoint);int pointsCount = mPoints.size();if (pointsCount > 3) {ControlTimedPoints tmp = calculateCurveControlPoints(mPoints.get(0), mPoints.get(1), mPoints.get(2));TimedPoint c2 = tmp.c2;recyclePoint(tmp.c1);tmp = calculateCurveControlPoints(mPoints.get(1), mPoints.get(2), mPoints.get(3));TimedPoint c3 = tmp.c1;recyclePoint(tmp.c2);Bezier curve = mBezierCached.set(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;//对应的比划宽度float newWidth = strokeWidth(velocity);//开始和结束的绘制点addBezier(curve, mLastWidth, newWidth);mLastVelocity = velocity;mLastWidth = newWidth;//从列表中删除第一个元素,总长度不超过4个绘制点recyclePoint(mPoints.remove(0));recyclePoint(c2);recyclePoint(c3);} else if (pointsCount == 1) {TimedPoint firstPoint = mPoints.get(0);mPoints.add(getNewPoint(firstPoint.x, firstPoint.y));}}private void addBezier(Bezier curve, float startWidth, float endWidth) {mSvgBuilder.append(curve, (startWidth + endWidth) / 2);ensureSignatureBitmap();float originalWidth = mPaint.getStrokeWidth();float widthDelta = endWidth - startWidth;float drawSteps = (float) Math.floor(curve.length());for (int i = 0; i < drawSteps; i++) {//计算这个步骤的 Bezier(x,y) 坐标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;//设置增量笔画宽度和绘制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;float m1X = (s1.x + s2.x) / 2.0f;float m1Y = (s1.y + s2.y) / 2.0f;float m2X = (s2.x + s3.x) / 2.0f;float m2Y = (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 = (m1X - m2X);float dym = (m1Y - m2Y);float k = l2 / (l1 + l2);if (Float.isNaN(k)) k = 0.0f;float cmX = m2X + dxm * k;float cmY = m2Y + dym * k;float tx = s2.x - cmX;float ty = s2.y - cmY;return mControlTimedPointsCached.set(getNewPoint(m1X + tx, m1Y + ty), getNewPoint(m2X + tx, m2Y + ty));}private float strokeWidth(float velocity) {return Math.max(mMaxWidth / (velocity + 1), mMinWidth);}/*** 记录历史画布上绘制的区域** @param historicalX 上次x轴点* @param historicalY 上次y轴点*/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;}}/*** 当动作事件发生时重置已绘制的区域** @param eventX 当前x轴点* @param eventY 当前y轴点*/private void resetDirtyRect(float eventX, float eventY) {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(getContext().getResources().getDisplayMetrics().density * dp);}public interface OnSignedListener {void onStartSigning();void onSigned();void onClear();}
}
3.在/src/res/values目录下添加attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="SignaturePad"><attr name="penMinWidth" format="dimension" /><attr name="penMaxWidth" format="dimension" /><attr name="penColor" format="color" /><attr name="velocityFilterWeight" format="float" /><attr name="clearOnDoubleClick" format="boolean" /></declare-styleable>
</resources>
4.添加xml文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><com.xiaoying.mysignature.signature.SignaturePadandroid:id="@+id/signaturePad"android:layout_width="match_parent"android:layout_height="500dp" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="510dp"android:orientation="horizontal"><TextViewandroid:id="@+id/btnClear"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="1"android:background="@null"android:enabled="false"android:gravity="center"android:paddingBottom="11dp"android:paddingTop="11dp"android:text="清除签名"android:textColor="@color/colorPrimary"android:textSize="16sp" /><TextViewandroid:id="@+id/btnSave"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="1"android:background="@null"android:enabled="false"android:gravity="center"android:paddingBottom="11dp"android:paddingTop="11dp"android:text="保存签名"android:textColor="@color/colorPrimary"android:textSize="16sp" /></LinearLayout>
</FrameLayout>
5.获取主要的java文件
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;import com.xiaoying.mysignature.signature.SignaturePad;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;public class MainActivity extends AppCompatActivity implements SignaturePad.OnSignedListener {@BindView(R.id.signaturePad)SignaturePad signaturePad;@BindView(R.id.btnClear)TextView btnClear;@BindView(R.id.btnSave)TextView btnSave;private File photo;private Bitmap signatureBitmap;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);signaturePad.setOnSignedListener(this);}@OnClick({R.id.btnClear, R.id.btnSave})public void onViewClicked(View view) {switch (view.getId()) {case R.id.btnClear://清除电子签名signaturePad.clear();break;case R.id.btnSave:signatureBitmap = signaturePad.getSignatureBitmap();if (addJpgSignatureToGallery(signatureBitmap)) {Toast.makeText(this, "保存签名成功", Toast.LENGTH_LONG).show();//跳转至系统相册skipPictures();}break;}}private Intent skipPictures() {/* 开启Pictures画面Type设定为image */Intent intent = new Intent();intent.setType("image/*");/* 使用Intent.ACTION_GET_CONTENT这个Action */intent.setAction(Intent.ACTION_GET_CONTENT);/* 取得相片后返回本画面 */startActivityForResult(intent, 1);//(在onActivityResult方法里,返回的意图里获取图片uri,在通过uri,结合内容提供者在查出图片的路径)return intent;}@Overridepublic void onStartSigning() {}@Overridepublic void onSigned() {btnClear.setEnabled(true);btnSave.setEnabled(true);}@Overridepublic void onClear() {btnClear.setEnabled(false);btnSave.setEnabled(false);}//将.jpg 签名添加到 Gallery 中private boolean addJpgSignatureToGallery(Bitmap signature) {boolean result = false;try {photo = new File(getAlbumStorageDir("SignaturePad"), String.format("Signature_%d.jpg", System.currentTimeMillis()));saveBitmapToJPG(signature, photo);scanMediaFile(photo);result = true;} catch (IOException e) {e.printStackTrace();}return result;}private File getAlbumStorageDir(String albumName) {//电子签名图片存储目录File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), albumName);if (!file.mkdirs()) {Log.e("SignaturePad", "Directory not created");}return file;}//将位图保存格式为.jpgprivate void saveBitmapToJPG(Bitmap bitmap, File photo) throws IOException {Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(newBitmap);canvas.drawColor(Color.WHITE);canvas.drawBitmap(bitmap, 0, 0, null);OutputStream stream = new FileOutputStream(photo);newBitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream);stream.close();}//媒体文件扫描private void scanMediaFile(File photo) {Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);Uri contentUri = Uri.fromFile(photo);mediaScanIntent.setData(contentUri);this.sendBroadcast(mediaScanIntent);}
}
6.附上源码 欢迎留言讨论
这篇关于安卓-电子签名signature的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!