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.WHITE
const val DEFAULT_BRUSH_COLOR = Color.BLACK
const val DEFAULT_BRUSH_WIDTH = 12F
}
private var bgColor: Int = DEFAULT_BG_COLOR
private var brushColor: Int = DEFAULT_BRUSH_COLOR
private var brushWidth = DEFAULT_BRUSH_WIDTH
private val paint = Paint().apply {
isAntiAlias = true
color = brushColor
style = Paint.Style.STROKE
strokeWidth = brushWidth
}
private val path = Path()
private var touchX: Float = 0F
private var touchY: Float = 0F
private var isClear = false
init {
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 = brushColor
strokeWidth = 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 = blank
val width = bitmap.width
val height = bitmap.height
var left = 0
var right = 0
var top = 0
var bottom = 0
var pixs = IntArray(width)
var isStop = false
//扫描上边距不等于背景颜色的第一个点
for (i in 0 until height) {
bitmap.getPixels(pixs, 0, width, 0, i, width, 1)
isStop = false
for (pix in pixs) {
if (pix != bgColor) {
top = i
isStop = true
break
}
}
if (isStop) {
break
}
}
//扫描下边距不等于背景颜色的第一个点
for (i in height - 1 downTo 0) {
bitmap.getPixels(pixs, 0, width, 0, i, width, 1)
isStop = false
for (pix in pixs) {
if (pix != bgColor) {
bottom = i
isStop = true
break
}
}
if (isStop) {
break
}
}
pixs = IntArray(height)
//扫描左边距不等于背景颜色的第一个点
for (x in 0 until width) {
bitmap.getPixels(pixs, 0, 1, x, 0, 1, height)
isStop = false
for (pix in pixs) {
if (pix != bgColor) {
left = x
isStop = true
break
}
}
if (isStop) {
break
}
}
//扫描右边距不等于背景颜色的第一个点
for (x in width - 1 downTo 1) {
bitmap.getPixels(pixs, 0, 1, x, 0, 1, height)
isStop = false
for (pix in pixs) {
if (pix != bgColor) {
right = x
isStop = true
break
}
}
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 = true
invalidate()
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
touchX = event.x
touchY = event.y
path.moveTo(touchX, touchY)
return true
}
MotionEvent.ACTION_MOVE -> {
val x = event.x
val y = event.y
Log.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) / 2
val cy = (y + touchY) / 2
path.quadTo(touchX, touchY, cx, cy)
touchX = x
touchY = 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">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onSave1"
android:text="保存1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onSave2"
android:text="保存2" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClear"
android:text="清除" />
</LinearLayout>
<com.example.widgets.signature.SignatureView
android: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: SignatureView
override 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()
}
}
源码下载
原文地址:https://blog.csdn.net/qq_14876133/article/details/144078111
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!