上文Android:实现一个自定义有限制区域的图例(角度自识别)涂鸦工具类(上)中我们已经实现了自定义View签名的功能,包含撤回、清除方法。但我们最终要实现的功能还不止如此,这篇我们就来说说给限制区域内签名的实现过程。
试想,既然是自定义View了,那么如果要限制用户在指定区域内签名,最好的办法不是在触摸的时候通过坐标点的判断添加一些拦截吗?没错,起初我也是这么想的,但是再看到限制区域的图形后,我陷入了深深的沉思......
没错,就是这样的图,这还是其中的一张,后期指不定还会有多少张这样形状复杂的图。单看组成就不得了,都是些大小不一的圆相交相切,圆心散落在各个位置。但从自定义角度绘制这样的图形相信难度也不小,就更不要说通过坐标点的计算来拦截触摸事件的方式限制签名范围了。
此时绝望的我突然想起之前项目中的一个上传图片功能,当时是利用了ViewGroup作为遮罩来简单实现的,那么,这个功能其实也可如此。我们大可不必大费周章的采用触摸事件判断呀,超出范围如果使用画布遮挡其实也能满足。结合项目需求最后涂鸦完成后,需要生成图片,我们也可通过View自带的Draw方法生成图片,咱们说干就干。
首先,自定义一个ViewGroup(作为遮罩、生成图片使用)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class FaceViewGroup(context: Context, attrs: AttributeSet? = null) :
LinearLayout(context, attrs, 0) {
private var mWith = 0
private var mHight = 0
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
mWith = MeasureSpec.getSize(widthMeasureSpec)
mHight = MeasureSpec.getSize(heightMeasureSpec)
}
override fun dispatchDraw(canvas: Canvas?) {
......
}
}
|
这里我们需要了解下dispatchDraw方法:
绘制 ViewGroup 中的子 View 时,会调用 dispatchDraw(Canvas canvas),需要注意的是,是在绘制 ViewGroup 自己之后,也就是在 onDraw(Canvas canvas) 之后。
最后才会触发这个方法,所以利用它绘制遮罩再合适不过,我们可以让UI将各种复杂的图形切出来保存再本地,再利用dispatchDraw方法中的画布将图片绘制在中心。切图时需要注意,我们需要绘制的区域需要透明。
1
2
3
4
5
6
7
8
|
//本地区域图
val bitmap = BitmapFactory.decodeResource(resources,R.mipmap.ic_face)
//绘制到ViewGroup中
canvas?.drawBitmap(bitmap, (mWith-bitmap.width)/2f, (mHight-bitmap.height)/2f, Paint().apply {
color = Color.WHITE
isAntiAlias = true
style = Paint.Style.FILL
})
|
在Xml:
1
2
3
4
5
6
7
8
9
10
|
<com.example.FaceViewGroup
android:id="@+id/group"
android:layout_width="414dp"
android:layout_height="280dp">
<com.example.SignatureView
android:id="@+id/linePath"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="2dp" />
</com.example.FaceViewGroup>
|
SignatureView就是我们上篇讲到的自定义View,运行起来发现效果还不错,确实实现了限制的问题,虽然不是真正意义上的限制,但效果一样,满足需求了。最后生成图片:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
val bitmap: Bitmap = Bitmap.createBitmap(
resources.displayMetrics,
binding.group.width,
binding.group.height,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
binding.group.setBackgroundColor(ContextCompat.getColor(this, R.color.white))
//解决白底问题
canvas.drawRect(
Rect(
0,
0,
bitmap.width,
bitmap.height
), Paint().apply {
color = Color.WHITE
style = Paint.Style.FILL
})
binding.group.draw(canvas)
|
这里说下,如果不设置背景色,通过View的draw方式生成的图片是黑底的。另外还有个值得注意的点,如果使用了三方ui适配方案,你会发现无论怎么调整视图和Bitmap最后生成的图片和实际绘制看到的图片存在问题—绘制的内容偏离了限制区域,最终发现需要进行适配,也就是这一步,参考资料:
1
2
3
4
5
6
7
|
//这里需要添加resources.displayMetrics才能适配宽高
val bitmap: Bitmap = Bitmap.createBitmap(
resources.displayMetrics,
binding.group.width,
binding.group.height,
Bitmap.Config.ARGB_8888
)
|
最开始一直以为是横竖屏的切换导致位置偏移,实则是使用了AndroidAutoSize使得尺寸发生变化,所以在创建Bitmap时需要将适配过后的displayMetrics传入。到此,我们就完成了异性区域内涂鸦功能。
总结
有时候不能被固定思维所困住,换种思路或许能让复杂的问题轻松解决,但还是需要多积累经验才行。如果没有之前的自定义View和上传图片的经验,或许实现上述功能还不能找到简单的实现方式。好了,这篇就先介绍到这里,下篇我们将基于本篇的实现上,添加根据手势自动判断方向,实现图例按照手势移动的方向显示。
|