我在我正在开发的自定义 Android 视图中收到此警告(来自问题标题)。
为什么我会收到警告?它背后的逻辑是什么,即为什么它是好的
练习在覆盖
performClick
时也覆盖 onTouchEvent
?
在其他一些答案中,您可以看到使警告消失的方法,但重要的是要了解系统为什么希望您首先覆盖
performClick()
。
世界上有数百万盲人。也许你通常不会想太多,但你应该考虑。他们也使用Android。 “如何?”你可能会问。一种重要的方法是通过TalkBack应用程序。它是一个提供音频反馈的屏幕阅读器。您可以通过转至设置 > 辅助功能 > TalkBack 在手机中将其打开。浏览那里的教程。这真的很有趣。现在尝试闭上眼睛使用您的应用程序。您可能会发现您的应用程序充其量是非常烦人,最坏的情况是完全损坏。这对你来说是一个失败,任何有视力障碍的人都会快速卸载。
观看 Google 制作的这段精彩视频,了解如何让您的应用程序易于访问。
performClick()
让我们看一个示例自定义视图,了解覆盖
performClick()
的实际工作原理。我们将制作一个简单的导弹发射应用程序。自定义视图将是触发它的按钮。
启用 TalkBack 后听起来好多了,但动画 gif 不允许音频,所以你只能自己尝试一下。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<net.example.customviewaccessibility.CustomView
android:layout_width="200dp"
android:layout_height="200dp"
android:contentDescription="Activate missile launch"
android:layout_centerInParent="true"
/>
</RelativeLayout>
请注意,我设置了
contentDescription
。这允许 TalkBack 在用户感觉到自定义视图时读出它是什么。
自定义视图.java
public class CustomView extends View {
private final static int NORMAL_COLOR = Color.BLUE;
private final static int PRESSED_COLOR = Color.RED;
public CustomView(Context context) {
super(context);
init();
}
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setBackgroundColor(NORMAL_COLOR);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setBackgroundColor(PRESSED_COLOR);
return true;
case MotionEvent.ACTION_UP:
setBackgroundColor(NORMAL_COLOR);
// For this particular app we want the main work to happen
// on ACTION_UP rather than ACTION_DOWN. So this is where
// we will call performClick().
performClick();
return true;
}
return false;
}
// Because we call this from onTouchEvent, this code will be executed for both
// normal touch events and for when the system calls this using Accessibility
@Override
public boolean performClick() {
super.performClick();
launchMissile();
return true;
}
private void launchMissile() {
Toast.makeText(getContext(), "Missile launched", Toast.LENGTH_SHORT).show();
}
}
注释
mDownTouch
变量,该变量似乎用于过滤掉额外的修饰事件,但由于它没有得到很好的解释或对于我们的应用程序来说是严格必需的,因此我将其省略。如果你制作一个真正的导弹发射器应用程序,我建议你更多地研究这个。
launchMissile()
)的主要方法只是从
performClick()
调用。如果
onTouchEvent
中也有它,请小心不要调用它两次。您需要根据自定义视图的具体情况准确决定如何以及何时调用业务逻辑方法。
performClick()
,然后不做任何事情只是为了消除警告。如果你想忽略世界上数以百万计的盲人,那么你可以压制警告。至少这样你才能诚实地面对自己的无情。
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) { ... }
performClick
@Override
public boolean performClick() {
// Calls the super implementation, which generates an AccessibilityEvent
// and calls the onClick() listener on the view, if any
super.performClick();
// Handle the action for the custom click here
return true;
}
但这不是强制性的。因为我创建了一个自定义旋钮视图,并且它工作得很好,但我也面临着这个警告。
onTouchEvent
,如单击警告详细信息中的“更多...”链接所述。它建议您覆盖
performClick
以执行所需的操作,或者至少将其与您的
onTouchEvent
一起覆盖。如果您的代码更适合触摸事件,您可以使用类似以下内容:
@Override
public boolean performClick() {
if (actionNotAlreadyExecuted) {
MotionEvent myEvent = MotionEvent.obtain(long downTime, long eventTime, int action, float x, float y, int metaState);
onTouch(myView, myEvent);
}
return true; // register it has been handled
}
有关通过代码访问触摸事件的更多信息,请参阅
ImageView 或 AppCompatImageView 的人(不知道它是否也适用于其他视图),您应该注意,即使您在 onTouchEvent() 内部处理它,系统也会 also 触发performClick()并返回 true。这将导致从 onTouchEvent() 中的代码以及系统中调用 PerformClick:
强制其正常工作的唯一方法是强制将视图设置为不可点击和不可聚焦:
setClickable(false);
setFocusable(false);
仅供参考:
private void init() {
// To prevent getting double clicks we need to disable focusable and clickable
setClickable(false);
setFocusable(false);
}
@Override
public boolean onTouchEvent(final MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPressed = true;
invalidate();
return true;
case MotionEvent.ACTION_UP:
if (mPressed) {
invalidate();
performClick();
}
return true;
}
return false;
}
@Override
public boolean performClick() {
super.performClick();
return true;
}