Merge pull request #5607 from abGit9/fix_stuttering_list_2

fix: resolve stuttering when scrolling to the bottom of channel/playlist videos list
This commit is contained in:
Bnyro 2024-02-25 12:56:20 +01:00 committed by GitHub
commit 066e01be51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 298 additions and 267 deletions

View File

@ -59,6 +59,8 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
private var nextPages = Array<String?>(5) { null }
private var searchChannelAdapter: SearchChannelAdapter? = null
private var isAppBarFullyExpanded: Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
channelName = args.channelName
@ -86,14 +88,24 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Check if the AppBarLayout is fully expanded
binding.channelAppBar.addOnOffsetChangedListener { _, verticalOffset ->
isAppBarFullyExpanded = verticalOffset == 0
}
// Determine if the child can scroll up
binding.channelRefresh.setOnChildScrollUpCallback { _, _ ->
!isAppBarFullyExpanded
}
binding.channelRefresh.setOnRefreshListener {
fetchChannel()
}
binding.channelScrollView.viewTreeObserver.addOnScrollChangedListener {
binding.channelRecView.viewTreeObserver.addOnScrollChangedListener {
val binding = _binding ?: return@addOnScrollChangedListener
if (binding.channelScrollView.canScrollVertically(1) || isLoading) return@addOnScrollChangedListener
if (binding.channelRecView.canScrollVertically(1) || isLoading) return@addOnScrollChangedListener
loadNextPage()
}
@ -199,7 +211,8 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
isLoading = false
binding.channelRefresh.isRefreshing = false
binding.channelScrollView.isVisible = true
binding.channelCoordinator.isVisible = true
binding.channelName.text = response.name
if (response.verified) {
binding.channelName

View File

@ -123,7 +123,6 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
}
private fun fetchPlaylist() {
binding.playlistScrollview.isGone = true
lifecycleScope.launch {
val response = try {
withContext(Dispatchers.IO) {
@ -136,12 +135,13 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
val binding = _binding ?: return@launch
playlistFeed = response.relatedStreams.toMutableList()
binding.playlistScrollview.isVisible = true
nextPage = response.nextpage
playlistName = response.name
isLoading = false
ImageHelper.loadImage(response.thumbnailUrl, binding.thumbnail)
binding.playlistProgress.isGone = true
binding.playlistAppBar.isVisible = true
binding.playlistRecView.isVisible = true
binding.playlistName.text = response.name
binding.playlistInfo.text = getChannelAndVideoString(response, response.videos)
@ -312,8 +312,8 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
}
})
binding.playlistScrollview.viewTreeObserver.addOnScrollChangedListener {
if (_binding?.playlistScrollview?.canScrollVertically(1) == false &&
binding.playlistRecView.viewTreeObserver.addOnScrollChangedListener {
if (_binding?.playlistRecView?.canScrollVertically(1) == false &&
!isLoading
) {
// append more playlists to the recycler view

View File

@ -6,165 +6,175 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/channel_scrollView"
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/channel_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="invisible"
tools:context=".ui.fragments.ChannelFragment">
android:visibility="gone">
<LinearLayout
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/channel_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_height="wrap_content">
<ImageView
android:id="@+id/channel_banner"
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/channel_collapsing_tb"
android:layout_width="match_parent"
android:layout_height="80dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="10dp"
android:orientation="horizontal">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/channel_image"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_gravity="center"
app:shapeAppearance="@style/CircleImageView" />
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll"
app:titleCollapseMode="scale">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:orientation="vertical">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_collapseMode="pin">
<TextView
android:id="@+id/channel_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="3.5dp"
android:drawablePadding="3dp"
android:ellipsize="end"
android:maxLines="1"
android:textSize="16sp"
android:textStyle="bold"
tools:text="Channel Name" />
<ImageView
android:id="@+id/channel_banner"
android:layout_width="match_parent"
android:layout_height="80dp" />
<TextView
android:id="@+id/channel_subs"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="10dp"
android:orientation="horizontal">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/channel_image"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_gravity="center"
app:shapeAppearance="@style/CircleImageView" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/channel_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="3.5dp"
android:drawablePadding="3dp"
android:ellipsize="end"
android:maxLines="1"
android:textSize="16sp"
android:textStyle="bold"
tools:text="Channel Name" />
<TextView
android:id="@+id/channel_subs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:maxLines="1"
android:text="@string/app_name"
android:textSize="12sp" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/channel_share"
style="@style/ElevatedIconButton"
android:tooltipText="@string/share"
app:icon="@drawable/ic_share"
tools:targetApi="m" />
<com.google.android.material.button.MaterialButton
android:id="@+id/notification_bell"
style="@style/ElevatedIconButton"
android:tooltipText="@string/notifications"
app:icon="@drawable/ic_notification"
tools:targetApi="m" />
<com.google.android.material.button.MaterialButton
android:id="@+id/channel_subscribe"
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTint="?android:attr/textColorPrimary"
android:stateListAnimator="@null"
android:text="@string/subscribe"
android:textColor="?android:attr/textColorPrimary"
android:textSize="12sp"
app:cornerRadius="20dp"
app:elevation="20dp"
tools:targetApi="m" />
</LinearLayout>
<com.github.libretube.ui.views.ExpandableTextView
android:id="@+id/channel_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:maxLines="1"
android:text="@string/app_name"
android:textSize="12sp" />
android:layout_marginHorizontal="5dp"
android:autoLink="web"
android:padding="10dp" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:scrollbars="none">
<com.google.android.material.chip.ChipGroup
android:id="@+id/tab_chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:checkedChip="@+id/videos"
app:selectionRequired="true"
app:singleLine="true"
app:singleSelection="true">
<com.google.android.material.chip.Chip
android:id="@id/videos"
style="@style/channelChip"
android:text="@string/videos"
android:visibility="visible" />
<com.google.android.material.chip.Chip
android:id="@+id/shorts"
style="@style/channelChip"
android:text="@string/yt_shorts" />
<com.google.android.material.chip.Chip
android:id="@+id/livestreams"
style="@style/channelChip"
android:text="@string/livestreams" />
<com.google.android.material.chip.Chip
android:id="@+id/playlists"
style="@style/channelChip"
android:text="@string/playlists" />
<com.google.android.material.chip.Chip
android:id="@+id/channels"
style="@style/channelChip"
android:text="@string/channels" />
</com.google.android.material.chip.ChipGroup>
</HorizontalScrollView>
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/channel_share"
android:tooltipText="@string/share"
style="@style/ElevatedIconButton"
app:icon="@drawable/ic_share"
tools:targetApi="m" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/notification_bell"
android:tooltipText="@string/notifications"
style="@style/ElevatedIconButton"
app:icon="@drawable/ic_notification"
tools:targetApi="m" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/channel_subscribe"
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTint="?android:attr/textColorPrimary"
android:stateListAnimator="@null"
android:text="@string/subscribe"
android:textColor="?android:attr/textColorPrimary"
android:textSize="12sp"
app:cornerRadius="20dp"
app:elevation="20dp"
tools:targetApi="m" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/channel_recView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</LinearLayout>
<com.github.libretube.ui.views.ExpandableTextView
android:id="@+id/channel_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:autoLink="web"
android:padding="10dp" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:scrollbars="none">
<com.google.android.material.chip.ChipGroup
android:id="@+id/tab_chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:checkedChip="@+id/videos"
app:selectionRequired="true"
app:singleLine="true"
app:singleSelection="true">
<com.google.android.material.chip.Chip
android:id="@id/videos"
style="@style/channelChip"
android:text="@string/videos"
android:visibility="visible" />
<com.google.android.material.chip.Chip
android:id="@+id/shorts"
style="@style/channelChip"
android:text="@string/yt_shorts" />
<com.google.android.material.chip.Chip
android:id="@+id/livestreams"
style="@style/channelChip"
android:text="@string/livestreams" />
<com.google.android.material.chip.Chip
android:id="@+id/playlists"
style="@style/channelChip"
android:text="@string/playlists" />
<com.google.android.material.chip.Chip
android:id="@+id/channels"
style="@style/channelChip"
android:text="@string/channels" />
</com.google.android.material.chip.ChipGroup>
</HorizontalScrollView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/channel_recView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</RelativeLayout>
</LinearLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</com.github.libretube.ui.views.CustomSwipeToRefresh>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
@ -10,148 +10,156 @@
android:id="@+id/playlist_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:visibility="gone" />
android:visibility="gone"
android:layout_gravity="center" />
<ScrollView
android:id="@+id/playlist_scrollview"
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/playlist_recView"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:clipToPadding="false"
android:visibility="gone"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<LinearLayout
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/playlist_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/playlist_collapsing_tb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/thumbnail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.Small"
tools:src="@tools:sample/backgrounds/scenic" />
app:layout_scrollFlags="scroll"
app:titleCollapseMode="scale">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
android:orientation="vertical">
<com.github.libretube.ui.views.ExpandableTextView
android:id="@+id/playlist_name"
android:layout_width="0dp"
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/thumbnail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:paddingHorizontal="5dp"
android:paddingVertical="2dp"
android:textSize="20sp"
android:textStyle="bold" />
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.Small"
tools:src="@tools:sample/backgrounds/scenic" />
<LinearLayout
android:id="@+id/sortContainer"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="8dp"
android:background="@drawable/rounded_ripple"
android:padding="5dp"
android:visibility="gone"
tools:visibility="visible">
android:layout_marginTop="8dp"
android:orientation="horizontal">
<TextView
android:id="@+id/sortTV"
<com.github.libretube.ui.views.ExpandableTextView
android:id="@+id/playlist_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:paddingHorizontal="5dp"
android:paddingVertical="2dp"
android:textSize="20sp"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/sortContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
tools:text="@string/most_recent" />
android:layout_gravity="center"
android:layout_marginEnd="8dp"
android:background="@drawable/rounded_ripple"
android:padding="5dp"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/sortTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
tools:text="@string/most_recent" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:src="@drawable/ic_sort" />
</LinearLayout>
<ImageView
android:id="@+id/optionsMenu"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:src="@drawable/ic_sort" />
android:layout_marginEnd="20dp"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_three_dots" />
</LinearLayout>
<ImageView
android:id="@+id/optionsMenu"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:layout_marginEnd="20dp"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_three_dots" />
</LinearLayout>
<TextView
android:id="@+id/playlistInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="15dp"
android:paddingBottom="5dp"
android:textStyle="bold" />
<com.github.libretube.ui.views.ExpandableTextView
android:id="@+id/playlistDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:autoLink="web"
android:paddingHorizontal="10dp"
android:paddingTop="5dp"
android:paddingBottom="10dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="10dp"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/play_all"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:maxLines="1"
android:text="@string/play_all"
app:icon="@drawable/ic_playlist" />
<com.google.android.material.button.MaterialButton
android:id="@+id/bookmark"
style="@style/Widget.Material3.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:maxLines="1"
android:text="@string/bookmark"
app:icon="@drawable/ic_bookmark_outlined" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/playlist_recView"
<TextView
android:id="@+id/playlistInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:nestedScrollingEnabled="false" />
</RelativeLayout>
android:paddingHorizontal="15dp"
android:paddingBottom="5dp"
android:textStyle="bold" />
</LinearLayout>
<com.github.libretube.ui.views.ExpandableTextView
android:id="@+id/playlistDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:autoLink="web"
android:paddingHorizontal="10dp"
android:paddingTop="5dp"
android:paddingBottom="10dp" />
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="10dp"
android:orientation="horizontal">
</RelativeLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/play_all"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:maxLines="1"
android:text="@string/play_all"
app:icon="@drawable/ic_playlist" />
<com.google.android.material.button.MaterialButton
android:id="@+id/bookmark"
style="@style/Widget.Material3.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:maxLines="1"
android:text="@string/bookmark"
app:icon="@drawable/ic_bookmark_outlined" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>