这是效果图

百度云下载源码
链接: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;@Overrideprotected 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() {@Overridepublic void onSigned() {mSaveButton.setEnabled(true);mClearButton.setEnabled(true);}@Overridepublic 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() {@Overridepublic void onClick(View view) {mSignaturePad.clear();}});mSaveButton.setOnClickListener(new View.OnClickListener() {@Overridepublic 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

                

更多相关文章

  1. Android弧形效果
  2. Android(安卓)Studio的GridLayout中使按钮分布于整个网格
  3. Android(安卓)studio 3.5.3 第一次启动卡在gradle sync,run是灰色
  4. Android(安卓)Studio 快捷键 for mac
  5. rk3288 Android(安卓)5.1root方法
  6. android studio3.1.4更新3.2.1版本问题汇总
  7. 使用Jenkins自动构建Android问题总结
  8. Android的Master/Detail风格界面中实现自定义ListView的单选
  9. Android(安卓)ComboBox

随机推荐

  1. 【Android】结合源码解析Android消息队列
  2. Android核心分析 之十-------Android(安
  3. Android(安卓)项目在Eclipse中的目录结构
  4. android防止内存溢出浅析
  5. [Android] 将Android工程做成jar包和资源
  6. android 中 padding与margin的区别
  7. Android逆向之旅---Android中的sharedUse
  8. Android音频开发(6):使用 OpenSL ES API(上)
  9. “史上最强Android木马”现身?360手机安全
  10. android游戏引擎初探