Кнопка "Press and hold" на Android требует изменения состояний (настраиваемый селектор XML) с помощью onTouchListener

У меня есть кнопка, которая должна иметь функциональность "нажимать и удерживать", поэтому вместо использования onClickListener я использую onTouchListener, чтобы приложение могло реагировать на

 MotionEvent.ACTION_DOWN,

и

 MotionEvent.ACTION_UP

В зависимости от того, как быстро срабатывают эти два события, я могу запустить "pressAndHoldHandler" за время между ними.

В любом случае, длинный рассказ: у меня есть множество "основных" кнопок в одном приложении, которые не требуют функции пресса и удержания, поэтому они используют onClickListener.

Каждая из этих кнопок настраивается графически со своим собственным файлом селектора XML:

<?xml version="1.0" encoding="UTF-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:state_enabled="false"
        android:drawable="@drawable/btn_chicken_off" />

    <item
        android:state_enabled="true"
        android:state_pressed="true"
        android:drawable="@drawable/btn_chicken_s3" />

    <item
        android:state_enabled="true"
        android:state_focused="true"
        android:drawable="@drawable/btn_chicken_s2" />

    <item
        android:state_enabled="true"
        android:drawable="@drawable/btn_chicken_off" />

</selector>

Итак, проблема здесь: вышеприведенный селектор не получает доступ с помощью onTouchListener. Только onClickListener будет вносить изменения состояния с помощью секции onClick() своего собственного метода, поэтому эти кнопки "нажимать и удерживать" никогда не меняют состояние. Довольно страшная обратная связь для пользователя.

В настоящее время я заставляю вышеперечисленное в случае переключения ACTION_DOWN и ACTION_UP делать следующее:

if (action == MotionEvent.ACTION_DOWN) {
    btn_chicken.setBackgroundResource(R.drawable.btn_chicken_s3);
}
else
    if (action == MotionEvent.ACTION_UP) {
        btn_chicken.setBackgroundResource(R.drawable.btn_chicken_off);
    }

Но это похоже на хак, и ему не хватает "сфокусированного, но не нажатого" этапа.

Кто-нибудь споткнулся об этом раньше?

Ответ 1

Используйте функцию view.setPressed(), чтобы самостоятельно имитировать нажатое поведение.

Вероятно, вы захотите включить состояние Пресса, когда вы получите событие ACTION_DOWN и отключите его, когда вы получите событие ACTION_UP.

Кроме того, будет хорошей идеей отключить его, если пользователь выйдет из кнопки. Захват события ACTION_OUTSIDE, как показано в следующем примере:

@Override
public boolean onTouch(View v, MotionEvent event) {

    switch (event.getAction() & MotionEvent.ACTION_MASK) {

    case MotionEvent.ACTION_DOWN:
        v.setPressed(true);
        // Start action ...
        break;
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_OUTSIDE:
    case MotionEvent.ACTION_CANCEL:
        v.setPressed(false);
        // Stop action ...
        break;
    case MotionEvent.ACTION_POINTER_DOWN:
        break;
    case MotionEvent.ACTION_POINTER_UP:
        break;
    case MotionEvent.ACTION_MOVE:
        break;
    }

    return true;
}

Ответ 2

Обязательно верните false в конце функции onTouchListener().:)

Ответ 3

Вы можете просто установить кнопку onClickListener и оставить свой метод onClick пустым. Ваша логика реализована внутри onTouch. Таким образом, вы получите эффект нажатия.

P.S Вам не нужно все это состояние в селекторе, который вы можете просто использовать:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:state_pressed="true" android:drawable="@drawable/IMAGE_PRESSED" />

    <item android:drawable="@drawable/IMAGE" />

</selector>

Ответ 4

Я понял это! Просто используйте view.setSelected() следующим образом:

@Override
public boolean onTouch(View v, MotionEvent event) {
    if(event.getAction() == MotionEvent.ACTION_DOWN){
        yourView.setSelected(true);
        ...
        return true;
    }
    else if(event.getAction() == MotionEvent.ACTION_UP){
        yourView.setSelected(false);
        ...
        return true;
    }
    else 
        return false;
}

Это сделает ваш селектор работать, даже если ваш просмотр имеет прослушиватель onTouch:)

Ответ 5

Лучшее решение:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_activated="true">
        <bitmap android:src="@drawable/image_selected"/>
    </item>
    <item>
        <bitmap android:src="@drawable/image_not_selected"/>
    </item>
</selector>

@Override
public void onClick(View v) {
    if (v.isActivated())
       v.setActivated(false);
    else
       v.setActivated(true);
}