Останавливаем скроллирование ScrollView

в 12:32, , рубрики: Разработка под android

Возникла необходимость сделать 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js