ps:因公司推崇线上信息办公化 设计到客户签名 将客户签好的名字上传到服务器 因此 写了一个demo
废话不多哔哔 上效果图:
这里我运用的是自定义view
//权限<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
public class SignatureView extends View {private static final String TAG = SignatureView.class.getSimpleName();public static final int PEN_WIDTH = 10;public static final int PEN_COLOR = Color.BLACK;public static final int BACK_COLOR = Color.WHITE;//画笔x坐标起点private float mPenX;//画笔y坐标起点private float mPenY;private Paint mPaint = new Paint();private Path mPath = new Path();private Canvas mCanvas;private Bitmap cacheBitmap;//画笔宽度private int mPentWidth = PEN_WIDTH;//画笔颜色private int mPenColor = PEN_COLOR;//画板颜色private int mBackColor = BACK_COLOR;private boolean isTouched = false;private String mSavePath = null;public SignatureView(Context context) {this(context, null);}public SignatureView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SignatureView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SignatureView);//拿到样式集合//画笔颜色mPenColor = typedArray.getColor(R.styleable.SignatureView_penColor, PEN_COLOR);//画板颜色mBackColor = typedArray.getColor(R.styleable.SignatureView_backColor, BACK_COLOR);//画笔宽度mPentWidth = typedArray.getInt(R.styleable.SignatureView_penWidth, PEN_WIDTH);typedArray.recycle();//回收资源init();}private void init() {mPaint.setAntiAlias(true);//抗锯齿mPaint.setStyle(Paint.Style.STROKE);//描边mPaint.setStrokeWidth(mPentWidth);//设置画笔宽度mPaint.setColor(mPenColor); //设置画笔颜色 black}public boolean getTouched() {return isTouched;}public void setPentWidth(int pentWidth) {mPentWidth = pentWidth;}public void setPenColor(@ColorInt int penColor) {mPenColor = penColor;}public void setBackColor(@ColorInt int backColor) {mBackColor = backColor;}/*** 清空签名*/public void clear() {if (mCanvas != null) {isTouched = false;mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);//清除画布内容mCanvas.drawColor(mBackColor);//重新设置画布颜色invalidate();}}/*** 保存图片** @param path 保存的地址* @param clearBlank 是否清除空白区域* @param blank 空白区域留空距离* @throws IOException*/public void save(String path, boolean clearBlank, int blank,Context context) throws IOException {if (TextUtils.isEmpty(path)) {return;}mSavePath = path;Bitmap bitmap = cacheBitmap;Log.i("zahuishi","@"+bitmap);saveFile(context,bitmap);// if (clearBlank) {// bitmap = clearBlank(bitmap, blank);// }// ByteArrayOutputStream bos = new ByteArrayOutputStream();// press(pressFormat.PNG, 100, bos);// byte[] buffer = bos.toByteArray();// if (buffer != null) {// File file = new File(path);// if (file.exists()) {//file.delete();// }// OutputStream os = new FileOutputStream(file);// os.write(buffer);// os.close();// bos.close();// }}public static void saveFile(Context context, Bitmap bm) throws IOException {File dirFile = new File(Environment.getExternalStorageDirectory().getPath());if (!dirFile.exists()) {dirFile.mkdir();}String fileName = UUID.randomUUID().toString() + ".jpg";File myCaptureFile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/" + fileName);BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));press(pressFormat.JPEG, 80, bos);bos.flush();bos.close();//把图片保存后声明这个广播事件通知系统相册有新图片到来Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);Uri uri = Uri.fromFile(myCaptureFile);intent.setData(uri);context.sendBroadcast(intent);}/*** 获取Bitmap缓存*/public Bitmap getBitmap() {setDrawingCacheEnabled(true);buildDrawingCache();Bitmap bitmap = getDrawingCache();setDrawingCacheEnabled(false);return bitmap;}/*** 获取保存路径*/public String getSavePath() {return mSavePath;}/*** 逐行扫描,清除边界空白** @param blank 边界留多少个像素*/private Bitmap clearBlank(Bitmap bmp, int blank) {int height = bmp.getHeight();int width = bmp.getWidth();int top = 0, left = 0, right = 0, bottom = 0;int[] pixs = new int[width];boolean isStop;//扫描上边距不等于背景颜色的第一个点for (int i = 0; i < height; i++) {bmp.getPixels(pixs, 0, width, 0, i, width, 1);isStop = false;for (int pix :pixs) {if (pix != mBackColor) {top = i;isStop = true;break;}}if (isStop) {break;}}//扫描下边距不等于背景颜色的第一个点for (int i = height - 1; i >= 0; i--) {bmp.getPixels(pixs, 0, width, 0, i, width, 1);isStop = false;for (int pix :pixs) {if (pix != mBackColor) {bottom = i;isStop = true;break;}}if (isStop) {break;}}pixs = new int[height];//扫描左边距不等于背景颜色的第一个点for (int x = 0; x < width; x++) {bmp.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--) {bmp.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(bmp, left, top, right - left, bottom - top);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {//布局发生变化 重新回去控件大小super.onSizeChanged(w, h, oldw, oldh);//创建一个空位图,没有色彩,宽高和bitmap2一样cacheBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);//用来承载画的内容。mCanvas = new Canvas(cacheBitmap);//画板颜色mCanvas.drawColor(mBackColor);isTouched = false;}@Overrideprotected void onDraw(Canvas canvas) {//绘制super.onDraw(canvas);canvas.drawBitmap(cacheBitmap, 0, 0, mPaint);canvas.drawPath(mPath, mPaint);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mPenX = event.getX();mPenY = event.getY();mPath.moveTo(mPenX, mPenY);//不会进行绘制,只用于移动移动画笔。return true;case MotionEvent.ACTION_MOVE:isTouched = true;float x = event.getX();float y = event.getY();float penX = mPenX;float penY = mPenY;float dx = Math.abs(x - penX);//取绝对值float dy = Math.abs(y - penY);if (dx >= 3 || dy >= 3) {float cx = (x + penX) / 2;float cy = (y + penY) / 2;mPath.quadTo(penX, penY, cx, cy);mPenX = x;mPenY = y;}invalidate();break;case MotionEvent.ACTION_UP:mCanvas.drawPath(mPath, mPaint);mPath.reset();//清除之前绘制的pathbreak;default:break;}return super.onTouchEvent(event);}}
在MainActivity使用
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.f1reking.signatureview_sample.MainActivity"><Buttonandroid:id="@+id/btn_clear"android:layout_width="0dp"android:layout_height="wrap_content"android:text="清除"app:layout_constraintHorizontal_weight="1"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toLeftOf="@+id/btn_save"tools:ignore="MissingConstraints" /><Buttonandroid:id="@+id/btn_save"android:layout_width="0dp"android:layout_height="wrap_content"android:text="保存"app:layout_constraintHorizontal_weight="1"app:layout_constraintLeft_toRightOf="@+id/btn_clear"app:layout_constraintRight_toRightOf="parent"tools:ignore="MissingConstraints" /><com.f1reking.signatureview.SignatureViewandroid:id="@+id/view_signature"android:layout_width="match_parent"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintTop_toBottomOf="@+id/btn_clear"app:layout_constraintVertical_weight="1"app:backColor="#FFFFFF"/></androidx.constraintlayout.widget.ConstraintLayout>
主要逻辑代码:
public class MainActivity extends AppCompatActivity {private static final String TAG ="didijiuwoa" ;private SignatureView mSignatureView;private Button btnClear;private Button btnSave;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);checkPermission();initView();}private void checkPermission() {//Android平台版本,如我的版本为Android 7.1.2Log.v(TAG,"Build.VERSION.RELEASE----->"+ Build.VERSION.RELEASE);//当前手机版本-API版本号Log.v(TAG,"android.os.Build.VERSION.SDK_INT----->"+Build.VERSION.SDK_INT);//android 6.0 对应的 API版本号23Log.v(TAG,"Build.VERSION_CODES.M----->"+Build.VERSION_CODES.M);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//android 6.0以上Log.v(TAG,"测试手机版本为:android 6.0以上");int writePermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);if (writePermission != PackageManager.PERMISSION_GRANTED) {Log.v(TAG,"测试手机版本为:android 6.0以上--->未申请--->申请读写权限");requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);}else{Log.v(TAG,"测试手机版本为:android 6.0以上--->已申请");}}else{//android 6.0以下Log.v(TAG,"测试手机版本为:android 6.0以下");}}private void initView() {mSignatureView = findViewById(R.id.view_signature);btnClear = findViewById(R.id.btn_clear);btnSave = findViewById(R.id.btn_save);btnClear.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mSignatureView.clear();}});btnSave.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//Intent intent = new Intent(MainActivity.this,ImageActivity.class);//startActivity(intent);if (mSignatureView.getTouched()) {try {mSignatureView.save("/sdcard/sign.png", true, 10,MainActivity.this);Toast.makeText(MainActivity.this, "图片保存在:"+mSignatureView.getSavePath(), Toast.LENGTH_SHORT).show();} catch (IOException e) {e.printStackTrace();}} else {Toast.makeText(MainActivity.this, "请先签名", Toast.LENGTH_SHORT).show();}}});}@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == 100) {if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[0]== PackageManager.PERMISSION_GRANTED) {//允许Log.v(TAG, "测试手机版本为:android 6.0以上--->未申请--->申请读写权限--->成功!");} else {//拒绝Log.v(TAG, "测试手机版本为:android 6.0以上--->未申请--->申请读写权限--->失败!");Toast.makeText(this, "请赋予读写权限,否则应用将无法使用!", Toast.LENGTH_LONG).show();MainActivity.this.finish();}}}}
ok 这就结束了