Кнопка "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);
}