调用 canvas.scale() 不会缩放视图的点击侦听器区域
Calling canvas.scale() doesn't scale the click listener area of the views
我在 LinearLayout 中有一堆按钮。在 LinearLayout 中,我重写 onDraw() 以调用 canvas.scale(scale, scale),其中 scale 是某个数字。 LinearLayout 中的按钮会在视觉上缩放,但点击区域不会随着视觉表示移动和缩放。也就是说,点击区域在缩放后注册到原来的位置
这是一个使用滑块缩放 canvas 的演示。缩放后可以看到按钮只有在原位置点击时才会显示点击动画
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.slider.Slider
android:id="@+id/mySlider"
android:valueFrom=".5"
android:valueTo="3"
android:value="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.dgnt.playground.MyLinearLayout
android:id="@+id/myContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
</com.dgnt.playground.MyLinearLayout>
</LinearLayout>
</layout>
自定义线性布局
class MyLinearLayout : LinearLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
context,
attrs,
defStyle
)
init {
setWillNotDraw(false)
}
private var scale: Float = 1.0f;
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.scale(scale, scale)
}
fun setScale(value: Float) {
scale = value
invalidate()
requestLayout()
}
}
主要Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityMainBinding.inflate(layoutInflater).apply {
setContentView(root)
mySlider.addOnChangeListener { slider, value, fromUser ->
myContainer.setScale(value)
}
}
}
}
我尝试覆盖 dispatchTouchEvent 来确定用户的触摸坐标是否拦截了视图,但似乎 view.getLocationOnScreen() 也没有考虑 canvas 的比例正如这个问题所证明的 Android: Get View Location on Screen after View is scaled?
我该怎么做才能让 click/touch 准确记录视图在缩放 canvas 之前和之后的位置?
解决此问题的一种方法是缩放您的触摸事件,我在我创建的具有缩放和平移的自定义视图中完成了此操作。
在你的 MyLinearLayout
覆盖 dispatchTouchEvent
创建一个应用比例因子的 Matrix
,然后在调用原始 dispatchTouchEvent
之前用比例矩阵转换 MotionEvent
抱歉 Java 但类似的东西(有点来自记忆,而不是我是如何做到的,因为它有一些更复杂的平移用例和屏幕不同部分的不同平移)
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Matrix matrix = new Matrix();
Matrix mappingMatrix = new Matrix();
matrix.setScale(scaleFactor, scaleFactor);
// From memory the Matrix for the transform has to be inverted
matrix.invert(mappingMatrix);
// Copy motion event because I seem to remember you cannot change the original motion event
MotionEvent transformEvent = MotionEvent.obtain(ev);
transformEvent.transform(mappingMatrix);
// call parent class so this processes the modified event as normal
boolean returnVal = super.dispatchTouchEvent(transformEvent);
// Tidy up the copied motion event to not leak memory
transformEvent.recycle();
return returnVal;
}
我在 LinearLayout 中有一堆按钮。在 LinearLayout 中,我重写 onDraw() 以调用 canvas.scale(scale, scale),其中 scale 是某个数字。 LinearLayout 中的按钮会在视觉上缩放,但点击区域不会随着视觉表示移动和缩放。也就是说,点击区域在缩放后注册到原来的位置
这是一个使用滑块缩放 canvas 的演示。缩放后可以看到按钮只有在原位置点击时才会显示点击动画
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.slider.Slider
android:id="@+id/mySlider"
android:valueFrom=".5"
android:valueTo="3"
android:value="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.dgnt.playground.MyLinearLayout
android:id="@+id/myContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
</com.dgnt.playground.MyLinearLayout>
</LinearLayout>
</layout>
自定义线性布局
class MyLinearLayout : LinearLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
context,
attrs,
defStyle
)
init {
setWillNotDraw(false)
}
private var scale: Float = 1.0f;
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.scale(scale, scale)
}
fun setScale(value: Float) {
scale = value
invalidate()
requestLayout()
}
}
主要Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityMainBinding.inflate(layoutInflater).apply {
setContentView(root)
mySlider.addOnChangeListener { slider, value, fromUser ->
myContainer.setScale(value)
}
}
}
}
我尝试覆盖 dispatchTouchEvent 来确定用户的触摸坐标是否拦截了视图,但似乎 view.getLocationOnScreen() 也没有考虑 canvas 的比例正如这个问题所证明的 Android: Get View Location on Screen after View is scaled?
我该怎么做才能让 click/touch 准确记录视图在缩放 canvas 之前和之后的位置?
解决此问题的一种方法是缩放您的触摸事件,我在我创建的具有缩放和平移的自定义视图中完成了此操作。
在你的 MyLinearLayout
覆盖 dispatchTouchEvent
创建一个应用比例因子的 Matrix
,然后在调用原始 dispatchTouchEvent
MotionEvent
抱歉 Java 但类似的东西(有点来自记忆,而不是我是如何做到的,因为它有一些更复杂的平移用例和屏幕不同部分的不同平移)
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Matrix matrix = new Matrix();
Matrix mappingMatrix = new Matrix();
matrix.setScale(scaleFactor, scaleFactor);
// From memory the Matrix for the transform has to be inverted
matrix.invert(mappingMatrix);
// Copy motion event because I seem to remember you cannot change the original motion event
MotionEvent transformEvent = MotionEvent.obtain(ev);
transformEvent.transform(mappingMatrix);
// call parent class so this processes the modified event as normal
boolean returnVal = super.dispatchTouchEvent(transformEvent);
// Tidy up the copied motion event to not leak memory
transformEvent.recycle();
return returnVal;
}