Android 手写签名板
文章目录
- Android 手写签名板
- 概述
- 效果
- 代码实现
- 源码下载
Android 手写签名板
概述
手写签名板功能,支持图片保存、支持去除空白区域。
效果
生成图片效果:
代码实现
定义属性:
<declare-styleable name="SignatureView"><attr name="bgColor" format="color" /><attr name="brushColor" format="color" /><attr name="brushWidth" format="dimension" />
</declare-styleable>
代码:
class SignatureView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {companion object {const val DEFAULT_BG_COLOR = Color.WHITEconst val DEFAULT_BRUSH_COLOR = Color.BLACKconst val DEFAULT_BRUSH_WIDTH = 12F}private var bgColor: Int = DEFAULT_BG_COLORprivate var brushColor: Int = DEFAULT_BRUSH_COLORprivate var brushWidth = DEFAULT_BRUSH_WIDTHprivate val paint = Paint().apply {isAntiAlias = truecolor = brushColorstyle = Paint.Style.STROKEstrokeWidth = brushWidth}private val path = Path()private var touchX: Float = 0Fprivate var touchY: Float = 0Fprivate var isClear = falseinit {val typedArray = context.obtainStyledAttributes(attrs, R.styleable.SignatureView)bgColor = typedArray.getColor(R.styleable.SignatureView_bgColor, DEFAULT_BG_COLOR)brushColor = typedArray.getColor(R.styleable.SignatureView_brushColor, DEFAULT_BRUSH_COLOR)brushWidth =typedArray.getDimension(R.styleable.SignatureView_brushWidth, DEFAULT_BRUSH_WIDTH)typedArray.recycle()paint.apply {color = brushColorstrokeWidth = brushWidth}setBackgroundColor(bgColor)}/*** 保存图片** @param imageDirs 目录* @param imageName 文件名* @param clearBlank 是否清除空白区域* @param blank 边距*/fun save(imageDirs: String, imageName: String, clearBlank: Boolean = false, blank: Int = 0) {if (imageDirs.isEmpty() || imageName.isEmpty()) {return}if (path.isEmpty) {return}var bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)val canvas = Canvas(bitmap)canvas.drawColor(bgColor)canvas.drawPath(path, paint)if (clearBlank) {bitmap = clearBlank(bitmap, blank)}val dir = File(imageDirs)if (!dir.exists())dir.mkdirs()val file = File(dir, imageName)if (file.exists())file.delete()val out = FileOutputStream(file)try {bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)} catch (e: Exception) {e.printStackTrace()} finally {out.close()}}/*** 清除空白区域** @param bitmap 原图* @param blank 保留边距* @return*/private fun clearBlank(bitmap: Bitmap, blank: Int): Bitmap {var space = blankval width = bitmap.widthval height = bitmap.heightvar left = 0var right = 0var top = 0var bottom = 0var pixs = IntArray(width)var isStop = false//扫描上边距不等于背景颜色的第一个点for (i in 0 until height) {bitmap.getPixels(pixs, 0, width, 0, i, width, 1)isStop = falsefor (pix in pixs) {if (pix != bgColor) {top = iisStop = truebreak}}if (isStop) {break}}//扫描下边距不等于背景颜色的第一个点for (i in height - 1 downTo 0) {bitmap.getPixels(pixs, 0, width, 0, i, width, 1)isStop = falsefor (pix in pixs) {if (pix != bgColor) {bottom = iisStop = truebreak}}if (isStop) {break}}pixs = IntArray(height)//扫描左边距不等于背景颜色的第一个点for (x in 0 until width) {bitmap.getPixels(pixs, 0, 1, x, 0, 1, height)isStop = falsefor (pix in pixs) {if (pix != bgColor) {left = xisStop = truebreak}}if (isStop) {break}}//扫描右边距不等于背景颜色的第一个点for (x in width - 1 downTo 1) {bitmap.getPixels(pixs, 0, 1, x, 0, 1, height)isStop = falsefor (pix in pixs) {if (pix != bgColor) {right = xisStop = truebreak}}if (isStop) {break}}if (space < 0) {space = 0}//计算加上保留空白距离之后的图像大小left = Math.max(left - space, 0)top = Math.max(top - space, 0)right = Math.min(right + space, width - 1)bottom = Math.min(bottom + space, height - 1)return Bitmap.createBitmap(bitmap, left, top, right - left, bottom - top)}/*** 清除画板*/fun clear() {isClear = trueinvalidate()}override fun onTouchEvent(event: MotionEvent): Boolean {when (event.action) {MotionEvent.ACTION_DOWN -> {touchX = event.xtouchY = event.ypath.moveTo(touchX, touchY)return true}MotionEvent.ACTION_MOVE -> {val x = event.xval y = event.yLog.e("TAG", "moveX:$x moveY:$y")// 计算滑动时偏移值val dx = Math.abs(x - touchX)val dy = Math.abs(y - touchY)// 偏移值大于3px绘制if (dx >= 3 || dy >= 3) {val cx = (x + touchX) / 2val cy = (y + touchY) / 2path.quadTo(touchX, touchY, cx, cy)touchX = xtouchY = y}invalidate()}}return super.onTouchEvent(event)}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)if (isClear) {canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);canvas.drawColor(bgColor)path.reset()isClear = false} else {if (!path.isEmpty) {canvas.drawPath(path, paint)}}}
}
使用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns: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"android:orientation="vertical"tools:context=".signature.SignatureActivity"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onSave1"android:text="保存1" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onSave2"android:text="保存2" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onClear"android:text="清除" /></LinearLayout><com.example.widgets.signature.SignatureViewandroid:id="@+id/signature_view"android:layout_width="match_parent"android:layout_height="match_parent"app:bgColor="@color/blue"app:brushColor="@color/red"app:brushWidth="6dp" />
</LinearLayout>
class SignatureActivity : BaseActivity() {private lateinit var signatureView: SignatureViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_signature)signatureView = findViewById(R.id.signature_view)}fun onSave1(view: View) {val imageDir = "${this.cacheDir}/signature"val imageName = "${System.currentTimeMillis()}.png"signatureView.save(imageDir, imageName)}fun onSave2(view: View) {val imageDir = "${this.cacheDir}/signature"val imageName = "${System.currentTimeMillis()}.png"signatureView.save(imageDir, imageName, true, 10)}fun onClear(view: View) {signatureView.clear()}
}
源码下载
相关文章:
Android 手写签名板
文章目录 Android 手写签名板概述效果代码实现源码下载 Android 手写签名板 概述 手写签名板功能,支持图片保存、支持去除空白区域。 效果 生成图片效果: 代码实现 定义属性: <declare-styleable name"SignatureView">&…...
Java基础夯实——2.9 多线程如何共享数据
在 Java 多线程编程中,共享数据通过以下几种方式实现: 1. 使用共享对象 多个线程可以通过引用同一个对象来实现数据共享。例如: class SharedData {private int count;public synchronized void increment() {count;}public synchronized …...
京准电钟:NTP网络校时服务器从入门到精准
京准电钟:NTP网络校时服务器从入门到精准 京准电钟:NTP网络校时服务器从入门到精准 1.前言 由计算机网络系统组成的分布式系统,若想协调一致进行:IT行业的“整点开拍”、“秒杀”、“Leader选举”,通信行业的“同步…...
【C++习题】15.滑动窗口_串联所有单词的子串
文章目录 题目链接:题目描述:解法C 算法代码:图解 题目链接: 30. 串联所有单词的子串 题目描述: 解法 滑动窗口哈希表 这题和第14题不同的是: 哈希表不同:hash<string,int>left与right指…...
【好玩的经典游戏】Docker环境下部署贪吃蛇网页小游戏(二)
【好玩的经典游戏】Docker环境下部署贪吃蛇网页小游戏(二) 一、贪吃蛇小游戏介绍1.1 小游戏简介1.2 项目预览二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.3 检查Docker版本3.4 检查docker compose 版本四、下载容器镜像五、部…...
《Python Web 抓取实战:豆瓣电影 Top 250 数据抓取与分析》
引言 在信息爆炸的时代,掌握数据获取的能力显得尤为重要。通过数据抓取,我们可以从互联网上获取大量有价值的信息,并进行深入分析。本文将介绍如何使用 Python 进行 Web 抓取,以豆瓣电影 Top 250 为例,展示数据抓取的…...
Qt-系统相关(2)多线程网络
Qt多线程 在 Qt 中,多线程的处理⼀般是通过 QThread类 来实现。 QThread 代表⼀个在应⽤程序中可以独⽴控制的线程,也可以和进程中的其他线程共享数据。 QThread 对象管理程序中的⼀个控制线程。 QThread 常⽤ API: 使用线程 关于创建线程…...
代码随想录算法训练营第五十九天|Day59 图论
Bellman_ford 算法精讲 https://www.programmercarl.com/kamacoder/0094.%E5%9F%8E%E5%B8%82%E9%97%B4%E8%B4%A7%E7%89%A9%E8%BF%90%E8%BE%93I.html 思路 #include <stdio.h> #include <stdlib.h> #include <limits.h>#define MAXM 10000 // 假设最大边数为1…...
SpringBoot集成ESAPI
官网地址:https://github.com/ESAPI/esapi-java-legacy 一、POM依赖 <dependency><groupId>org.owasp.esapi</groupId><artifactId>esapi</artifactId><version>2.6.0.0</version> <!-- Preferably the latest ver…...
Git 进程占用报错-解决方案
背景 大仓库,由于开发者分支较多,我们在使用 git pull 或 git push 等命令时(与远端仓库交互的命令),不知之前配置了什么,我的电脑会必现以下报错(有非常长一大串报错-不同分支的git进程占用报…...
python怎么设置静态变量
众所周知,Python语言并不支持静态变量,比如下面这样一个应用场景: void foo() {static int count 0;count ; } 在Python中无法自然实现这个功能。换个角度来看这个问题,函数中的静态变量其实可以看做是函数的一个内部变量&#x…...
【H2O2|全栈】Node.js(1)
目录 前言 开篇语 准备工作 ES6导入导出 导入 有名导出 匿名导出 Node概念 Node导入导出 导入 有名导出 匿名导出 Node常用模块 path模块 和路径有关的全局变量 常见方法 导入方法 fs模块 常见方法 导入方法 结束语 前言 开篇语 本系列博客主要分享Java…...
SJYP 24冬季系列 FROZEN CHARISMA发布
近日,女装品牌SJYP 2024年冬季系列——FROZEN CHARISMA已正式发布,展现了更加干练的法式风格。此次新品发布不仅延续了SJYP一贯的强烈设计风格和个性时尚,更融入了法式风情的干练元素,为消费者带来了一场视觉与穿着的双重盛宴。 …...
嵌入式硬件实战基础篇(四)多路直流稳压电源
设计一个多路直流稳压电源 要求设计制作一个多路输出直流稳压电源,可将220 V / 5 0HZ交流电转换为5路直流稳压输出。具体要求: 输出直流电压 12V, 5V;和一路输出3- 15V连续可调直流稳压电源: 输出电流Iom500mA; 稳压系数 Sr≤0.05;...
力扣_876. 链表的中间结点
力扣_876. 链表的中间结点 给你单链表的头结点 head ,请你找出并返回链表的中间结点。 如果有两个中间结点,则返回第二个中间结点。 输入:head [1,2,3,4,5] 输出:[3,4,5] 解释:链表只有一个中间结点,值为…...
shell脚本命令(一)
shell脚本命令(一) 一、Shell 基础知识 Shell的基本概念 定义: Shell 是操作系统中的命令解释器,位于用户与操作系统内核之间。它接收并解释用户输入的命令,将任务传递给操作系统内核执行,然后将执行结果…...
一文理解多模态大语言模型——上
作者:Sebastian Raschka 博士, 翻译:张晶,Linux Fundation APAC Open Source Evangelist 编者按:本文并不是逐字逐句翻译,而是以更有利于中文读者理解的目标,做了删减、重构和意译,…...
[代码随想录Day24打卡] 93.复原IP地址 78.子集 90.子集II
93.复原IP地址 一个合法的IP地址是什么样的: 有3个’.分割得到4个数,每个数第一个数不能是0,不能含有非法字符,不能大于255。 这个是否属于合法IP相当于一个分割问题,把一串字符串分割成4部分,分别判断每…...
去哪儿大数据面试题及参考答案
Hadoop 工作原理是什么? Hadoop 是一个开源的分布式计算框架,主要由 HDFS(Hadoop 分布式文件系统)和 MapReduce 计算模型两部分组成 。 HDFS 工作原理 HDFS 采用主从架构,有一个 NameNode 和多个 DataNode。NameNode 负责管理文件系统的命名空间,维护文件和目录的元数据信…...
controller中的参数注解@Param @RequestParam和@RequestBody的不同
现在controller中有个方法:(LoginUserRequest是一个用户类对象) PostMapping("/test/phone")public Result validPhone(LoginUserRequest loginUserRequest) {return Result.success(loginUserRequest);}现在讨论Param("login…...
排序算法思维导图
冒泡排序 def bubble_sort(alist):j 0while j < len(alist):i 0while i < len(alist) - 1:if alist[i] > alist[i1]:alist[i], alist[i1] alist[i1], alist[i]i 1j 1li [34,556,235,7,56,45,63,35,23,4,875] bubble_sort(li) print(li) 选择排序 def select_s…...
ZYNQ试用于哪些场景
ZYNQ系列SoC(System on Chip)由于其独特的处理器与FPGA(可编程逻辑)集成设计,非常适合应用于多种需要高性能、灵活性和低功耗的场景。 以下是一些ZYNQ特别适用的场景: 嵌入式系统:ZYNQ的ARM处理…...
【03】Selenium+Python 八种定位元素方法
操作元素,需要先查找定位到对应的元素。 查找单个元素:driver.find_element() 返回是一个web element 对象 查找多个元素:driver.find_elements() 返回是一个list对象 By 是 Selenium 中一个非常重要的类,用于定位网页元素。 使…...
Java 自动资源管理(Auto Resource Management)详解
Java 自动资源管理(Auto Resource Management)详解 在Java编程中,资源的正确管理是开发过程中一个非常重要的环节。如果资源(如文件、数据库连接、网络连接等)未被正确释放,可能会导致资源泄漏,…...
IT运维专家给年轻人一些职业上的建议
运维工作在现代企业中是非常重要的一环,保证系统的稳定性、可用性以及安全性对企业的正常运营至关重要。以下是我给年轻人的一些职业发展建议,希望能够帮助你们在运维领域找到方向并取得成功。 1. 夯实基础,扎实技术功底 精通操作系统与网络:运维工作需要深入理解操作系统…...
视图查询中投影裁剪规则的原理和解析 | OceanBase 查询优化
背景 在SQL查询中使用视图查询时,执行中可能会产生的较多的中间结果集。为了优化这类查询的执行,OceanBase 引入了投影裁剪规则。能够识别出父查询中未实际使用的列,并将这些列从视图查询的select列表中剔除,进而提升整体查询的性…...
利用Nginx在服务器上部署你的第一个静态页面
文章目录 序言Nginx常用功能1. 反向代理2.负载均衡轮询加权轮询IP_Hash Nginx配置文件结构安装并部署你的静态网页1. 安装(懒人直接看3)2.查看配置文件3.编写或放入自己的静态页面文件 序言 首先我们先来认识一下什么是Nginx源码 官网 Nginx (engine x) 是一个高性能的HTTP和…...
【拥抱AI】RAG如何通过分析反馈、识别问题来提高命中率
分析用户反馈并识别问题是持续优化RAG系统的重要步骤。这不仅可以帮助你了解系统的当前表现,还可以指导未来的改进方向。直接进入正题, 1. 收集用户反馈 方法 问卷调查:设计问卷,让用户填写他们对系统输出的满意度、易用性等方…...
内核模块签名验证
安装内核模块报错 今天在调试地平线 J6 板子时,安装自己编译的内核模块报错 roothobot:/tmp# insmod hobot_eth_j6.ko insmod: ERROR: could not insert module hobot_eth_j6.ko: Key was rejected by service前两天刚在 x86 电脑上解决过这个问题,参…...
C++模板(入门)
文章目录 泛型编程函数模板函数模板的概念函数模板格式函数模板的原理函数模板的实例化隐式实例化显示实例化模板参数的匹配 类模板为什么有类模板类模板的定义格式类模板的实例化Stack模板类的简单实现(不涉及深拷贝) 模板的注意问题模板不支持分离编译…...
如何在Python中进行数学建模?
数学建模是数据科学中使用的强大工具,通过数学方程和算法来表示真实世界的系统和现象。Python拥有丰富的库生态系统,为开发和实现数学模型提供了一个很好的平台。本文将指导您完成Python中的数学建模过程,重点关注数据科学中的应用。 数学建…...
C++优质学习资源汇总
1 学懂C语言-C核心编程精讲 学懂C语言-C核心编程精讲 该课程基本把面试所需要的C常用的网络等教程均进行讲解,满足基本需求...
心情追忆:构建支付模块的五个基本接口设计
之前,我独自一人开发了一个名为“心情追忆”的小程序,旨在帮助用户记录日常的心情变化及重要时刻。我从项目的构思、设计、前端(小程序)开发、后端搭建到最终部署。经过一个月的努力,通过群聊分享等方式,用…...
数据库导论
data 数据是数据库中存储的基本数据,描述事物的符号称为数据。 DB 数据库是长期存储在计算机内,有组织,可共享的大量数据的集合。数据库中的数据按照一定的数据模型组织,描述和存储,具有较小的冗余度,较…...
echarts使用示例
柱状图折线图 折柱混合:https://echarts.apache.org/examples/zh/editor.html?cmix-line-bar option {title:{show: true},tooltip: {trigger: axis,axisPointer: {type: cross,crossStyle: {color: #999}}},toolbox: {feature: {dataView: { show: true, readOnl…...
选修课(Java Python JS C++ C )
题目描述 现有两门选修课,每门选修课都有一部分学生选修,每个学生都有选修课的成绩,需要你找出同时选修了两门选修课的学生,先按照班级进行划分,班级编号小的先输出,每个班级按照两门选修课成绩和的降序排序,成绩相同时按照学生的学号升序排序。 输入描述 第一行为第…...
《解锁 C++数据读写秘籍:赋能人工智能训练》
在人工智能蓬勃发展的时代,数据无疑是驱动模型学习与成长的核心燃料。而 C作为一门高性能编程语言,在处理人工智能训练所需数据集的读取与写入时,有着独特的优势与关键作用。高效地运用 C进行数据操作,能够显著加速训练进程&#…...
23种设计模式-外观(Facade)设计模式
文章目录 一.什么是外观设计模式?二.外观设计模式的特点三.外观设计模式的结构四.外观设计模式的优缺点五.外观设计模式的 C 实现六.外观设计模式的 JAVA 实现七.代码解析八.总结 类图: 外观设计模式类图 一.什么是外观设计模式? 外观设计模…...
ReactPress(阮一峰推荐工具):一款基于Next.js的免费开源博客CMS系统
ReactPress Github项目地址:https://github.com/fecommunity/reactpress 欢迎Star。 此项目是用于构建博客网站的,包含前台展示、管理后台和后端。 此项目是基于 React antd NestJS NextJS MySQL 的,项目已经开源,项目地址在 …...
什么是缓存击穿?如何避免之布隆过滤器
缓存击穿(Cache Penetration)是分布式系统和缓存使用中的一个常见问题,布隆过滤器在解决缓存击穿问题时非常有用。接下来我会介绍缓存击穿的概念以及布隆过滤器在解决该问题中的应用。 什么是缓存击穿? 缓存击穿是指当大量的客户…...
React 第八节组件生命周期钩子-类式组件,函数式组件模拟生命周期用法
概述 React组件的生命周期可以分为三个主要阶段: 挂载阶段(Mounting):组件被创建,插入到DOM 树的过程; 更新阶段(Updating):是组件中 props 以及state 发生变化时&#…...
java虚拟机——如何排查jvm问题
在项目中排查JVM问题是一个系统性的过程,涉及到多个工具和方法。以下是一些常见的步骤和工具,可以帮助你有效地诊断和解决JVM相关的问题: 1. 监控和日志 日志分析 JVM日志:启用JVM的日志记录功能,查看垃圾收集日志、…...
Altium Designer PCB设计检查工具1
此工具最大的特点是不需要联网,完全使用本地的计算资源即可实现检查统计操作,可用于不能联网的应用场景中。此工具支持多种计算加速方法,支持调用CUDA显卡进行数据处理,此功能需要计算机安装Matlab 2016以上版本,并需要…...
统计词频
目标:统计词频 从文件1.txt ,读取内容,保存在一个字符串中统计字符串中,每个单词出现的频率对结果进行排序把最后的结果写入一个新的文件 import java.io.PrintWriter import scala.io.Source//知识点: //1.字符串&a…...
串,数组,广义表相关知识点
串 一.串的储存 1.基本概念 2.顺序储存 3.链式储存 二. 串的模式匹配算法 1.BF算法 将主串的第pos个字符和模式的第一个字符比较, 若相等,继续逐个比较后续字符; 若相等,继续逐个比较后续字符; 若不等,…...
Leetcode 131 Palindrome Partition
题意 把一个字符串分割成多个回文字符串的partition,返回所有的可能partion 链接 https://leetcode.com/problems/palindrome-partitioning/description/ 思考 这只是dfs套了一个回文问题 题解 dfs每次截取一段字符串,判断是否是回文 退出条件是遍…...
git使用文档手册
创建一个本地代码工作空间,比如这里使用test目录作为工作目录 针对仓库地址 http://192.168.31.125:9557/poxiaoai-crm/project-crm.git。 1. 安装 Git 确保您的系统已经安装了 Git。如果未安装,请根据操作系统访问 Git 官网 下载并安装。 验证安装 …...
开发需求总结19-vue 根据后端返回一年的数据,过滤出符合条件数据
需求描述: 定义时间分界点:每月26号8点,过了26号8点则过滤出data数组中符合条件数据下个月的数据,否则过滤出当月数据 1.假如现在是2024年11月14日,那么过滤出data数组中日期都是2024-11月的数据; 2.假如…...
android 安全sdk相关
前述 在网上有看到许多android安全sdk相关的内容,有重复的也有比较新鲜的内容,这里做一个整体的合集,以及后续又看到一些比较新的东西会一起放在这里。 android内sdk目前可以分为以下几个部分(有一些部分可能会存在一些重合&#…...
ChemBench—— 探索大语言模型在化学领域的新基准框架是否胜过化学专家
概述 大规模语言模型是一种机器学习模型,通过学习大量文本来生成文本。这些模型的能力正在迅速提高,现在已经可以通过美国国家医学考试。它们还可以与网络搜索和合成规划器等工具结合使用,自主设计化学反应和进行实验。 一些人认为这些模型…...