一年多前写过一篇文章, Android扩大View点击区域, 此方法存在诸多限制,且只能对其中一个子View进行点击范围的扩展, 今天想到一个新的思路, 实现并支持扩展任意多个子View的点击范围,供君参考(源码见文末)。
使用实例
binding.cv1.setOnClickListener {
ToastUtils.show("cv1 Click!!")
}
val dp20 = 20.dpToPx()
ClickAreaExpander()
.addExpandView(binding.cv1, dp20, dp20)
//.addExpandView(binding.cv2, dp20, dp20)
//.....
.expandOn(binding.touchOutView)
XML示例
预览效果
<FrameLayout
android:id="@+id/touchOutView"
android:layout_width="match_parent"
android:layout_height="200dp">
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:background="#22000000" />
<View
android:id="@+id/cv1"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="center"
android:background="@drawable/shape_white_16" />
</FrameLayout>
源码实现
class ClickAreaExpander : View.OnTouchListener {
private var touchDelegates: MutableList<TouchDelegate> = mutableListOf()
private var enable = false
private var mItems = mutableListOf<Item>()
lateinit var vg: ViewGroup
var needInit = true
fun expandOn(viewGroup: ViewGroup) {
vg = viewGroup
enable = true
vg.setOnTouchListener(this)
}
/**
* 增加需要扩宽点击范围的View
*
* @param view 扩充点击/长按事件的VIew
* @param leftTop 扩宽距离,单位px
* @param rightBottom 扩宽距离,单位px
*/
fun addExpandView(view: View, leftTop: Int, rightBottom: Int): ClickAreaExpander {
mItems.add(Item(view, leftTop, rightBottom))
needInit = true
return this
}
/**
* 移除
*/
fun removeView(view: View): ClickAreaExpander {
mItems.firstOrNull { x -> x.view == view }?.let {
mItems.remove(it)
needInit = true
}
return this
}
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
event ?: return false
v ?: return false
var handle = false
if (enable) {
if (needInit) {
if (mItems.isEmpty()) {
enable = false
return false
}
initViews()
}
for (i in 0 until touchDelegates.size) {
val item = touchDelegates[i]
val h = item.onTouchEvent(event)
if (h) {
handle = true
break
}
}
}
return handle
}
fun enable(e: Boolean = true) {
enable = e
}
private fun initViews() {
needInit = false
touchDelegates.clear()
mItems.forEach {
val v = it.view
if (vg.contains(v)) {
val rect = Rect()
v.getHitRect(rect)
rect.inset(-it.leftTop, -it.rightBottom)
touchDelegates.add(0, TouchDelegate(Rect(rect), v))
}
}
}
inner class Item(val view: View, val leftTop: Int, val rightBottom: Int)
}