Skip to content

Commit

Permalink
Merge pull request #140 from rubensousa/bump-version
Browse files Browse the repository at this point in the history
Release 1.0.0-beta03
  • Loading branch information
rubensousa authored May 3, 2023
2 parents 5e73f73 + 2e5acad commit 7a88282
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 42 deletions.
31 changes: 31 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@

## Version 1.0.0

### 1.0.0-beta03

2023-05-03

#### Dependency updates

- Updated compose-ui to version `1.4.2`

#### New Features

- Added `getSpanSizeLookup()` to `DpadRecyclerView`
- Added `onViewHolderSelectedAndAligned` to `DpadViewHolder`

#### Compose

- Added `DpadAbstractComposeViewHolder` to allow subclasses to get access to focus changes. Check [Compose interoperability](compose.md) for more information.


#### Testing

See the documentation [here](testing.md)

- Added `KeyEvents.back()` to easily press back key events
- Added `DpadRecyclerViewActions.scrollTo` and `DpadRecyclerViewActions.scrollToHolder` to scroll to specific ViewHolders using KeyEvents.
- Added `DpadViewAssertions` for asserting focus states:
- `DpadViewAssertions.hasFocus()`
- `DpadViewAssertions.doesNotHaveFocus()`
- `DpadViewAssertions.isFocused()`
- `DpadViewAssertions.isNotFocused()`


### 1.0.0-beta02

2023-04-18
Expand Down
124 changes: 91 additions & 33 deletions docs/compose.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
# Compose interoperability

The `dpadrecyclerview-compose` module contains a `DpadComposeViewHolder`
that you can extend to easily render composables in your `RecyclerView`.
The `dpadrecyclerview-compose` module contains the following:

- `DpadAbstractComposeViewHolder`: ViewHolder that exposes a `Content` function to render a Composable
- `DpadComposeViewHolder`: simple implementation of `DpadAbstractComposeViewHolder` that forwards a lambda to the `Content` function and handles clicks

You can use these to easily render composables in your `RecyclerView`.

The focus is kept in the `itemView` and not actually sent to the Composables inside due to these issues:

1. Focus is not sent correctly from Views to Composables: [b/268248352](https://issuetracker.google.com/issues/268248352)
2. Clicking on a focused Composable does not trigger the standard audio feedback: [b/268268856](https://issuetracker.google.com/issues/268268856)

!!! 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)


## DpadComposeViewHolder
Example: `ItemComposable` that should render a text and different colors based on the focus state

```kotlin linenums="1"
@Composable
fun ItemComposable(
item: Int,
isFocused: Boolean,
modifier: Modifier = Modifier,
) {
val backgroundColor = if (isFocused) {
Color.White
} else {
Color.Black
}
val textColor = if (isFocused) {
Color.Black
} else {
Color.White
}
fun ItemComposable(item: Int, isFocused: Boolean) {
val backgroundColor = if (isFocused) Color.White else Color.Black
val textColor = if (isFocused) Color.Black else Color.White
Box(
modifier = modifier.background(backgroundColor),
modifier = Modifier.background(backgroundColor),
contentAlignment = Alignment.Center,
) {
Text(
Expand All @@ -35,9 +38,6 @@ fun ItemComposable(
}
```

To render `ItemComposable` in a `RecyclerView.Adapter`, just use `DpadComposeViewHolder`:


```kotlin linenums="1"
class ComposeItemAdapter(
private val onItemClick: (Int) -> Unit
Expand All @@ -49,25 +49,20 @@ class ComposeItemAdapter(
): DpadComposeViewHolder<Int> {
return DpadComposeViewHolder(
parent,
composable = { item, isFocused, _ ->
ItemComposable(
modifier = Modifier
.width(120.dp)
.aspectRatio(9 / 16f),
item = item,
isFocused = isFocused
)
},
onClick = onItemClick
)
) { item, isFocused, isSelected ->
ItemComposable(item, isFocused)
}
}

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

}

```

New compositions will be triggered whenever the following happens:
Expand All @@ -76,4 +71,67 @@ New compositions will be triggered whenever the following happens:
- Focus state changes
- Selection state changes

## Dpad AbstractComposeViewHolder

Extending from this class directly gives you more flexibility for customizations:

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

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ComposeItemViewHolder {
return ComposeItemViewHolder(parent, onItemClick)
}

override fun onBindViewHolder(
holder: ComposeItemViewHolder,
position: Int
) {
holder.setItemState(getItem(position))
}

override fun onViewRecycled(holder: ComposeItemViewHolder) {
holder.onRecycled()
}
}
```

```kotlin linenums="1"
class ComposeItemViewHolder(
parent: ViewGroup,
onClick: (Int) -> Unit
) : DpadAbstractComposeViewHolder<Int>(parent) {

private val itemAnimator = ItemAnimator(itemView)

init {
itemView.setOnClickListener {
getItem()?.let(onItemClick)
}
}

@Composable
override fun Content(item: Int, isFocused: Boolean, isSelected: Boolean) {
ItemComposable(item, isFocused)
}

override fun onFocusChanged(hasFocus: Boolean) {
if (hasFocus) {
itemAnimator.startFocusGainAnimation()
} else {
itemAnimator.startFocusLossAnimation()
}
}

fun onRecycled() {
itemAnimator.cancel()
}

}
```

Check the sample on [Github](https://github.com/rubensousa/DpadRecyclerView/) for more examples that include simple animations.
24 changes: 19 additions & 5 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ The module `dpadrecyclerview-testing` includes useful Espresso helpers for UI te

Check the [Espresso training guide](https://developer.android.com/training/testing/espresso/basics) if you're not familiar with this testing framework.

The official project sample over at Github also contains example UI tests [here](https://github.com/rubensousa/DpadRecyclerView/blob/master/sample/src/androidTest/java/com/rubensousa/dpadrecyclerview/sample/test/SampleTests.kt)

## Dispatching key events

`KeyEvents` provides some utility methods for easily pressing keys a certain amount of times.

```kotlin
KeyEvents.pressKey(KeyEvent.KEYCODE_DPAD_CENTER)
KeyEvents.click()
KeyEvents.back()
KeyEvents.pressDown(times = 5)
// 50 ms between each key press
KeyEvents.pressUp(times = 5, delay = 50)
Expand All @@ -34,11 +37,14 @@ class UiTest() {
`DpadViewActions` contains the following:

* `getViewBounds`: returns the bounds of a view in the coordinate-space of the root view of the window
* `getRelativeViewBounds`: returns the bounds of a view in the coordinate-space of the parent view
* `clearFocus`: clears the focus of a view if something else can take focus in its place
* `requestFocus`: requests focus of a view

`DpadRecyclerViewActions` contains the following:

* `scrollTo`: scrolls to a specific itemView using KeyEvents
* `scrollToHolder`: scrolls to a specific ViewHolder using KeyEvents
* `selectLastPosition `: selects the last position of the adapter
* `selectPosition `: selects a given position or position-subPosition pair
* `selectSubPosition `: selects a given subPosition for the current selected position
Expand All @@ -50,19 +56,27 @@ class UiTest() {
Example:

```kotlin
Espresso.onView(withId(R.id.recyclerView)).perform(DpadRecyclerViewActions.selectPosition(5))
Espresso.onView(withId(R.id.recyclerView))
.perform(DpadRecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
hasDescendant(withText("Some title"))
))
```

## ViewAssertions

`DpadRecyclerViewAssertions` contains the following:
`DpadRecyclerViewAssertions`:

* `isNotFocused`: checks if the `DpadRecyclerView` is not focused
* `isFocused`: checks if a ViewHolder at a given position is focused
* `isSelected`: checks if a ViewHolder at a given position or position-subPosition pair is selected

`DpadViewAssertions`:

* `isFocused` and `isNotFocused` : checks if a View is focused
* `hasFocus` and `doesNotHaveFocus`: checks if a View or one of its descendants has focus

Example:

```kotlin
Espresso.onView(withId(R.id.recyclerView)).assert(DpadRecyclerViewAssertions.isFocused(position = 5))
Espresso.onView(withId(R.id.recyclerView))
.assert(DpadRecyclerViewAssertions.isFocused(position = 5))
```
2 changes: 1 addition & 1 deletion dpadrecyclerview-compose/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
LIBRARY_VERSION=1.0.0-beta02
LIBRARY_VERSION=1.0.0-beta03
LIBRARY_GROUP=com.rubensousa.dpadrecyclerview
LIBRARY_ARTIFACT=dpadrecyclerview-compose
# POM info
Expand Down
2 changes: 1 addition & 1 deletion dpadrecyclerview-testing/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
LIBRARY_VERSION=1.0.0-beta02
LIBRARY_VERSION=1.0.0-beta03
LIBRARY_GROUP=com.rubensousa.dpadrecyclerview
LIBRARY_ARTIFACT=dpadrecyclerview-testing
# POM info
Expand Down
2 changes: 1 addition & 1 deletion dpadrecyclerview/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
LIBRARY_VERSION=1.0.0-beta02
LIBRARY_VERSION=1.0.0-beta03
LIBRARY_GROUP=com.rubensousa.dpadrecyclerview
LIBRARY_ARTIFACT=dpadrecyclerview
# POM info
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ theme:

extra:
dpadrecyclerview:
version: '1.0.0-beta02'
version: '1.0.0-beta03'
social:
- icon: 'fontawesome/brands/github'
link: 'https://github.com/rubensousa/DpadRecyclerView'
Expand Down

0 comments on commit 7a88282

Please sign in to comment.