Возникла необходимость сделать Drag'n'Drop с элемента ScrollView.
Причем решить эту задачу без использования посторонних библиотек и для всех устройств, начиная с Froyo.
Сам процесс перетаскивания элемента проблемой не является.
Проблема — отключить скроллирование списка после того, как мы уже с ним не работаем.
Дело в том, что после того как мы вытащили элемент за пределы ScrollView и перемещаем его в нужно место, ScrollView продолжает следить за нашими движениями и продолжает скроллироваться. Это не влияет на функционал, но является недопустимым с точки зрения дизайна интерфейса.
Тема достаточно простая. Я думаю, практически каждый программист способен решить эту задачу самостоятельно.
Но проблема в том, что вместо изобретения велосипеда мы часто лезем в интернет в поисках решения.
И находим. В том числе stackoverflow предлагает нам вполне «работающее» решение.
Не удивлюсь, что кто-то может его применить в своем коде без тщательной проверки…
Решение это выглядит вот так:
dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, 0, 0, 0));
На первый взгляд — адекватное решение. И оно работает! Вот только оно ломает обработку касаний у ScrollView. Результат — падение приложения с переполнением стэка в непредсказуемый момент времени.
Решение выглядит красивым, но использовать его нельзя! И дело здесь не только в том, что события надо освобождать после создания.
Есть решение чуть более замудрённое, но гораздо более безопасное и предсказуемое.
Необходимо расширить функционал ScrollView следующим образом:
public class ScrollViewEx extends ScrollView {
protected boolean scrollActive = true;
public ScrollViewEx(Context context) {
super(context);
}
public ScrollViewEx(Context context, AttributeSet attrs){
super(context,attrs);
}
public ScrollViewEx(Context context, AttributeSet attrs, int defStyle){
super(context,attrs,defStyle);
}
public void stopScroll(){
scrollActive = false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event){
if (event.getAction()==MotionEvent.ACTION_DOWN)
scrollActive = true;
if (scrollActive || event.getAction()!=MotionEvent.ACTION_MOVE)
return super.dispatchTouchEvent(event);
return false;
}
}
Здесь делается очень простая вещь — создаётся переменная, которая отвечает за отключение скролла.
Переменная выставляется программистом в нужный момент и автоматически сбрасывается при нажатии на элемент.
Всё что делает переменная — блокирует виджету обработку события «перемещение касания».
Соответственно, в нужный момент мы вызываем метод stopScroll, после этого список продолжает обрабатывать сообщения о касаниях, но не реагирует на перемещение. Никакого скроллирования не происходит.
Если же пользователь снова нажал на список, то скроллирование включается обратно.
Задача решена. При этом код гораздо более предсказуем и корректен.
Автор: AllexIn