Closed fankloano closed 5 months ago
Which views are you marking as focusable? If it's the root view of your ViewHolder, focus should persist there. A sample here would also help
I have following recyclerview:
<com.rubensousa.dpadrecyclerview.DpadRecyclerView
android:id="@+id/rv_layout_TvChannels"
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintStart_toEndOf="@id/linLayout_tv_categories"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_percent="0.50"
tools:listitem="@layout/rv_item_tvchannels"/>
and the root view of the rv_item_tvchannels holds the focus:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cardView_tvchannel"
android:layout_width="match_parent"
android:layout_height="80dp"
android:focusable="true"
android:focusableInTouchMode="true"
android:descendantFocusability="afterDescendants"
android:background="@drawable/channel_item_background"
xmlns:app="http://schemas.android.com/apk/res-auto">
other views....
So in the adapter to handle key events etc. I am using always cardView_tvchannel
You shouldn't use android:descendantFocusability="afterDescendants
if the focus should stay in that view. Please remove that or replace it with beforeDescendants
You can also figure out where the focus is being held with the developer option "Show layout bounds". This should show you a cross on the view that's receiving focus. You can also use these APIs to see if they solve your problem: https://rubensousa.github.io/DpadRecyclerView/api/dpadrecyclerview/com.rubensousa.dpadrecyclerview/-dpad-recycler-view/set-focus-out-allowed.html
Changing android:descendantFocusability="afterDescendants
to beforeDescendants
or removing it didn't help. Using the focus out allowed apis neither (I was already using them):
private fun prepareTvChannelsRecyclerView() {
tvChannelsAdapter = TvChannelsAdapter(onChannelClickListener, onChannelLongClickListener,this, helpViewModel)
binding.rvLayoutTvChannels.apply {
adapter = tvChannelsAdapter
addItemDecoration(
DpadLinearSpacingDecoration.create(
itemSpacing = 6,
edgeSpacing = 6,
perpendicularEdgeSpacing = 6
)
)
setFocusOutAllowed(false, false)
setFocusOutSideAllowed(false, false)
}
}
The only thing that helped was setting: setSmoothFocusChangesEnabled(false), with this the issue is gone. Why could it work with set to false?
I think I know where the focus goes (when loosing the focus, navigating up or down changes the channels, as I would be in the tv_categories_recyclerview):
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/const_tv"
android:background="?attr/backgroundLight"
android:descendantFocusability="afterDescendants"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:id="@+id/linLayout_tv_categories"
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="?attr/backgroundLight"
app:layout_constraintStart_toEndOf="@id/linLayout_tvCountries_menu"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.20">
<TextView
android:id="@+id/tv_focusedCountry"
android:layout_width="match_parent"
android:layout_height="36dp"
android:gravity="center_vertical"
android:paddingStart="18dp"
android:paddingEnd="10dp"
android:layout_margin="1dp"
android:background="?attr/backgroundMain"
android:textColor="?attr/textMainColor"
android:textSize="12sp"/>
<com.rubensousa.dpadrecyclerview.DpadRecyclerView
android:id="@+id/rv_layout_tv_categories"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#34373C"
android:orientation="vertical"
android:layout_marginStart="2dp"
android:layout_margin="1dp"
tools:listitem="@layout/rv_item_tvcategory"
app:layout_constraintStart_toEndOf="@id/rv_layout_tvCountries_menu"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.20"/>
</LinearLayout>
<LinearLayout
android:id="@+id/linLayout_tvCountries_menu"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/rv_item_activated_countries"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:orientation="vertical"
app:layout_constraintWidth_percent="0.20">
<View
android:layout_width="match_parent"
android:layout_height="36dp"
android:layout_margin="1dp"
android:background="?attr/backgroundMain"/>
<com.rubensousa.dpadrecyclerview.DpadRecyclerView
android:id="@+id/rv_layout_tvCountries_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/backgroundMain"
android:orientation="vertical"
android:layout_margin="1dp"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/rv_item_activated_countries"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_percent="0.20"/>
</LinearLayout>
<com.rubensousa.dpadrecyclerview.DpadRecyclerView
android:id="@+id/rv_layout_TvChannels"
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintStart_toEndOf="@id/linLayout_tv_categories"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_percent="0.50"
tools:listitem="@layout/rv_item_tvchannels"/>
If this still didn't work, I would appreciate a drawing with the problematic focus states please. Show me where the focus is held when you fast scroll. setSmoothFocusChangesEnabled(false)
will trigger layout requests for each position change, which is more expensive. Maybe you could modify the sample project on this repo to reproduce this issue so I can have a look at your fork
@rubensousa I made a video of loosing the focus, or what do you mean by drawing? https://github.com/rubensousa/DpadRecyclerView/assets/83220273/fc627d25-17f7-4f9e-a9f5-5fddbebefd94
So I have to fork the sample project, modify it in android studio and update the fork with it, is this correct? (sorry, never done this before :-) )
Looks like you're updating the adapter while fast scrolling. Are you using DiffUtil
to apply diffing or requesting focus somewhere else? Please enable the "Show layout bounds" and then upload a new recording.
If you can, please fork the project and create a sample inside it that reproduces the issue.
Yes i am using DiffUtil in the adapter:
companion object {
private val MANAGE_TVCHANNELS_COMPERATOR = object : DiffUtil.ItemCallback<TvChannelsWithEpgData>() {
override fun areItemsTheSame(oldItem: TvChannelsWithEpgData, newItem: TvChannelsWithEpgData) =
oldItem.tvChannel.channelId == newItem.tvChannel.channelId
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: TvChannelsWithEpgData, newItem: TvChannelsWithEpgData) =
oldItem.epgDataList == newItem.epgDataList &&
oldItem.tvChannel.showingName == newItem.tvChannel.showingName &&
oldItem.tvChannel.epgChannelId == newItem.tvChannel.epgChannelId &&
}
}
and notifyitemchanged in the fragment to update specific channels (I just removed the notifyitemchanged parts, but it didn't solve the problem). Following the video with enabled Show layout bounds..
https://github.com/rubensousa/DpadRecyclerView/assets/83220273/4c4a4c43-35e2-48f8-b5d4-a7237677e873
ps. For forking the project etc. I need some time, as I never did that before and I a have only short time on PC the rest of this + most of the next week (easter, family, kids..)
@rubensousa Just to let you know, I've been sick in bed for a few days (not better yet) . If you're still waiting for the fork, it may take a while, sorry...
@rubensousa Sorry, but it seems that I am to stupid to fork and modify.. Do I have to add also things like RetrofitInstance etc.? So everything I need or use in my app?? Or would there be an easier way, so that you can investigate the issue? (eventually step by step what happens in the issue-part of my app?)
Thanks, Alex
@fankloano No, you don't. Just create a similar project in github that reproduces the problem so I can investigate. You don't need to copy your real app
Same here, I think this happens because of lazy updating of the list that changes positions in the list, and when I disable smooth focus change, it doesn't happen anymore
@homayoonahmadi can you share a sample that reproduces this problem?
@fankloano @homayoonahmadi Can you please try this: setLayoutWhileScrollingEnabled(false)
and let me know if it solves the issue?
@rubensousa Hello, tried with: private fun prepareTvChannelsRecyclerView() { tvChannelsAdapter = TvChannelsAdapter(onChannelClickListener, onChannelLongClickListener) binding.rvLayoutTvChannels.apply { adapter = tvChannelsAdapter addItemDecoration( DpadLinearSpacingDecoration.create( itemSpacing = 6, edgeSpacing = 6, perpendicularEdgeSpacing = 6 ) ) setLayoutWhileScrollingEnabled(false) setFocusOutAllowed(false, false) setFocusOutSideAllowed(false, false) } } Unfortunately, this did not solve the problem. setSmoothFocusChangesEnabled(false) is still what seems to solve the problem.
@fankloano Can you please create a project in github using this library that reproduces that problem? Otherwise it's really hard for me to help
@fankloano Fix attempt in 1.3.0-alpha03
will be released. Please try it out
@fankloano Released in 1.3.0-alpha03
now. Please try it out and let me know
I removed setSmoothFocusChangeEnabled(false):
private fun prepareTvChannelsRecyclerView() {
tvChannelsAdapter = TvChannelsAdapter(onChannelClickListener, onChannelLongClickListener,this, helpViewModel)
binding.rvLayoutTvChannels.apply {
adapter = tvChannelsAdapter
addItemDecoration(
DpadLinearSpacingDecoration.create(
itemSpacing = 6,
edgeSpacing = 6,
perpendicularEdgeSpacing = 6
)
)
setFocusOutAllowed(false, false)
setFocusOutSideAllowed(false, false)
}
}
AAAAAND.... everything seems to work now as it should :-) [At least in my short test with the Android Studio emulator]. BIG THANKS What caused the problem?
DpadRecyclerView
disables the focus search automatically during animations, which are triggered when you change the contents: https://rubensousa.github.io/DpadRecyclerView/api/dpadrecyclerview/com.rubensousa.dpadrecyclerview/-dpad-recycler-view/set-focus-search-enabled-during-animations.html
This is done on purpose because on TVs the experience of focus would be interrupted. There was a bug with that implementation and I solved it in the last release. I think if you used setFocusSearchEnabledDuringAnimations(true)
in the last release you should've been fine too
Hello again,
another issue I had some time ago: Having a vertical recyclerview with an imageview inside the item (loading the image from an url to the imageview), when I was scrolling fast I always lost the focus (mostly when I was getting to the bottom or top of the list).
I was able to solve it using: setSmoothFocusChangesEnabled(false)
So I don't know if it's really a bug (maybe it has also to do with the image libraries I tested [Glide & Picasso]), but eventually you want to have a look at it. (for me it's not mandatory, as there is a work-around).