- Scrolling Behavior for Appbars in Android
- Basic Setup
- Standard App bar scrolling with only Toolbar
- App bar scrolling with tabs
- App bar scrolling with Flexible space
- App bar scrolling with image in Flexible space
- App bar scrolling with overlapping content in Flexible space
- Fast Scrolling with RecyclerView
- Как создать кнопку прокрутки наверх с помощью RecyclerView
- 2 ответа
- На SDK надейся и сам не плошай: Проблема вложенных скроллов в BottomSheetBehavior
- Первая встреча
- Пробуем отлавливать события
- Ищем перехватчика
- BottomSheetBehavior
- Исправление
- Java Reflection API
- Копипаст
- Заключение
Scrolling Behavior for Appbars in Android
Jan 1, 2017 · 5 min read
App bars contains four main aspects, that plays huge role in scrolling behavior. They are,
AppBar scrolling behavior enriches the way contents in a page presented.
I am going to share my experience about how easily we can understand and use the elevation in scrolling, sizing the flexible spaces, how to anchor specific elements.
App Bar has following scrolling options,
- Standard App bar scrolling with only Toolbar
- App bar scrolling with tabs
- App bar scrolling with Flexible space
- App bar scrolling with image in Flexible space
- App bar scrolling with overlapping content in Flexible space
If you wish to jump into the code directly, Here is the Github repository link.
Basic Setup
Before we start jumping in and see all types of scrolling behavior, we needs to be clear about the basic setup and implementation.
Us e design support library to achieve AppBar scrolling behavior. This library provides many of the material design components.
In app build.gradle,
Extend android.support.v7.app.AppCompatActivity in the Activity class.
In the layout xml, we need to have CoordinatorLayout in the top. Add Toolbar inside AppBarLayout and the AppBarLayout needs to be inside the CoordinatorLayout. CoordinatorLayout is the one, which gives proper scrolling and material animations to the views attached with it like FloatingButtons, ModalSheets and SnackBar.
That’s it. We have done with the basic implementation and after this, there are some flags that will decide the scrolling behavior.
Standard App bar scrolling with only Toolbar
- scroll off-screen with the content and returns when the user reverse scrolls.
- stay fixed at the top with content scrolling under it.
To achieve this, apart from the above basic setup code implementation:
The Toolbar needs to have app:layout_scrollFlags
scroll -will be scrolled along with the content.
enterAlways -when content is pulled down, immediately app bar will appear.
snap -when the AppBar is half scrolled and content scrolling stopped, this will allow the AppBar to settle either hidden or appear based on the scrolled size of Toolbar.
Once app:layout_scrollFlags added to Toolbar, the content view (Either a NestedScrollView or RecyclerView) needs to have app:layout_behavior tag.
That’s it these two tags along with basic setup is enough to achieve the Standard AppBar with Toolbar scrolling behavior. We can get different behaviors by playing with app:layout_scrollFlags.
Here is clear explanation from Android docs for the flags,
App bar scrolling with tabs
- TabBar stays anchored at the top, while the Toolbar scrolls off.
- Whole AppBar stays anchored at the top, with the content scrolling underneath.
- Both the toolbar and tab bar scroll off with content. The TabBar returns on reverse-scroll, and the Toolbar returns on complete reverse scroll.
To achieve this, we need to add TabLayout inside the AppBarLayout and provide the layout_scrollFlags inside TabLayout. That will be enough to achieve this and we can play around with the scrolling behavior like above examples by just altering the layout_scrollFlags.
App bar scrolling with Flexible space
- The flexible space shrinks until only the toolbar remains. The title shrinks to 20sp in the navigation bar. When scrolling to the top of the page, the flexible space and the title grow into place again.
- The whole app bar scrolls off. When the user reverse scrolls, the toolbar returns anchored to the top. When scrolling all the way back, the flexible space and the title grow into place again.
To get Flexible space for AppBar, we need to use CollapsingToolbarLayout around the ToolBar tag. Which means CoordinatorLayout in the top and AppBarLayout, CollapsingToolbarLayout, ToolbarLayout inside the order.
We need to add height for the AppBarLayout and need to specify app:layout_scrollFlags for CollapsingToolbarLayout.
Also we need to add app:layout_collapseMode=”pin” tag in Toolbar.
exitUntilCollapsed -flag will make the Flexible space scrolled down while scrolling back to position along with the content.
App bar scrolling with image in Flexible space
- Similar to the above Flexible space behavior. When scrolling image will pushed up with slight animation and the color changes to primary color.
- While reversing the scrolling primary color fades away to leave way for the image been pulled down with a slight animation.
It is very much similar to the Flexible Space implementation with the below changes,
- ImageView needs to added inside CollapsingToolbarlayout.
- AppBarLayout height specified 200dp will be applied to image.
App bar scrolling with overlapping content in Flexible space
- In this scrolling, the AppBar with Flexible space will be placed behind the content. Once content starts scrolling, the app bar will scroll faster than the content until it gets out of the overlapping content view. Once the content reaches top, app bar comes upside of the content and content goes underneath and scrolls smoothly.
- The whole AppBar can scroll off-screen along with content and can be returned while reverse scrolling.
- There will not be any TabBar placement in this behavior.
This can be achieved by using app:behaviour_overlapTop in the NestedScrollView or RecyclerView. Also in this case we are specifying height value for CollapsingToolbarLayout .
Also we can implement and specify the scrollFlags dynamically through java code.
Hopefully this article will help you to implement scrolling behaviors for AppBar.
I posted this article originally on my blog.
Code for demo app is available on Github.
If you like the article, follow me on Medium and Twitter. You can also find me on LinkedIn.
Источник
Fast Scrolling with RecyclerView
Aug 26, 2017 · 3 min read
Continuing with Support Library 26 features (if you missed the one with downloadable fonts), a much awaited feature was enabled : fast scrolling for RecyclerView.
Of all the great advantages RecyclerView has over ListView, one feature that I miss a lot is fast scrolling where you could drag a thumb drawable and scroll around the list. In ListView you could do the following :
But with RecyclerView, there is no easy way to have a thumb drawable like so :
So we would rely on libraries like this and this.
With Suppo r t Library 26, we can easily enable fast scrolling for RecyclerView. Let’s get to it!
Let’s make sure we’re getting the Support Library 26. The app build.gradle file will look something like :
Since Support Library 26 has now been moved to Google’s maven repository, let’s include that in our project level build.gradle
This is what our layout file for the activity looks like :
I have setup a simple RecyclerView filled with mock data that shows US states and their state code. It looks something like :
Now let’s enable the fast scrolling. The updated xml file looks like :
Let’s go over each property one by one :
- fastScrollEnabled : boolean value to enable the fast scrolling. Setting this as true will require that we provide the following four properties.
- fastScrollHorizontalThumbDrawable : A StateListDrawable that will be used to draw the thumb which will be draggable across the horizontal axis.
- fastScrollHorizontalTrackDrawable : A StateListDrawable that will be used to draw the line that will represent the scrollbar on horizontal axis.
- fastScrollVerticalThumbDrawable : A StateListDrawable that will be used to draw the thumb which will be draggable on vertical axis.
- fastScrollVerticalTrackDrawable : A StateListDrawable that will be used to draw the line that will represent the scrollbar on vertical axis.
Let’s look at the StateListDrawables. I’ve used native shapes so that you can easily reuse them.
Источник
Как создать кнопку прокрутки наверх с помощью RecyclerView
У меня есть приложение, которое отображает изображения с помощью RecyclerView. Я хочу показать пользователю кнопку для возврата вверх при прокрутке вниз
Примерно так
так как я могу это сделать
Вот мои коды
Примечание. Я использую библиотеки androidx .
2 ответа
Использование RelativeLayout является обязательным требованием? Это кажется простым с FrameLayout или ConstraintLayout и FloatingActionButton :
RecyclerView имеет несколько вспомогательных методов и интерфейсов для прокрутки, проверьте документы для всех них. Вы ищете scrollToPosition
Вы даже можете сделать кнопку видимой только после того, как пользователь прокрутит вверх, и скрыть ее в противном случае, с помощью onScrollListener. Слушатель срабатывает каждый раз, когда в представлении регистрируется изменение прокрутки:
Вы можете добавить FloatingActionButton в RelativeLayout в качестве родственного элемента к RecyclerView , настроить его положение по верху и центру родителя.
Затем измените его backgroundTint на свой серый цвет (работает с API-21), ниже API-21, он принимает colorAccent по умолчанию.
Добавьте ic_baseline_arrow_upward_24.xml из ресурсов Android в папку для рисования и используйте его в атрибуте android:src FAB.
Для прокрутки к вершине RecyclerView используйте scrollToPosition() или smoothScrollToPosition()
Источник
На SDK надейся и сам не плошай: Проблема вложенных скроллов в BottomSheetBehavior
Наверное, каждый, любуясь красивыми и магически плавно съезжающими в разные стороны окошками, тулбарами и остальными вьюхами, задумывался как это работает, наверное, даже что-то читал про CoordianatorLayout, про различные Behaivor, которые позволяют создавать буквально волшебство на андроидовских вьюхах. Конечно, можно писать кастомные вьюхи, с нужным поведением, которое может быть ограничено только твоим воображением или твоими знаниями в Android разработке. Но помимо этого, есть ещё одно ограничение — время, не будешь же ты писать кастомную вьюху на хакатоне, в горящем по срокам проекте, а написанные заранее решения с кастомными вьюхами могут быть освоены не за самый короткий срок членами команды. Именно тогда приходит самое простое и самое логичное решение проблемы — не выпендриваться, использовать стандартные инструменты Android, там же смышлённые ребята сидят, всё будет в шоколаде (ну или в других сладостях андроида).
Но не всё так просто, тут и начинается моё близкое знакомство с магией таких ребят, как CoordinatorLayout, BottomSheetBehavior, а точнее с багом, который выпустили из виду разработчики, когда их писали. В статье будет описан процесс выявления бага, связанного с вложенной прокруткой внутри view-компонентов с поведением BottomSheetBehavior, а также способы его решения.
Первая встреча
Передо мной стояла задача сделать несложный функционал с вложенной прокруткой, состоящей из горизонтального RecyclerView, ScrollView и CoordinatorLayout, именно он позволяет, ориентируясь на Behavior-ы вложенных вьюх, вытворять такие фигуры высшего пилотажа, как плавные произвольные сдвиги различных вью.
Разложим по полочкам: делаем корневой CoordinatorLayout, внутри которого, находится RecyclerView, с прописанным поведением из библиотеки android.material BottomSheetBehavior, что позволит нашему ресайклеру, выезжать снизу корневого вью или обратно туда сворачиваться. Внутри item-ов каждого, находится TextView — заголовок item-а, и NestedScrollView, с различным наполнением (допустим тот же TextView).
Почему NestedScrollView? Потому что он, согласно официальной документации Android, выполняет ту же функцию, что ScrollView, но помимо этого поддерживает вложенную прокрутку, как со стороны родительской, так и со стороны дочерней вьюхи, по умолчанию.
Накидали вьюхи на layout, написали своих наследников RecyclerView.Adapter и RecyclerView.ViewHolder, сделали модели данных, заглушки, кажется всё готово.
Итак, камера, мотор, и… Всё работает? Шторка выезжает и сворачивается, NestedScrollView крутится, в чём подвох? Перелистываем recycler на другой item, пробуем скроллить… Не работает… Прокрутим ещё на несколько item-ов дальше, снова работает, и так продолжается с точной периодичностью в несколько item-ов. Кто знаком с идеей работы RecyclerView, уже знает в чём дело — дело в грязных View, которые Recycler переиспользует, чтобы экономить ресурсы смартфона. Но это не совсем то, из-за чего может не скроллится NestedScrollView, начинаем расследование.
Пробуем отлавливать события
Первое что, пришло мне в голову — посмотреть какие события приходят в рабочем item-е и в нерабочем. Попробуем отлавливать события onScrollChange:
В первом item всё ок, а вот в остальных тишина… Тогда пробуем, кое-что поинтереснее, а именно перехватывать касания:
Картина в логах поинтереснее, мы видим, что события касаний логируются, как в рабочем, так и в нерабочем примере, однако если повнимательнее посмотреть, то можно увидеть, что в рабочем примере событий больше, чем в нерабочем, следовательно можно придти к выводу, что кто-то или что-то перехватывает наши события, которые так нужны, чтобы сделать скролл.
Ищем перехватчика
Как мы знаем, все события поступают не сразу в дочернюю вьюху, а сначала проходят через все родительские. Что от нас требуется? Найти ту вьюху, в которой появляются пропадающие в NestedScrollView события OnTouch. Пробуем слушать события в корневой вьюхе item-а:
Без изменений. Идём выше, пробуем перехватить в самом recycler, картина не меняется, однозначно приходят не все события. Проверяем самого главного парня: CoordinatorLayout, и тут есть пробитие — события приходящие из работающего item-а, такие же как из неработающего.
Но как известно, сам по себе CoordinatorLayout, мало чего умеет, он может только отлавливать поведение дочерних компонентов, нет поведения — CoordinatorLayout, грубо говоря, равен FrameLayout. И тут мы вспоминаем, что как раз назначили RecyclerView поведение из библиотеки material — BottomSheetBehavior. Следовало быть, он тот, кого мы и искали.
BottomSheetBehavior
Что из себя представляет BottomSheetBehavior в двух словах — java класс, наследуемый от CoordinatorLayout.Behavior, предоставляющий инструкции по поведению дочерней вьюхи внутри родительской (CoordinatorLayout). Чтобы разобраться, что и как вызывается, было бы неплохо залогировать все методы этого класса. Как это сделать? Наследуем от BottomSheetBehavior собственный класс TestBehavior, переопределяем, все методы, оставляя внутри логи и вызовы методов супер-класса. Подставляем в RecyclerView новое поведение.
Запускаем и смотрим логи. В рабочем item-е приходят следующие события: onInterceptTouchEvent, onStartNestedScroll, onNestedPreScroll, onNestedScroll, onStopNestedScroll. В нерабочем: onInterceptTouchEvent, onStartNestedScroll, onNestedPreScroll, onStopNestedScroll. Судя по всему, именно в методе onNestedPreScroll происходит заглушение событий. Переходим к методу onNestedPreScroll в классе BottomSheetBehavior и ставим точку остановки, запускаем отладчик, и видим, что условие при котором происходит выход из метода:
Итак, что за переменная nestedScrollingChildRef? Слабая ссылка на дочерний View, соответственно в условии осуществляется проверка на соответствие View, которым управляет CoordinatorLayout и View, для которого поступило событие.
Смотрим инициализацию и понимаем, что объект инициализируется только при единственном вызове метода onLayoutChild:
И тут вспоминаем про Recycler, про то, что при каждом пролистывании обновляется просматриваемый item, следственно отображается другое View, а ссылка внутри BottomSheetBehavior, все ещё ссылается на первую вьюху, именно поэтому, когда первая View переиспользуется, то скролл происходит, когда используется другая — нет. Причина найдена, пробуем исправить проблему.
Исправление
Что нужно, чтобы всё начало работать? Нужно как-то обновлять ссылку на текущую view в Recycler-е, например сделать метод, который будет обновлять значение ссылки, но так как BottomSheetBehavior это класс, который недоступен для редактирования и поле является приватным, у нас есть два выбора: написать собственную реализацию этого класса (например: скопировав полностью класс, добавив свой метод) или использовать Java Reflection API. Оба варианта выглядят неочень, но выкручиваться надо.
Java Reflection API
Решение с Java Reflection API, через того же наследника TestBeahvior, получаем приватное поле в Runtime и меняем его значение, единственное, что надо учесть, менять поле лучше всего в методе onStartNestedScroll, перед проверкой в onNestedPreScroll.
Копипаст
Тут всё просто, копируем класс BottomSheetBehavior, дописываем private метод refreshNestedScrollingChildRef, который будет обновлять содержимое объекта ссылки, вызываем этот метод в методе onStartNestedScroll. Готово, всё работает.
Заключение
Разработчики системы Android предоставляют множество классных и хорошо работающих инструментов, однако это не значит, что все они будут работать идеально. Всегда можно найти кейс при котором, стандартный инструмент не будет работать, как и в моём случае.
Источник