Skip to content

Commit

Permalink
Fix focus searches not checking items in order
Browse files Browse the repository at this point in the history
  • Loading branch information
rubensousa committed Oct 7, 2024
1 parent 280577a commit 8f4051f
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,67 @@ internal class DefaultFocusInterceptor(
position: Int,
direction: Int
): View? {
val absoluteDirection = FocusDirection.getAbsoluteDirection(
val focusAbsoluteDirection = FocusDirection.getAbsoluteDirection(
direction = direction,
isVertical = configuration.isVertical(),
reverseLayout = layoutInfo.shouldReverseLayout()
)
return focusFinder.findNextFocus(recyclerView, focusedView, absoluteDirection)
// Exit early if the focus finder can't find focus already
val nextFocusFinderView = focusFinder.findNextFocus(
recyclerView, focusedView, focusAbsoluteDirection
) ?: return null
val currentViewHolder = layoutInfo.getChildViewHolder(focusedView)
val nextViewHolder = layoutInfo.getChildViewHolder(nextFocusFinderView)
/**
* Check if the focus finder has found another focusable view for the same ViewHolder
* This might happen when sub positions are used
*/
if (nextViewHolder === currentViewHolder && nextFocusFinderView !== focusedView) {
return nextFocusFinderView
}
return if (configuration.spanCount == 1) {
val relativeFocusDirection = FocusDirection.from(
direction, isVertical = configuration.isVertical(),
reverseLayout = configuration.reverseLayout
) ?: return nextFocusFinderView
findNextLinearChild(position, relativeFocusDirection)
} else {
return nextFocusFinderView
}
}

private fun findNextLinearChild(position: Int, direction: FocusDirection): View? {
// We only support the main direction
if (direction.isSecondary()) {
return null
}
val positionIncrement = layoutInfo.getPositionIncrement(
goingForward = direction == FocusDirection.NEXT_ROW
|| direction == FocusDirection.NEXT_COLUMN
)
val nextPosition = position + positionIncrement
// Jump early if we're going out of bounds
if (nextPosition < 0 || nextPosition == layoutInfo.getItemCount()) {
return null
}
return findNextFocusableView(
fromPosition = nextPosition,
positionIncrement = positionIncrement
)
}

private fun findNextFocusableView(
fromPosition: Int,
positionIncrement: Int
): View? {
var currentPosition = fromPosition
while (currentPosition >= 0 && currentPosition < layoutInfo.getItemCount()) {
val view = layoutInfo.findViewByPosition(currentPosition)
if (view != null && layoutInfo.isViewFocusable(view)) {
return view
}
currentPosition += positionIncrement
}
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package com.rubensousa.dpadrecyclerview.sample.ui.screen.dynamic
import android.view.LayoutInflater
import android.view.ViewGroup
import com.rubensousa.dpadrecyclerview.UnboundViewPool
import com.rubensousa.dpadrecyclerview.sample.databinding.AdapterListComposeBinding
import com.rubensousa.dpadrecyclerview.sample.databinding.AdapterListMediumBinding
import com.rubensousa.dpadrecyclerview.sample.ui.model.RecyclerViewItem
import com.rubensousa.dpadrecyclerview.sample.ui.model.ViewHolderDelegate
import com.rubensousa.dpadrecyclerview.state.DpadScrollState
Expand All @@ -32,7 +32,7 @@ class MediumListDelegate(
override fun onCreateViewHolder(parent: ViewGroup): MediumListViewHolder {
return MediumListViewHolder(
viewPool,
AdapterListComposeBinding.inflate(
AdapterListMediumBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ package com.rubensousa.dpadrecyclerview.sample.ui.screen.dynamic

import com.rubensousa.dpadrecyclerview.DpadViewHolder
import com.rubensousa.dpadrecyclerview.UnboundViewPool
import com.rubensousa.dpadrecyclerview.sample.databinding.AdapterListComposeBinding
import com.rubensousa.dpadrecyclerview.sample.databinding.AdapterListMediumBinding
import com.rubensousa.dpadrecyclerview.sample.ui.model.DelegateViewHolder
import com.rubensousa.dpadrecyclerview.sample.ui.widgets.common.ListAnimator

class MediumListViewHolder(
private val recycledViewPool: UnboundViewPool,
private val binding: AdapterListComposeBinding,
private val binding: AdapterListMediumBinding,
) : DelegateViewHolder<MediumList>(binding.root), DpadViewHolder {

val recyclerView = binding.cardRecyclerView
Expand Down
53 changes: 53 additions & 0 deletions sample/src/main/res/layout/adapter_list_medium.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cardListLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:orientation="vertical">

<TextView
android:id="@+id/cardListTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/list_margin_start"
android:textColor="@color/white"
tools:text="Headline" />

<com.rubensousa.dpadrecyclerview.DpadRecyclerView
android:id="@+id/cardRecyclerView"
android:layout_width="match_parent"
android:layout_height="220dp"
android:clipChildren="false"
android:gravity="center"
android:nextFocusDown="@id/mediumButtonAnchor"
android:orientation="horizontal"
app:dpadRecyclerViewChildAlignmentFraction="0.0"
app:dpadRecyclerViewItemEdgeSpacing="@dimen/list_margin_start"
app:dpadRecyclerViewItemSpacing="@dimen/horizontal_item_spacing"
app:dpadRecyclerViewParentAlignmentFraction="0.0"
app:dpadRecyclerViewParentAlignmentOffset="@dimen/list_margin_start" />

<FrameLayout
android:id="@+id/mediumButtonAnchor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginStart="@dimen/list_margin_start"
android:background="@color/list_item_background"
android:focusable="true"
android:focusableInTouchMode="true">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:padding="8dp"
android:text="Sub position"
android:textColor="@color/list_item_text" />

</FrameLayout>

</LinearLayout>

0 comments on commit 8f4051f

Please sign in to comment.