Пользовательское представление... отменяет onTouchEvent, но не выполняетClick

Я получаю это предупреждение (из названия вопроса) в пользовательском представлении Android, которое я разрабатываю.

Почему меня предупреждают? Какая логика в этом заключается, почему это хорошо? практика также отменяет performClick при переопределении onTouchEvent?

Ответы

Ответ 1

Какова цель?

В некоторых других ответах вы можете увидеть способы, как убрать предупреждение, но важно понять, почему система в первую очередь хочет, чтобы вы переопределяли performClick().

В мире миллионы слепых людей. Может быть, вы обычно не думаете о них много, но вы должны. Они тоже используют Android. "Как?" Вы можете спросить. Одним из важных способов является использование приложения TalkBack. Это программа чтения с экрана, которая дает звуковую обратную связь. Вы можете включить его в своем телефоне, зайдя в Настройки> Специальные возможности> TalkBack. Пройдите учебник там. Это действительно интересно. Теперь попробуйте использовать ваше приложение с закрытыми глазами. Вы, вероятно, обнаружите, что в лучшем случае ваше приложение чрезвычайно раздражает, а в худшем - полностью сломано. Это неудача для вас и быстрая деинсталляция для всех, кто имеет проблемы со зрением.

Посмотрите это отличное видео от Google, чтобы узнать, как сделать ваше приложение доступным.

Как переопределить performClick()

Давайте посмотрим на пример пользовательского представления, чтобы увидеть, как на самом деле работает переопределение performClick(). Мы сделаем простое приложение для запуска ракет. Пользовательский вид будет кнопкой для его запуска.

enter image description here

Звучит намного лучше с включенным TalkBack, но анимированные гифки не позволяют аудио, так что вам просто нужно попробовать это самостоятельно.

Код

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

Заметки

  • В документации также используется переменная mDownTouch которая, по-видимому, используется для фильтрации дополнительных событий подкраски, но, поскольку она недостаточно хорошо объяснена или строго необходима для нашего приложения, я не учел ее. Если вы создадите настоящее приложение для запуска ракет, я советую вам больше в этом разобраться.
  • Основной метод, запускающий ракету (launchMissile()), просто performClick() из performClick(). Будьте осторожны, чтобы не вызывать его дважды, если он также есть в onTouchEvent. Вам нужно будет решить, как и когда вызывать метод бизнес-логики, в зависимости от особенностей вашего пользовательского представления.
  • Не переопределяйте performClick() а затем ничего не делайте с ним, просто чтобы избавиться от предупреждения. Если вы хотите игнорировать миллионы слепых людей в мире, вы можете подавить предупреждение. По крайней мере, так вы честны в своей бессердечности.

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

Дальнейшее обучение

Ответ 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;
 }

Но это не обязательно. Поскольку я создал пользовательский knobView, и он работает довольно хорошо, где я также сталкиваюсь с этим предупреждением.

Ответ 3

Хотя это всего лишь предупреждение и его можно игнорировать, оно, похоже, необходимо для обеспечения доступности.

Подробности описаны здесь

Затем, когда вы управляете действием, вы должны добавить executeClick, то есть:

    if (action == MotionEvent.ACTION_DOWN) {
        performClick(); // Call this method to handle the response, and
        // thereby enable accessibility services to
        // perform this action for a user who cannot
        // click the touchscreen.

Ответ 4

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
}

Более подробную информацию о доступе к сенсорным событиям с помощью кода можно получить в событии касания триггера программно