Android 自定义View--手写签名

1、概述

1.1 目的 :

在我们的日常开发中,有很多Android UI界面上有一些特殊或者特别的控件与界面,是Android自带的控件所不能满足的,需要我们自己定制一些适合的控件来完成。

1.2 Android自定义View步骤 :

  1. 自定义属性;
  2. 选择和设置构造方法;
  3. 重写onMeasure()方法;
  4. 重写onDraw()方法;
  5. 重写onLayout()方法;
  6. 重写其他事件的方法(滑动监听等)。

2、代码实现

2.1 自定义属性:

我们通常将自定义属性定义在/values/attr.xml文件中(attr.xml文件需要自己创建)。

2.2 实现方法含义

1、在OnMeasure()方法中,测量自定义控件的大小,使自定义控件能够自适应布局各种各样的需求。
2、在OnDraw()方法中,利用哼哈二将(Canvas与Paint)来绘制要显示的内容。
3、在OnLayout()方法中来确定控件显示位置。
4、在OnTouchEvent()方法处理控件的触摸事件。、

[图片上传失败...(image-283eee-1538105354491)]

2.3 继承View实现代码

package com.fly.myview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.support.annotation.ColorInt;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * 
 *           .----.
 *        _.'__    `.
 *    .--(Q)(OK)---/$\
 *  .' @          /$$$\
 *  :         ,   $$$$$
 *   `-..__.-' _.-\$$$/
 *         `;_:    `"'
 *       .'"""""`.
 *      /,  FLY  ,\
 *     //         \\
 *     `-._______.-'
 *     ___`. | .'___
 *    (______|______)
 * 
* 包 名 : com.fly.myview * 作 者 : FLY * 创建时间 : 2018/3/2 * 描述: 手写签名 */ 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; //签名开始与结束 private Touch touch; 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(Paint.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(), Bitmap.Config.ARGB_8888); cacheCanvas = new Canvas(cachebBitmap); cacheCanvas.drawColor(mBackColor); isTouched = false; } @Override public boolean onTouchEvent(MotionEvent event) { if (touch != null) touch.OnTouch(true); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: touchDown(event); break; case MotionEvent.ACTION_MOVE: isTouched = true; if (touch != null) touch.OnTouch(false); 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(); } } public interface Touch { void OnTouch(boolean isTouch); } public Touch getTouch() { return touch; } public void setTouch(Touch touch) { this.touch = touch; } /** * 保存画板 * * @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; } }

3、使用

  
   
  //修改背景、笔宽、颜色
        mPathView.setBackColor(Color.WHITE);
        mPathView.setPaintWidth(20);
        mPathView.setPenColor(Color.BLACK);
    //清除
    mBtnClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPathView.clear();
                mPathView.setBackColor(Color.WHITE);
                mPathView.setPaintWidth(20);
                mPathView.setPenColor(Color.BLACK);
            }
        });
   //保存
  mBtnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mPathView.getTouched()) {
                    try {
                        mPathView.save("/sdcard/qm.png", true, 10);
                        setResult(100);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } else {
                    Toast.makeText(LinePathViewActivity.this, "您没有签名~", Toast.LENGTH_SHORT).show();
                }
            }
        });

4.效果

Android 自定义View--手写签名_第1张图片
效果图

希望对各位朋友有帮助,谢谢!!!!

你可能感兴趣的:(Android 自定义View--手写签名)