【问题标题】:Custom view ... overrides onTouchEvent but not performClick自定义视图 ... 覆盖 onTouchEvent 但不覆盖 performClick
【发布时间】:2015-02-12 06:36:35
【问题描述】:

我在正在开发的自定义 Android 视图中收到此警告(来自问题标题)。

为什么我会收到警告?它背后的逻辑是什么,即为什么它很好
练习在覆盖onTouchEvent 时也覆盖performClick

【问题讨论】:

标签: java android android-view android-custom-view


【解决方案1】:

目的是什么?

在其他一些答案中,您可以看到使警告消失的方法,但重要的是要首先了解系统为什么要您覆盖 performClick()

世界上有数以百万计的盲人。也许你通常不会想太多,但你应该。他们也使用安卓。 “如何?”你可能会问。一种重要的方式是通过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 就可以在用户感觉到自定义视图时读出自定义视图是什么。

CustomView.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();
    }
}

注意事项

  • documentation 还使用了一个mDownTouch 变量,该变量似乎用于过滤掉额外的修饰事件,但由于它没有得到很好的解释或对我们的应用程序来说不是绝对必要的,因此我将其省略了。如果你制作一个真正的导弹发射器应用程序,我建议你多研究一下。
  • 发射导弹的主要方法 (launchMissile()) 刚刚从 performClick() 调用。如果您在onTouchEvent 中也有它,请注意不要调用它两次。您需要根据自定义视图的具体情况准确决定如何以及何时调用业务逻辑方法。
  • 不要覆盖performClick(),然后什么也不做,只是为了摆脱警告。如果你想忽视世界上数以百万计的盲人,那么你可以压制警告。至少这样你才能诚实地表达自己的无情。

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) { ... }
    

进一步研究

【讨论】:

  • 但如果您使用以下命令也会出现警告: SwitchCompat.setOnTouchListener(this);在这种情况下,我不能覆盖 performClick,或者我怎么能?
  • @David,很抱歉,我没有这方面的经验。不过,如果您找到了答案,请留下另一条评论。
  • 读者提问:你说,“发射导弹的主要方法(launchMissile())刚刚从performClick()调用。注意不要调用两次,如果你也在@987654343 @。”我不确定我是否明白你的意思 - 你能帮我理解你所说的“如果你在 onTouchEvent 中也有它,不要调用它两次”的意思。你还指的是哪里?另外,您只能从onTouchEvent() 内部调用performClick(),对吗?或者,你的意思是说,在onTouchEvent() 中,既然你可以得到ACTION_UPACTION_DOWN,不要同时调用它?
  • @Reader,系统调用performClick() 获取可访问性事件。在上面的代码中,我为ACTION_UP 事件调用performClick()。这意味着系统和我都可以调用performClick(),从而导致两次调用。我想记得在子类化按钮或其他东西时遇到过这种情况。无论如何,如果您测试您的自定义视图,您应该能够知道performClick() 被调用了多少次。
  • 我喜欢这个答案,也喜欢讲道。感谢您在让世界更容易访问方面发挥作用
【解决方案2】:

此警告告诉您覆盖 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;
 }

但这不是强制性的。因为我创建了一个自定义的旋钮视图,它在我也面临这个警告的地方工作得很好。

【讨论】:

  • 关键是您只有注释“处理自定义单击此处的操作”的部分。我应该在那里处理什么?我认为没有什么有用的。我不想写无用的代码只是为了关闭 Lint。
【解决方案3】:

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
}

有关通过代码访问触摸事件的更多信息,请访问trigger ontouch event programmatically

【讨论】:

    猜你喜欢
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多