Skip to content

Commit

Permalink
Updated documentation for latest version
Browse files Browse the repository at this point in the history
  • Loading branch information
rubensousa committed Mar 17, 2024
1 parent 0e8cbe2 commit 56380cc
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 63 deletions.
32 changes: 16 additions & 16 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Add the following dependency to your app's `build.gradle`:
```groovy
implementation "com.rubensousa.dpadrecyclerview:dpadrecyclerview:$latestVersion"
// Optional: If you want to use Compose together with DpadRecyclerView
// Recommended: To use Compose together with DpadRecyclerView
implementation "com.rubensousa.dpadrecyclerview:dpadrecyclerview-compose:$latestVersion"
// Optional: Espresso test helpers for your instrumented tests:
Expand All @@ -28,6 +28,20 @@ Check the official website for more information and recipes: https://rubensousa.

## New Features compared to Leanback's `BaseGridView`

### Layout

- Supports grids with different span sizes
- Supports infinite/endless scrolling
- Supports reverse layout
- XML attributes for easier configuration

### Scrolling and focus

- Supports changing the alignment configuration smoothly
- Supports limiting the number of pending alignments
- Supports non smooth scroll changes
- Supports continuous and circular grid focus

### Easier Compose Integration

Documentation: https://rubensousa.github.io/DpadRecyclerView/compose/
Expand Down Expand Up @@ -61,20 +75,6 @@ class ComposeItemAdapter(
}
```

### Layout

- Supports grids with different span sizes
- Supports infinite/endless scrolling
- Supports reverse layout
- XML attributes for easier configuration

### Scrolling and focus

- Supports changing the alignment configuration smoothly
- Supports limiting the number of pending alignments
- Supports non smooth scroll changes
- Supports continuous and circular grid focus

## Sample app

Nested lists:
Expand All @@ -90,7 +90,7 @@ Grid with different span sizes:

## License

Copyright 2023 Rúben Sousa
Copyright 2024 Rúben Sousa

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
93 changes: 50 additions & 43 deletions docs/compose.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,42 @@ The `dpadrecyclerview-compose` module contains the following:
- `DpadComposeViewHolder`: ViewHolder that exposes a function to render a Composable but keeps the focus state in the View system
- `RecyclerViewCompositionStrategy.DisposeOnRecycled`: a custom `ViewCompositionStrategy` that only disposes compositions when ViewHolders are recycled

!!! note
If you plan to use compose animations, check the performance during fast scrolling and consider
throttling key events using the APIs explained [here](recipes/scrolling.md#limiting-number-of-pending-alignments)
## Compose ViewHolder

### Receive focus inside Composables

## React to focus changes
Use `DpadComposeFocusViewHolder` to let your Composables receive the focus state.

```kotlin linenums="1"
class ComposeItemAdapter(
private val onItemClick: (Int) -> Unit
) : ListAdapter<Int, DpadComposeFocusViewHolder<Int>>(Item.DIFF_CALLBACK) {

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): DpadComposeFocusViewHolder<Int> {
return DpadComposeFocusViewHolder(parent) { item ->
ItemComposable(
item = item,
onClick = {
onItemClick(item)
}
)
}
}

override fun onBindViewHolder(
holder: DpadComposeFocusViewHolder<Int>,
position: Int
) {
holder.setItemState(getItem(position))
}

}
```

Then use the standard focus APIs to react to focus changes:

```kotlin linenums="1", hl_lines="13-16"
@Composable
Expand Down Expand Up @@ -44,46 +74,9 @@ fun ItemComposable(
}
```

## Handle clicks with sound

Use `Modifier.dpadClickable` instead of `Modifier.clickable` because of this issue:
[/b/268268856](https://issuetracker.google.com/issues/268268856)
### Keep focus inside the view system


## DpadComposeFocusViewHolder

```kotlin linenums="1"
class ComposeItemAdapter(
private val onItemClick: (Int) -> Unit
) : ListAdapter<Int, DpadComposeFocusViewHolder<Int>>(Item.DIFF_CALLBACK) {

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): DpadComposeFocusViewHolder<Int> {
return DpadComposeFocusViewHolder(parent) { item ->
ItemComposable(
item = item,
onClick = {
onItemClick(item)
}
)
}
}

override fun onBindViewHolder(
holder: DpadComposeFocusViewHolder<Int>,
position: Int
) {
holder.setItemState(getItem(position))
}

}
```

## DpadComposeViewHolder

If you need to keep the focus in the View system, use this class instead.
If you want to keep the focus inside the View system, use `DpadComposeViewHolder` instead:

```kotlin linenums="1"
class ComposeItemAdapter(
Expand Down Expand Up @@ -112,6 +105,8 @@ class ComposeItemAdapter(
}
```

In this case, you receive the focus state as an input that you can pass to your Composables:

```kotlin linenums="1"
@Composable
fun ItemComposable(item: Int, isFocused: Boolean) {
Expand All @@ -130,4 +125,16 @@ fun ItemComposable(item: Int, isFocused: Boolean) {
}
```

## Handle clicks with sound

Use `Modifier.dpadClickable` instead of `Modifier.clickable` because of this issue:
[/b/268268856](https://issuetracker.google.com/issues/268268856)

## Performance optimizations

- If you plan to use compose animations, check the performance during fast scrolling and consider throttling key events using the APIs explained [here](recipes/scrolling.md#limiting-number-of-pending-alignments)
- Consider using `dpadRecyclerView.setLayoutWhileScrollingEnabled(false)` to discard layout requests during scroll events.
This will skip unnecessary layout requests triggered by some compose animations.


Check the sample on [Github](https://github.com/rubensousa/DpadRecyclerView/) for more examples that include simple animations.
90 changes: 86 additions & 4 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Add the following dependency to your app's `build.gradle`:
```groovy
implementation "com.rubensousa.dpadrecyclerview:dpadrecyclerview:{{ dpadrecyclerview.version }}"
// Optional: If you want to use Compose together with DpadRecyclerView
// Recommended: To use Compose together with DpadRecyclerView
implementation "com.rubensousa.dpadrecyclerview:dpadrecyclerview-compose:{{ dpadrecyclerview.version }}"
// Optional: Espresso test helpers for your instrumented tests:
Expand All @@ -27,11 +27,93 @@ Since `DpadRecyclerView` is a custom view that extends from `RecyclerView`, you
!!! warning
Don't set a `LayoutManager` because `DpadRecyclerView` already assigns one internally.

Follow the [official RecyclerView guides](https://developer.android.com/develop/ui/views/layout/recyclerview) to render Views on the screen or use any RecyclerView library as you would for mobile apps.
Follow the [official RecyclerView guides](https://developer.android.com/develop/ui/views/layout/recyclerview) to render Views on the screen
or use any RecyclerView library as you would for mobile apps.

## Recipes
You can also render Composables inside using the `dpadrecyclerview-compose` library.

Take a look at the sections inside "Recipes" on this website to customise `DpadRecyclerView` according to your needs.

## Observe selection changes

You can observe selection changes using the following:

```kotlin linenums="1"
recyclerView.addOnViewHolderSelectedListener(object : OnViewHolderSelectedListener {
override fun onViewHolderSelected(
parent: RecyclerView,
child: RecyclerView.ViewHolder?,
position: Int,
subPosition: Int
) {}

override fun onViewHolderSelectedAndAligned(
parent: RecyclerView,
child: RecyclerView.ViewHolder?,
position: Int,
subPosition: Int
) {}
})
```

## Observe focus changes

To react to focus changes, use this:

```kotlin linenums="1"
recyclerView.addOnViewFocusedListener(object : OnViewFocusedListener {
override fun onViewFocused(
parent: RecyclerView.ViewHolder,
child: View,
) {
// Child is now focused
}
})
```

## How to use with Compose

Check [this](compose.md) page to see more some examples with Compose

```kotlin linenums="1", hl_lines="13-16"
@Composable
fun ItemComposable(
item: Int,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
var isFocused by remember { mutableStateOf(false) }
val backgroundColor = if (isFocused) Color.White else Color.Black
val textColor = if (isFocused) Color.Black else Color.White
Box(
modifier = modifier
.background(backgroundColor)
.onFocusChanged { focusState ->
isFocused = focusState.hasFocus
}
.focusTarget()
.dpadClickable {
onClick()
},
contentAlignment = Alignment.Center,
) {
Text(
text = item.toString(),
color = textColor,
fontSize = 35.sp
)
}
}
```

## More customizations

Check the following recipes:

1. [Layout](recipes/layout.md): for defining the type of layout (linear or grid) or to enable infinite carousels
2. [Spacing](recipes/spacing.md): add spacing between items
3. [Alignment](recipes/alignment.md): align items to different regions of the screen
4. [Focus](recipes/focus.md): configure how focus is handled
5. [Scrolling](recipes/scrolling.md): configure the scrolling speed

## Sample

Expand Down
17 changes: 17 additions & 0 deletions docs/recipes/focus.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Focus Recipes

## Observing child focus

Use this to react to a child getting focus:

```kotlin linenums="1"
recyclerView.addOnViewFocusedListener(object : OnViewFocusedListener {
override fun onViewFocused(
parent: RecyclerView.ViewHolder,
child: View,
) {
// Child is now focused
}
})
```
!!! note
If you set this in a vertical RecyclerView that contains multiple horizontal RecyclerViews, the parent will also receive this callback

## Disabling focus changes

You might want to temporarily disable focus changes and prevent other views from being selected
Expand Down

0 comments on commit 56380cc

Please sign in to comment.