百度云下载源码
链接:https://pan.baidu.com/s/1BUULg5JDLaKtFR_o35b51g
提取码:68qn
复制这段内容后打开百度网盘手机App,操作更方便哦
直接上代码咯。。。
—————————————— MainActivity
package com.signature.demo;
import android.app.Activity;
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.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.example.hand.R;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class MainActivity extends Activity {
private SignatureView mSignaturePad;
private Button mClearButton;
private Button mSaveButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSignaturePad = (SignatureView) findViewById(R.id.signature_pad);
mSignaturePad.setBackgroundResource(R.drawable.a);
mSignaturePad.setOnSignedListener(new SignatureView.OnSignedListener() {
@Override
public void onSigned() {
mSaveButton.setEnabled(true);
mClearButton.setEnabled(true);
}
@Override
public void onClear() {
mSaveButton.setEnabled(false);
mClearButton.setEnabled(false);
}
});
mClearButton = (Button) findViewById(R.id.clear_button);
mSaveButton = (Button) findViewById(R.id.save_button);
mClearButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mSignaturePad.clear();
}
});
mSaveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Bitmap signatureBitmap = mSignaturePad.getSignatureBitmap();
if (addSignatureToGallery(signatureBitmap)) {
Toast.makeText(MainActivity.this, "已保存到:"+Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures/SignatureFile/"+System.currentTimeMillis()+".jpg", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "保存失败", Toast.LENGTH_SHORT).show();
}
}
});
}
public File getAlbumStorageDir(String albumName) {
// Get the directory for the user's public pictures directory.
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) { Log.e("SignatureFile", "Directory not created");
}
return file;
}
public 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();
}
public boolean addSignatureToGallery(Bitmap signature) {
boolean result = false;
try {
File photo = new File(getAlbumStorageDir("SignatureFile"), String.format("Signature_%d.jpg",System.currentTimeMillis()));
saveBitmapToJPG(signature, photo);
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(photo);
mediaScanIntent.setData(contentUri);
MainActivity.this.sendBroadcast(mediaScanIntent);
result = true;
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
—————————————— SignatureView
package com.signature.demo;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
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 java.util.ArrayList;
import java.util.List;
public class SignatureView extends View {
// View state
private List 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.
*/
@SuppressLint("ResourceType")
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();
}
@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 int width, height;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
}
public void ensureSignatureBitmap() {
if (mSignatureBitmap == null) {
mSignatureBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
mSignatureBitmapCanvas = new Canvas(mSignatureBitmap);
//设置背景图图片 要指定图片大小 否则下面注释的方法会放大
mSignatureBitmapCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.a),
new Rect(0, 0, BitmapFactory.decodeResource(getResources(), R.drawable.a).getWidth(),
BitmapFactory.decodeResource(getResources(), R.drawable.a).getHeight()),
new Rect(0, 0, width, height), mPaint);
// mSignatureBitmapCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.bg_splash), matrix, mPaint);
}
}
// public void drawBitmap() {
// if (mSignatureBitmapCanvas == null) {
// mSignatureBitmapCanvas = new Canvas();
// }
// mSignatureBitmapCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.bg_splash), new Matrix(), mPaint);
//
// }
private int convertDpToPx(float dp) {
return Math.round(dp * (getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
public interface OnSignedListener {
public void onSigned();
public void onClear();
}
}
—————————————— Bezier
package com.signature.demo;
public class Bezier {
public TimedPoint startPoint;
public TimedPoint control1;
public TimedPoint control2;
public TimedPoint endPoint;
public Bezier(TimedPoint startPoint, TimedPoint control1, TimedPoint control2, TimedPoint endPoint) {
this.startPoint = startPoint;
this.control1 = control1;
this.control2 = control2;
this.endPoint = endPoint;
}
public float length() {
int steps = 10, length = 0;
int i;
float t;
double cx, cy, px = 0, py = 0, xdiff, ydiff;
for (i = 0; i <= steps; i++) {
t = i / steps;
cx = point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
cy = point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
if (i > 0) {
xdiff = cx - px;
ydiff = cy - py;
length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
}
px = cx;
py = cy;
}
return length;
}
public double point(float t, float start, float c1, float c2, float end) {
return start * (1.0 - t) * (1.0 - t) * (1.0 - t) + 3.0 * c1 * (1.0 - t) * (1.0 - t) * t + 3.0 * c2 * (1.0 - t)
* t * t + end * t * t * t;
}
}
—————————————— ControlTimedPoints
package com.signature.demo;
public class ControlTimedPoints {
public TimedPoint c1;
public TimedPoint c2;
public ControlTimedPoints(TimedPoint c1, TimedPoint c2) {
this.c1 = c1;
this.c2 = c2;
}
}
—————————————— TimedPoint
package com.signature.demo;
public class TimedPoint {
public final float x;
public final float y;
public final long timestamp;
public TimedPoint(float x, float y) {
this.x = x;
this.y = y;
this.timestamp = System.currentTimeMillis();
}
public float velocityFrom(TimedPoint start) {
float velocity = distanceTo(start) / (this.timestamp - start.timestamp);
if (velocity != velocity)
return 0f;
return velocity;
}
public float distanceTo(TimedPoint point) {
return (float) Math.sqrt(Math.pow(point.x - this.x, 2) + Math.pow(point.y - this.y, 2));
}
}
—————————————— activity_main.xml