На Хабре не раз встречались статьи про слайдинг экранов под Android ViewPager, ViewPagerIndicator. Я хочу предложить вариант слайдинга в четырех направлениях.
Не так давно у меня возникла необходимость сделать слайдинг экрана в четрех направлениях: влево, вправо, вверх и вниз. Поиски по интернету не дали положительного результата, или может я плохо искал. Так что в итоге я решил сделать то, что мне нужно сам.
Надо сказать, что начал я не с нуля, за основу была положена реализация слайдинга по горизонтали отсюда RealViewSwitcher.
Итак, вся реализация размещается в одном классе TwoDirectionsViewSwitcher, который наследуется от ViewGroup. Для реализации необходимого функционала, достаточно переопределить следующие методы базового класса:
В методе onMeasure задаются размеры для будущих экранов:
В onLayout производится компоновка экранов и собственно разделение на строки (количество строк передается в конструкторе):
Сам конструктор в который передается контекст приложения и количество строк, на которые необходимо разбить общий список экранов:
Следующий, ключевой, метод обработки прикосновений к экрану onTouchEvent:
И последний, метод расчета скролла дочерних экранов computeScroll:
Теперь несколько подробнее остановимся на некоторых ключевых методах класса. Метод onLayout.
Тут все просто, реализуется вложенный цикл по количеству строк и задаются смещения для каждого последующего экрана.
Более сложный метод — это обработчик прикосновений к экрану onTouchEvent.
Здесь он мало отличается от типовой реализации, обрабатываются различные типы действий:
В обработчике соытия MotionEvent.ACTION_DOW определяется факт начала движения по экрану, задаются текущие положения по X и Y нажатия экрана и задается признак начала скроллинга.
В обработчике MotionEvent.ACTION_MOVE обрабатывается смещение по эрану (случай, когда смещение превысило возвращаемое методом ViewConfiguration.get(getContext()).getScaledTouchSlop() значение), вычисляется значение скролла по X и Y и определяется направление движения по абсолютному значению смещения по осям:
Кроме того для ортогонального перемещения по осям введены три переменные isXMove, isYMove, isMoveBegin. Последняя из которых принимает значение true при обработке MotionEvent.ACTION_DOWN.
Дальше собственно идет скроллинг экрана с использованием нескольких утилитных методов.
Обработчик MotionEvent.ACTION_UP отвечает за завершение прокрутки экрана и обнуление рабочих переменных. Если перемещение экрана произошло больше чем на половину, происходит пролистывание на следующий экран, в противном случае старый экран возвращается в свое начальное положение.
Еще хотелось бы остановиться на моменте, когда нужно переместиться не на первый экран, а на произвольный, при создании экземпляра класса. Для этого создан конструктор:
В методе onMeasure при первом открытии обрабатывается заданная изначально позиция:
Конечно, данный класс не претендует на завершенность, в нем, например, стоило бы реализовать обработку изменения расположения экрана (сейчас он корректно работает в режиме Landscape), добавить эффектов, учитывать кратность общего числа экранов и количества строк, но даже в таком виде он выполняет те функции, для которых я его собственно и разрабатывал — перемещение в четырех направлениях.
Полный код проекта с примером использования опубликован на github TwoDirectionsViewSwitcher.
Не так давно у меня возникла необходимость сделать слайдинг экрана в четрех направлениях: влево, вправо, вверх и вниз. Поиски по интернету не дали положительного результата, или может я плохо искал. Так что в итоге я решил сделать то, что мне нужно сам.
Надо сказать, что начал я не с нуля, за основу была положена реализация слайдинга по горизонтали отсюда RealViewSwitcher.
Итак, вся реализация размещается в одном классе TwoDirectionsViewSwitcher, который наследуется от ViewGroup. Для реализации необходимого функционала, достаточно переопределить следующие методы базового класса:
В методе onMeasure задаются размеры для будущих экранов:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
В onLayout производится компоновка экранов и собственно разделение на строки (количество строк передается в конструкторе):
protected void onLayout(boolean changed, int l, int t, int r, int b)
Сам конструктор в который передается контекст приложения и количество строк, на которые необходимо разбить общий список экранов:
public TwoDirectionsViewSwitcher(Context context, int rows)
Следующий, ключевой, метод обработки прикосновений к экрану onTouchEvent:
public boolean onTouchEvent(MotionEvent ev)
И последний, метод расчета скролла дочерних экранов computeScroll:
public void computeScroll()
Теперь несколько подробнее остановимся на некоторых ключевых методах класса. Метод onLayout.
Тут все просто, реализуется вложенный цикл по количеству строк и задаются смещения для каждого последующего экрана.
Более сложный метод — это обработчик прикосновений к экрану onTouchEvent.
Здесь он мало отличается от типовой реализации, обрабатываются различные типы действий:
- MotionEvent.ACTION_DOWN — прикосновение к экрану;
- MotionEvent.ACTION_MOVE — движение по экрану после прикосновения;
- MotionEvent.ACTION_UP — окончание движение (поднятие пальца с экрана)
В обработчике соытия MotionEvent.ACTION_DOW определяется факт начала движения по экрану, задаются текущие положения по X и Y нажатия экрана и задается признак начала скроллинга.
В обработчике MotionEvent.ACTION_MOVE обрабатывается смещение по эрану (случай, когда смещение превысило возвращаемое методом ViewConfiguration.get(getContext()).getScaledTouchSlop() значение), вычисляется значение скролла по X и Y и определяется направление движения по абсолютному значению смещения по осям:
// приращение по X
final int deltaX = (int) (mLastMotionX - x);
// приращение по Y
final int deltaY = (int) (mLastMotionY - y);
mLastMotionX = x;
mLastMotionY = y;
// смещение View относительно экрана по осям
final int scrollX = getScrollX();
final int scrollY = getScrollY();
// определение направления перемещения, по горизонтали или вертикали
if (Math.abs(deltaX) > Math.abs(deltaY) && isMoveBegin) {
isMoveBegin = false;
isXMove = true;
isYMove = false;
} else if (Math.abs(deltaX) < Math.abs(deltaY) && isMoveBegin) {
isMoveBegin = false;
isXMove = false;
isYMove = true;
}
// обработка движения по горизонтали
if (isXMove) {
if (deltaX < 0) {
if (scrollX > 0) {
scrollBy(Math.max(-scrollX, deltaX), 0);
}
} else if (deltaX > 0) {
final int availableToScroll = getChildAt(getChildCount() / mRows -1).getRight() - scrollX - getWidth();
if (availableToScroll > 0) {
scrollBy(Math.min(availableToScroll, deltaX), 0);
}
}
}
// обработка движения по вертикали
else if (isYMove) {
if (deltaY < 0) {
if (scrollY > 0) {
scrollBy(0, Math.max(-scrollY, deltaY));
}
} else if (deltaY > 0) {
final int availableToScroll = getChildAt(getChildCount() - 1).getBottom() - scrollY - getHeight();
if (availableToScroll > 0) {
scrollBy(0, Math.min(availableToScroll, deltaY));
}
}
}
}
break;
Кроме того для ортогонального перемещения по осям введены три переменные isXMove, isYMove, isMoveBegin. Последняя из которых принимает значение true при обработке MotionEvent.ACTION_DOWN.
Дальше собственно идет скроллинг экрана с использованием нескольких утилитных методов.
Обработчик MotionEvent.ACTION_UP отвечает за завершение прокрутки экрана и обнуление рабочих переменных. Если перемещение экрана произошло больше чем на половину, происходит пролистывание на следующий экран, в противном случае старый экран возвращается в свое начальное положение.
Еще хотелось бы остановиться на моменте, когда нужно переместиться не на первый экран, а на произвольный, при создании экземпляра класса. Для этого создан конструктор:
public TwoDirectionsViewSwitcher(Context context, int rows, int currentScreen) {
super(context);
mCurrentScreen = currentScreen;
mRows = rows;
init();
}
В методе onMeasure при первом открытии обрабатывается заданная изначально позиция:
if (mFirstLayout) {
int row = mCurrentScreen / (getChildCount() / mRows);
int cell = mCurrentScreen % (getChildCount() / mRows);
scrollTo(cell * width, row * height);
mFirstLayout = false;
}
Конечно, данный класс не претендует на завершенность, в нем, например, стоило бы реализовать обработку изменения расположения экрана (сейчас он корректно работает в режиме Landscape), добавить эффектов, учитывать кратность общего числа экранов и количества строк, но даже в таком виде он выполняет те функции, для которых я его собственно и разрабатывал — перемещение в четырех направлениях.
Полный код проекта с примером использования опубликован на github TwoDirectionsViewSwitcher.