-
Notifications
You must be signed in to change notification settings - Fork 726
Carousel
This is intended as a plug and play "Carousel" view - a RecyclerView with horizontal scrolling. It comes with common defaults and performance optimizations and can be either used as a top level RecyclerView, or nested within a vertical RecyclerView.
This class provides:
-
Automatic integration with Epoxy for nested RecyclerView usage
-
Default horizontal padding for carousel peeking
-
Easily control how many items are shown on screen at a time
-
All of the benefits of EpoxyRecyclerView
If you need further flexibility you can subclass this view to change its width, height, scrolling direction, etc. You can annotate a subclass with @ModelView
to generate a new EpoxyModel.
The Carousel
class can be used via xml in an Activity or Fragment like a normal RecyclerView, but a very common use case will be as a nested horizontal list in a vertically scrolling RecyclerView.
Normally nested RecyclerViews are a bit tricky to set up correctly, as you need to coordinate sharing a view pool, recycling views correctly on unbind, setting up "peeking", optimizing for the number of items shown, and so on. Epoxy handles all of this for you.
A CarouselModel_
class is generated from the Carousel
view that you can use in the EpoxyController of your parent, vertical RecyclerView.
// From the EpoxyController of the vertical RecyclerView
void buildModels() {
...
List<PhotoViewModel_> photoModels = new ArrayList();
for (photo in photos) {
photoModels.add(new PhotoViewModel_()
.id(photo.id())
.url(photo.url))
}
new CarouselModel_()
.id("carousel")
.models(photoModels)
.addTo(this);
}
See the sample app for detailed example code of setting up complex nested Carousels.
A common behavior in a carousel is to allow the previous and next items to "peek" from the sides. This indicates to the user that there is more content to scroll to.
We may still want our first item aligned to a certain margin, so a good way to implement this is to add outside padding to the Carousel and set setClipToPadding
to false so that child views are shown through the padding.
Carousel
enables this behavior with default outside padding and setClipToPadding
disabled. You can call setPaddingDp
or setPaddingRes
to specify a custom padding value to use if needed.
This padding is applied to all outside edges, as well as in between items in the carousel via normal EpoxyRecyclerView item spacing
Alternatively you can use the Carousel.Padding
class and setPadding(Padding)
to flexibility control an individual padding amount of each side of the Carousel and in between items.
You can set the number of views to show on screen in a carousel at a time with setNumViewsToShowOnScreen
.
This is useful where you want to easily control for the number of items on screen, regardless of screen size. For example, you could set this to 1.2f so that one view is shown in full and 20% of the next view "peeks" from the edge to indicate that there is more content to scroll to.
Another pattern is setting a different view count depending on whether the device is phone or tablet.
Additionally, if a LinearLayoutManager is used this value will be forwarded to LinearLayoutManager#setInitialPrefetchItemCount
as a performance optimization.
If you want to change the prefetch count without changing the view size you can simply use setInitialPrefetchItemCount(int)
With Kotlin extension functions we can further simplify the model building above to just
fun buildModels() {
...
carousel {
id("carousel")
numViewsToShowOnScreen(5)
withModelsFrom(photos) {
PhotoViewModel_()
.id(it.id)
.url(it.url)
}
}
}
This uses the functions
/** For use in the buildModels method of EpoxyController. A shortcut for creating a Carousel model, initializing it, and adding it to the controller.
*
*/
inline fun EpoxyController.carousel(modelInitializer: CarouselModelBuilder.() -> Unit) {
CarouselModel_().apply {
modelInitializer()
}.addTo(this)
}
/** Add models to a CarouselModel_ by transforming a list of items into EpoxyModels.
*
* @param items The items to transform to models
* @param modelBuilder A function that take an item and returns a new EpoxyModel for that item.
*/
inline fun <T> CarouselModelBuilder.withModelsFrom(
items: List<T>,
modelBuilder: (T) -> EpoxyModel<*>
) {
models(items.map { modelBuilder(it) })
}
Epoxy does not yet package Kotlin extensions, but you can add this to your own project if desired.
By default a LinearSnapHelper
is attached to all Carousel instances. If you would like to change the default snap behavior you can call Carousel.setDefaultGlobalSnapHelperFactory(...)
and pass a factory object to create your snap helper. Null can be passed to disable snapping by default.
You may prefer to use something like this library for a different default snapping behavior, or your own custom implementation.
If you would like to add new behavior to the Carousel you can simply subclass it. For example:
@ModelView(autoLayout = Size.WRAP_WIDTH_MATCH_HEIGHT)
static class VerticalGridCarousel extends Carousel {
public SnappingCarousel(Context context) {
super(context);
}
@Override
protected LayoutManager createLayoutManager() {
return new GridLayoutManager(getContext(), 2, GridLayoutManager.VERTICAL, false);
}
}
By annotating our subclass with ModelView
Epoxy will generate a new EpoxyModel for this view. We have also changed the size here to be a column via the autoLayout
param. Alternatively you could provide a defaultLayout
param to style the view with a layout xml file.
By overriding createLayoutManager
we have changed the layout to a vertical grid.
Any other additional customizations can also be made in this way.