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();
//        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 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));bm.compress(Bitmap.CompressFormat.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="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/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 这就结束了

Android 电子签名/手写签名 保存到相册详解相关推荐

  1. android 电子签名 手写签名 功能实现

    android 电子签名  手写签名 功能实现 这个手写的效果 就是一个 重写的的自定义的view  代码如下: package com.example.hand.views;import java. ...

  2. 【卷积神经网络CNN 实战案例 GoogleNet 实现手写数字识别 源码详解 深度学习 Pytorch笔记 B站刘二大人 (9.5/10)】

    卷积神经网络CNN 实战案例 GoogleNet 实现手写数字识别 源码详解 深度学习 Pytorch笔记 B站刘二大人 (9.5/10) 在上一章已经完成了卷积神经网络的结构分析,并通过各个模块理解 ...

  3. Android电子手写签名

    前言:最近一直在做h5,尼玛!!!前端真伤不起啊,由于刚接触,害怕兼容性啥的,导致我一个页面写了一天,泪崩啦-- 好吧,觉得得去撸一撸android才能平复的不安的小内心,刚好看到有一个这样的需求,需 ...

  4. java实现手写签名,Android实现手写签名

    本文实例为大家分享了Android手写签名的实现方法,产品要求用户可以在app上签协议..所以得弄个手写签名版,参考了一些资料自己写了个PaintView去继承View,实现签名功能. package ...

  5. php手写签名保存,PHP+JS实现PC端+移动端PDF手写签名合并

    PHP+JS实现PC端+移动端PDF手写签名合并 1年前 阅读 3029 评论 0 喜欢 0 前端插件:`jSignature.js` PHP插件:`FPDF+FPDI` `index.html`代码 ...

  6. Android屏幕手写签名的实现-详细篇

    老规矩先看一下效果图 [实现思路] (1)创建画笔 private void init(Context context) {this.mContext = context;//设置抗锯齿mGestur ...

  7. 用TensorFlow做Kaggle“手写识别”达到98%准确率-详解

    (点击"阅读原文"即可进入查看课程表) 刘颖,某互联网创业公司COO,技术出身,做产品里最懂运营的. 这是一个TensorFlow的系列文章,本文是第三篇,在这个系列中,你讲了解到 ...

  8. 读书笔记-深度学习入门之pytorch-第四章(含卷积神经网络实现手写数字识别)(详解)

    1.卷积神经网络在图片识别上的应用 (1)局部性:对一张照片而言,需要检测图片中的局部特征来决定图片的类别 (2)相同性:可以用同样的模式去检测不同照片的相同特征,只不过这些特征处于图片中不同的位置, ...

  9. 四、用简单神经网络识别手写数字(内含代码详解及订正)

    本博客主要内容为图书<神经网络与深度学习>和National Taiwan University (NTU)林轩田老师的<Machine Learning>的学习笔记,因此在全 ...

最新文章

  1. 不要在 Spring Boot 集成测试中使用 @Transactional
  2. 转载:身体器官工作表一览,睡觉别太晚了
  3. Linux 磁盘与文件系统管理
  4. C++中int *p[4]和 int (*q)[4]的区别
  5. arduino naon介绍_Arduino Nano 自制版
  6. 网易致歉了,是为了员工还是为了舆论压力?
  7. linux usb驱动u盘启动不了,Linux环境下USB的原理、驱动和配置(4)
  8. 算法高级(33)-拓扑排序-maven依赖关系的确定
  9. 浏览器 制作---TWebBrowser 控件
  10. 互联网行业的体面隐退 | 畅言
  11. T+T+.....+T的递归下降子程序
  12. Caffe常见神经网络结构——汇总与对比
  13. java socket 客户端接收_java socket,怎么把客户端接收到的用户名和密码发送给后台?...
  14. Android中app的请求抓包工具 Fiddler 详解
  15. 室友转行软件测试,月薪1.4万,天天摸鱼没事干
  16. TIM下载的文件在哪个手机文件夹中(安卓)
  17. 【雅思大作文考官范文】——第十九篇:独居人口' essay
  18. 计算机怎么制作个人简历表步骤图片,教你如何制作个人简历表格!ppt课件
  19. android垂直进度条控件,Android常用控件之ProgressBar,水平进度条
  20. 使用VS+VisualGDB编译Linux版本RCF

热门文章

  1. 福州自贸片区首张电商营业执照发出
  2. 树莓派研究笔记(8)-- 编译lakka v2.1源码
  3. AI是如何为安防赋能 AI摄像机落地应用现状​
  4. 如何才能快速准确地获得客户资源?
  5. AUTH_USER_MODEL refers to model报错解决
  6. 有关于界面设计的技巧
  7. 老鼠和奶酪——————C++
  8. 小马哥---高仿苹果5s 主板型号A182 芯片为6582 拆机主板刷机多图展示
  9. ABR业务类最新发表的论文,有趋势介绍吗?
  10. Sublime Text个性化设置