手写

来源:互联网 发布:自动编程系统 编辑:程序博客网 时间:2024/04/28 05:34

本文通过自定义View实现Android手写签名,实现透明背景,边缘空白裁剪功能。

自定义LinePathView

代码注释有详细注释,不在具体讲解代码。

public class LinePathView extends View {    private Context mContext;    /**     * 笔画X坐标起点     */    private float mX;    /**     * 笔画Y坐标起点     */    private float mY;    /**     * 手写画笔     */    private final Paint mGesturePaint = new Paint();    /**     * 路径     */    private final Path mPath = new Path();    /**     * 签名画笔     */    private Canvas cacheCanvas;    /**     * 签名画布     */    private Bitmap cachebBitmap;    /**     * 是否已经签名     */    private boolean isTouched = false;    /**     * 画笔宽度 px;     */    private int mPaintWidth = 10;    /**     * 前景色     */    private int mPenColor = Color.BLACK;    /**     * 背景色(指最终签名结果文件的背景颜色,默认为透明色)     */    private int mBackColor=Color.TRANSPARENT;    public LinePathView(Context context) {        super(context);        init(context);    }    public LinePathView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    public LinePathView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context);    }    public void init(Context context) {        this.mContext = context;        //设置抗锯齿        mGesturePaint.setAntiAlias(true);        //设置签名笔画样式        mGesturePaint.setStyle(Style.STROKE);        //设置笔画宽度        mGesturePaint.setStrokeWidth(mPaintWidth);        //设置签名颜色        mGesturePaint.setColor(mPenColor);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        //创建跟view一样大的bitmap,用来保存签名        cachebBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);        cacheCanvas = new Canvas(cachebBitmap);        cacheCanvas.drawColor(mBackColor);        isTouched=false;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                touchDown(event);                break;            case MotionEvent.ACTION_MOVE:                isTouched = true;                touchMove(event);                break;            case MotionEvent.ACTION_UP:            //将路径画到bitmap中,即一次笔画完成才去更新bitmap,而手势轨迹是实时显示在画板上的。                cacheCanvas.drawPath(mPath, mGesturePaint);                mPath.reset();                break;        }        // 更新绘制        invalidate();        return true;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //画此次笔画之前的签名        canvas.drawBitmap(cachebBitmap, 0, 0, mGesturePaint);        // 通过画布绘制多点形成的图形        canvas.drawPath(mPath, mGesturePaint);    }    // 手指点下屏幕时调用    private void touchDown(MotionEvent event) {        // 重置绘制路线        mPath.reset();        float x = event.getX();        float y = event.getY();        mX = x;        mY = y;        // mPath绘制的绘制起点        mPath.moveTo(x, y);    }    // 手指在屏幕上滑动时调用    private void touchMove(MotionEvent event) {        final float x = event.getX();        final float y = event.getY();        final float previousX = mX;        final float previousY = mY;        final float dx = Math.abs(x - previousX);        final float dy = Math.abs(y - previousY);        // 两点之间的距离大于等于3时,生成贝塞尔绘制曲线        if (dx >= 3 || dy >= 3) {            // 设置贝塞尔曲线的操作点为起点和终点的一半            float cX = (x + previousX) / 2;            float cY = (y + previousY) / 2;            // 二次贝塞尔,实现平滑曲线;previousX, previousY为操作点,cX, cY为终点            mPath.quadTo(previousX, previousY, cX, cY);            // 第二次执行时,第一次结束调用的坐标值将作为第二次调用的初始坐标值            mX = x;            mY = y;        }    }    /**     * 清除画板     */    public void clear() {        if (cacheCanvas != null) {            isTouched = false;            //更新画板信息            mGesturePaint.setColor(mPenColor);            cacheCanvas.drawColor(mBackColor,PorterDuff.Mode.CLEAR);            mGesturePaint.setColor(mPenColor);            invalidate();        }    }    /**     * 保存画板     * @param path 保存到路径     */    public void save(String path)  throws IOException {        save(path, false, 0);    }    /**     * 保存画板     * @param path       保存到路径     * @param clearBlank 是否清除边缘空白区域     * @param blank  要保留的边缘空白距离     */    public void save(String path, boolean clearBlank, int blank) throws IOException {        Bitmap bitmap=cachebBitmap;        //BitmapUtil.createScaledBitmapByHeight(srcBitmap, 300);//  压缩图片        if (clearBlank) {            bitmap = clearBlank(bitmap, blank);        }        ByteArrayOutputStream bos = new ByteArrayOutputStream();        bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);        byte[] buffer = bos.toByteArray();        if (buffer != null) {                File file = new File(path);                if (file.exists()) {                    file.delete();                }                OutputStream outputStream = new FileOutputStream(file);                outputStream.write(buffer);                outputStream.close();        }    }    /**     * 获取画板的bitmap     * @return     */    public Bitmap getBitMap()    {        setDrawingCacheEnabled(true);        buildDrawingCache();        Bitmap bitmap=getDrawingCache();        setDrawingCacheEnabled(false);        return bitmap;    }    /**     * 逐行扫描 清楚边界空白。     *     * @param bp     * @param blank 边距留多少个像素     * @return     */    private Bitmap clearBlank(Bitmap bp, int blank) {        int HEIGHT = bp.getHeight();        int WIDTH = bp.getWidth();        int top = 0, left = 0, right = 0, bottom = 0;        int[] pixs = new int[WIDTH];        boolean isStop;        //扫描上边距不等于背景颜色的第一个点        for (int y = 0; y < HEIGHT; y++) {            bp.getPixels(pixs, 0, WIDTH, 0, y, WIDTH, 1);            isStop = false;            for (int pix : pixs) {                if (pix != mBackColor) {                    top = y;                    isStop = true;                    break;                }            }            if (isStop) {                break;            }        }        //扫描下边距不等于背景颜色的第一个点        for (int y = HEIGHT - 1; y >= 0; y--) {            bp.getPixels(pixs, 0, WIDTH, 0, y, WIDTH, 1);            isStop = false;            for (int pix : pixs) {                if (pix != mBackColor) {                    bottom = y;                    isStop = true;                    break;                }            }            if (isStop) {                break;            }        }        pixs = new int[HEIGHT];        //扫描左边距不等于背景颜色的第一个点        for (int x = 0; x < WIDTH; x++) {            bp.getPixels(pixs, 0, 1, x, 0, 1, HEIGHT);            isStop = false;            for (int pix : pixs) {                if (pix != mBackColor) {                    left = x;                    isStop = true;                    break;                }            }            if (isStop) {                break;            }        }        //扫描右边距不等于背景颜色的第一个点        for (int x = WIDTH - 1; x > 0; x--) {            bp.getPixels(pixs, 0, 1, x, 0, 1, HEIGHT);            isStop = false;            for (int pix : pixs) {                if (pix != mBackColor) {                    right = x;                    isStop = true;                    break;                }            }            if (isStop) {                break;            }        }        if (blank < 0) {            blank = 0;        }        //计算加上保留空白距离之后的图像大小        left = left - blank > 0 ? left - blank : 0;        top = top - blank > 0 ? top - blank : 0;        right = right + blank > WIDTH - 1 ? WIDTH - 1 : right + blank;        bottom = bottom + blank > HEIGHT - 1 ? HEIGHT - 1 : bottom + blank;        return Bitmap.createBitmap(bp, left, top, right - left, bottom - top);    }    /**     * 设置画笔宽度 默认宽度为10px     *     * @param mPaintWidth     */    public void setPaintWidth(int mPaintWidth) {        mPaintWidth = mPaintWidth > 0 ? mPaintWidth : 10;        this.mPaintWidth = mPaintWidth;        mGesturePaint.setStrokeWidth(mPaintWidth);    }    public void setBackColor(@ColorInt int backColor)    {        mBackColor=backColor;    }    /**     * 设置画笔颜色     *     * @param mPenColor     */    public void setPenColor(int mPenColor) {        this.mPenColor = mPenColor;        mGesturePaint.setColor(mPenColor);    }    /**     * 是否有签名     *     * @return     */    public boolean getTouched() {        return isTouched;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328

使用

  • 添加View
    <com.wastrel.handwritedemo.LinePathView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:id="@+id/view"        android:background="#FFFFFF" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 保存
if (mPathView.getTouched()) {    try {        mPathView.save("/sdcard/qm.png", true, 10);        setResult(100);        finish();    } catch (IOException e) {        e.printStackTrace();    }} else {    Toast.makeText(HandWriteActivity.this, "您没有签名~", Toast.LENGTH_SHORT).show();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 清除
mPathView.clear();
  • 1
  • 1
  • 修改背景、笔宽、颜色
  mPathView.setBackColor(Color.RED);  mPathView.setPaintWidth(20);  mPathView.setPenColor(Color.WHITE);  mPathView.clear();
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4