Polet

A solution to compose RecyclerView Decoration

The idea is to compose the decoration by reusing small and simple components.

Polet is itself an ItemDecoration as it extends the RecyclerView.ItemDecoration class. But an instance of Polet is an empty
shell that you fill by providing it informations that will compose the decoration.

Those elements, all optional, are:

  • One SizingDecoration which will define the final size of the item
  • One or multiple DrawingDecoration which will define how you fill the space available
  • One or multiple DecorationSelector which will define which items will receive the decoration

In isolation, those items are simple and easy to write and understand. But combined (or composed), they can form very complex
decoration with complex selective conditions.

Implementation

A very basic decoration which adds space around every item:

val spacing = resources.getDimensionPixelSize(R.dimen.spacing_default)
addItemDecoration(Polet().apply {
    sizingDecoration = SimpleSpacingDecoration(spacing)
})

image

A more complex decoration with a background, a line under each element and another line one the side.

addItemDecoration(Polet().apply {
    sizingDecoration = SimpleSpacingDecoration(defaultSpacing)
    drawingDecorations.add(PaintingDecoration(primaryColor))
    drawingDecorations.add(
        LineDrawingDecoration(accentColor, LineDrawingDecoration.Position.right,defaultSpacing)
    )
    drawingDecorations.add(
        LineDrawingDecoration(black,LineDrawingDecoration.Position.bottom, lineHeight)
    )
})

image

Composing with Decoration Selector

The real power comes when you combine the drawing composition with the selection composition.

Going back to the previous exemple with the simple spacing decoration, imagine that you want to add an extra spacing over the very first item.
The idea to do so, is to add another item decoration which will be a single space and will only apply to the first element.

For that, we can use the PositionningSelector and add it to the selectiveDecoration list. The PositionningSelector lets you include or exclude certain positions in your RecyclerView. Here we only include the First position.

val spacing = resources.getDimensionPixelSize(R.dimen.spacing_default)
addItemDecoration(Polet().apply {
    sizingDecoration = SimpleSpacingDecoration(spacing)
})

addItemDecoration(Polet().apply {
    sizingDecoration = SimpleSpacingDecoration(t = spacing * 3)
    selectiveDecoration.add(PositionningSelector().apply {
        excludeAll()
        includeFirst = true
    })
})

image

Another example could be to have a certain decoration if the data matches a certain condition and another decoration if the condition is not met.
For that, we can use the DataConditionSelector which takes a function as constructor parameter. This function recieves the adapter and the current position and must return a boolean indicating if the condition is matched.

Here the RecyclerView displays a list of random Ints between 0 and 9 and we need to apply a color if the number is between 0 and 5.

   addItemDecoration(Polet().apply {
       sizingDecoration = SimpleSpacingDecoration(defaultSpacing)
       drawingDecorations.add(PaintingDecoration(accentColor))
       selectiveDecoration.add(DataConditionSelector { adapter, position ->
           return@DataConditionSelector (adapter as RandomNumberAdapter?)?.list?.get(position) ?: 0 > 5
       })
   })
   addItemDecoration(Polet().apply {
       sizingDecoration = SimpleSpacingDecoration(defaultSpacing)
       drawingDecorations.add(PaintingDecoration(primaryColor))
       selectiveDecoration.add(DataConditionSelector { adapter, position ->
           return@DataConditionSelector (adapter as RandomNumberAdapter?)?.list?.get(position) ?: 0 <= 5
       })
   })

image

Content

SizingDecoration

  • SimpleSpacingDecoration -> Adds space around the item. Dimention needs to be provided in pixels, not resource id.

DrawingDecoration

  • PaintingDecoration -> Fills alls the space available with the provided color. Color needs to be provided as a resolved RGB color, not resource id.
  • LineDrawingDecoration -> Draws a line of the specified width and color at the specified location (top, bottom, left or right)

DecorationSelector

  • ViewTypeSelector -> Applies the decoration to the selected ViewTypes
  • SkippingSelector -> Applies decoration, then skips the required number of views and repeat
  • PositioningSelector -> Applies decoration to the selected positions: first, last, inner, firstInSameViewTypeGroup, lastInSameViewTypeGroup, innerInSameViewTypeGroup
  • FlexiblePositioninSelector -> Provides the same selection as the PositioningSelector but with more options to handle position conflicts. See class documentation for details.
  • DataConditionSelector -> Applies decoration if the data at the current position matches the provided condition.

This content is volontarily reduced to keep the library small and still cover many use cases. If you browse throught the code, you will realize that the drawing and selection classes are very simple. So if anything is missing, it should be quite easy to create your own DrawingDecoration or DecorationSelector

Install

The library is available using Jitpack

Step 1. Add the JitPack repository to your build file
Add it in your root build.gradle at the end of repositories:

allprojects {
	repositories {
		...
		maven { url 'https://jitpack.io' }
	}
}

Step 2. Add the dependency

dependencies {
        implementation 'com.github.fievx:Polet:<version>'
}

GitHub