Переопределение onTouchEvent, конкурирующего с ScrollView
Из упрощенного обзора у меня есть пользовательский вид, который содержит некоторые растровые изображения, которые пользователь может перетащить и изменить размер.
То, как я это делаю, довольно стандартно, поскольку я переопределяю onTouchEvent в своем CustomView и проверяю, касается ли пользователь касания внутри изображения и т.д.
Моя проблема возникает, когда я хочу разместить этот CustomView в ScrollView. Это работает, но ScrollView и CustomView, похоже, конкурируют за MotionEvents, т.е. Когда я пытаюсь перетащить изображение, оно либо перемещается вяло, либо свитки просмотра.
Я думаю, мне, возможно, придется расширить ScrollView, чтобы я мог переопределить onInterceptTouchEvent и сообщить ему, находится ли пользователь в пределах изображения, чтобы не прокручивать. Но потому, что ScrollView выше в иерархии, как бы получить доступ к текущему состоянию CustomView?
Есть ли лучший способ?
Ответы
Ответ 1
Обычно Android использует длительное нажатие, чтобы начать перетаскивание в таких случаях, поскольку оно помогает устранить неоднозначность, когда пользователь намеревается перетащить элемент или прокрутить контейнер элемента. Но если у вас есть однозначный сигнал, когда пользователь начинает перетаскивать элемент, попробуйте getParent().requestDisallowInterceptTouchEvent(true)
из пользовательского представления, когда вы знаете, что пользователь начинает перетаскивать. (Документы для этого метода здесь.) Это предотвратит перехват событий ScrollView до конца текущего жеста.
Ответ 2
Ни одно из найденных решений не работает "из коробки" для меня, возможно, потому, что мой пользовательский вид расширяет Просмотр, а не ViewGroup, и поэтому я не могу реализовать onInterceptTouchEvent
.
Также вызов getParent().requestDisallowInterceptTouchEvent(true)
запускал NPE или вообще ничего не делал.
Наконец, вот как я решил проблему:
Внутри пользовательского вызова onTouchEvent
requestDisallow...
, когда ваше представление позаботится о событии. Например:
@Override
public boolean onTouchEvent(MotionEvent event) {
Point pt = new Point( (int)event.getX(), (int)event.getY() );
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (/*this is an interesting event my View will handle*/) {
// here is the fix! now without NPE
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
clicked_on_image = true;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (clicked_on_image) {
//do stuff, drag the image or whatever
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
clicked_on_image = false;
}
return true;
}
Теперь мое настраиваемое представление прекрасно работает, обрабатывает некоторые события и позволяет scrollView улавливать те, которые нам не нужны. Нашел решение здесь: http://android-devblog.blogspot.com.es/2011/01/scrolling-inside-scrollview.html
Надеюсь, что это поможет.
Ответ 3
Существует Android-событие под названием MotionEvent.ACTION_CANCEL (value = 3). Все, что я делаю, переопределяет мой настраиваемый метод onTouchEvent и фиксирует это значение. Если я обнаруживаю это условие, то я отвечаю соответствующим образом.
Вот какой код:
@Override
public boolean onTouchEvent(MotionEvent event) {
if(isTouchable) {
int maskedAction = event.getActionMasked();
if (maskedAction == MotionEvent.ACTION_DOWN) {
this.setTextColor(resources.getColor(R.color.octane_orange));
initialClick = event.getX();
} else if (maskedAction == MotionEvent.ACTION_UP) {
this.setTextColor(defaultTextColor);
endingClick = event.getX();
checkIfSwipeOrClick(initialClick, endingClick, range);
} else if(maskedAction == MotionEvent.ACTION_CANCEL)
this.setTextColor(defaultTextColor);
}
return true;
}