From 6a4ceb3c50c43671b1e594eff1f1aaae2f12a2e9 Mon Sep 17 00:00:00 2001 From: David Carver Date: Tue, 30 Oct 2018 21:17:39 -0400 Subject: [PATCH] Creates a Focusable Layout Managers and Smooth Scrollers This creates two layout managers and new smooth scroller that will set the Focus after scrolling has completed. This gets around the hack of using a handler to set focus after a delay. No delay is need here. --- .../EpisodeVideoGalleryFragment.java | 8 +++++-- .../fragments/MovieVideoGalleryFragment.java | 3 ++- .../serenity/fragments/VideoGridFragment.java | 3 ++- .../ui/activity/SerenityActivity.java | 12 ---------- .../ui/browser/tv/OnKeyDownDelegate.java | 15 ++---------- .../ui/browser/tv/TVShowBrowserActivity.java | 8 ++++--- .../seasons/TVShowSeasonBrowserActivity.java | 9 ++++--- .../FocusableGridLayoutManager.kt | 17 +++++++++++++ .../FocusableLinearLayoutManager.kt | 14 +++++++++++ .../FocusableLinearSmoothScroller.kt | 24 +++++++++++++++++++ 10 files changed, 78 insertions(+), 35 deletions(-) create mode 100644 serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableGridLayoutManager.kt create mode 100644 serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableLinearLayoutManager.kt create mode 100644 serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableLinearSmoothScroller.kt diff --git a/serenity-app/src/main/java/us/nineworlds/serenity/fragments/EpisodeVideoGalleryFragment.java b/serenity-app/src/main/java/us/nineworlds/serenity/fragments/EpisodeVideoGalleryFragment.java index 13dc97788..5ed932175 100644 --- a/serenity-app/src/main/java/us/nineworlds/serenity/fragments/EpisodeVideoGalleryFragment.java +++ b/serenity-app/src/main/java/us/nineworlds/serenity/fragments/EpisodeVideoGalleryFragment.java @@ -43,6 +43,7 @@ import us.nineworlds.serenity.ui.browser.tv.episodes.EpisodePosterOnItemSelectedListener; import us.nineworlds.serenity.ui.browser.tv.seasons.EpisodePosterOnItemClickListener; import us.nineworlds.serenity.ui.listeners.GalleryVideoOnItemLongClickListener; +import us.nineworlds.serenity.ui.recyclerview.FocusableLinearLayoutManager; public class EpisodeVideoGalleryFragment extends InjectingFragment { @@ -74,8 +75,11 @@ public EpisodeVideoGalleryFragment() { videoGallery.setAdapter(adapter); videoGallery.setItemAnimator(new FadeInAnimator()); videoGallery.setHorizontalFadingEdgeEnabled(false); - videoGallery.setLayoutManager( - new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false)); + + FocusableLinearLayoutManager linearLayoutManager = new FocusableLinearLayoutManager(getActivity()); + linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); + videoGallery.setLayoutManager(linearLayoutManager); + videoGallery.addItemDecoration(new SpaceItemDecoration( getResources().getDimensionPixelOffset(R.dimen.horizontal_spacing))); videoGallery.setClipToPadding(false); diff --git a/serenity-app/src/main/java/us/nineworlds/serenity/fragments/MovieVideoGalleryFragment.java b/serenity-app/src/main/java/us/nineworlds/serenity/fragments/MovieVideoGalleryFragment.java index 6d46a525a..09c08ec79 100644 --- a/serenity-app/src/main/java/us/nineworlds/serenity/fragments/MovieVideoGalleryFragment.java +++ b/serenity-app/src/main/java/us/nineworlds/serenity/fragments/MovieVideoGalleryFragment.java @@ -51,6 +51,7 @@ import us.nineworlds.serenity.ui.listeners.AbstractVideoOnItemSelectedListener; import us.nineworlds.serenity.ui.listeners.GalleryVideoOnItemClickListener; import us.nineworlds.serenity.ui.listeners.GalleryVideoOnItemLongClickListener; +import us.nineworlds.serenity.ui.recyclerview.FocusableLinearLayoutManager; import static android.view.View.*; import static butterknife.ButterKnife.bind; @@ -135,7 +136,7 @@ private AbstractVideoOnItemLongClickListener createOnItemLongClickListener(Movie } protected LinearLayoutManager createLayoutManager() { - LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity()); + LinearLayoutManager linearLayoutManager = new FocusableLinearLayoutManager(getActivity()); linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); return linearLayoutManager; } diff --git a/serenity-app/src/main/java/us/nineworlds/serenity/fragments/VideoGridFragment.java b/serenity-app/src/main/java/us/nineworlds/serenity/fragments/VideoGridFragment.java index 8e8785dd1..993fb02dc 100644 --- a/serenity-app/src/main/java/us/nineworlds/serenity/fragments/VideoGridFragment.java +++ b/serenity-app/src/main/java/us/nineworlds/serenity/fragments/VideoGridFragment.java @@ -37,6 +37,7 @@ import us.nineworlds.serenity.ui.browser.movie.MoviePosterImageAdapter; import us.nineworlds.serenity.ui.browser.movie.MoviePosterOnItemSelectedListener; import us.nineworlds.serenity.ui.listeners.AbstractVideoOnItemSelectedListener; +import us.nineworlds.serenity.ui.recyclerview.FocusableGridLayoutManager; import us.nineworlds.serenity.widgets.SerenityMenuGridLayoutManager; import static butterknife.ButterKnife.bind; @@ -66,7 +67,7 @@ public VideoGridFragment() { protected LinearLayoutManager createLayoutManager() { GridLayoutManager layoutManager = - new GridLayoutManager(getActivity(), 3, + new FocusableGridLayoutManager(getActivity(), 3, GridLayoutManager.HORIZONTAL, false); return layoutManager; } diff --git a/serenity-app/src/main/java/us/nineworlds/serenity/ui/activity/SerenityActivity.java b/serenity-app/src/main/java/us/nineworlds/serenity/ui/activity/SerenityActivity.java index 5f2d9d2e0..9976faee1 100644 --- a/serenity-app/src/main/java/us/nineworlds/serenity/ui/activity/SerenityActivity.java +++ b/serenity-app/src/main/java/us/nineworlds/serenity/ui/activity/SerenityActivity.java @@ -68,12 +68,6 @@ public abstract class SerenityActivity extends InjectingMvpActivity { } gallery.smoothScrollToPosition(newPosition); Timber.d("New ItemPosition: " + newPosition); - - scrollingHandler.postDelayed(() -> { - LinearLayoutManager linearLayout = (LinearLayoutManager) gallery.getLayoutManager(); - View viewByPosition = linearLayout.findViewByPosition(linearLayout.findFirstVisibleItemPosition()); - viewByPosition.requestFocusFromTouch(); - }, 200); return true; } @@ -84,12 +78,6 @@ public abstract class SerenityActivity extends InjectingMvpActivity { } gallery.smoothScrollToPosition(newPosition); Timber.d("New ItemPosition: " + newPosition); - scrollingHandler.postDelayed(() -> { - LinearLayoutManager linearLayout = (LinearLayoutManager) gallery.getLayoutManager(); - View viewByPosition = linearLayout.findViewByPosition(linearLayout.findLastCompletelyVisibleItemPosition()); - viewByPosition.requestFocusFromTouch(); - }, 200); - return true; } } diff --git a/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/OnKeyDownDelegate.java b/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/OnKeyDownDelegate.java index d66277085..aea1478bf 100644 --- a/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/OnKeyDownDelegate.java +++ b/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/OnKeyDownDelegate.java @@ -74,14 +74,8 @@ public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { if (newPosition < 0) { newPosition = 0; } - tvRecyclerView.scrollToPosition(newPosition); + tvRecyclerView.smoothScrollToPosition(newPosition); Timber.d("New ItemPosition: " + newPosition); - - scrollingHandler.postDelayed(() -> { - LinearLayoutManager linearLayout = (LinearLayoutManager) tvRecyclerView.getLayoutManager(); - View viewByPosition = linearLayout.findViewByPosition(linearLayout.findFirstCompletelyVisibleItemPosition()); - viewByPosition.requestFocusFromTouch(); - }, 700); return true; } @@ -90,13 +84,8 @@ public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { if (newPosition > itemsCount) { newPosition = itemsCount - 1; } - tvRecyclerView.scrollToPosition(newPosition); + tvRecyclerView.smoothScrollToPosition(newPosition); Timber.d("New ItemPosition: " + newPosition); - scrollingHandler.postDelayed(() -> { - LinearLayoutManager linearLayout = (LinearLayoutManager) tvRecyclerView.getLayoutManager(); - linearLayout.findViewByPosition(linearLayout.findLastCompletelyVisibleItemPosition()).requestFocusFromTouch(); - }, 700); - return true; } } diff --git a/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/TVShowBrowserActivity.java b/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/TVShowBrowserActivity.java index 28b395de1..ada2010c8 100644 --- a/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/TVShowBrowserActivity.java +++ b/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/TVShowBrowserActivity.java @@ -57,6 +57,8 @@ import us.nineworlds.serenity.ui.activity.SerenityMultiViewVideoActivity; import us.nineworlds.serenity.ui.adapters.AbstractPosterImageGalleryAdapter; import us.nineworlds.serenity.ui.adapters.MenuDrawerAdapter; +import us.nineworlds.serenity.ui.recyclerview.FocusableGridLayoutManager; +import us.nineworlds.serenity.ui.recyclerview.FocusableLinearLayoutManager; import us.nineworlds.serenity.ui.util.DisplayUtils; import static android.view.View.*; @@ -111,7 +113,7 @@ public class TVShowBrowserActivity extends SerenityMultiViewVideoActivity private void populateTVShowContent() { if (gridViewActive) { - GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3, GridLayoutManager.HORIZONTAL, false); + GridLayoutManager gridLayoutManager = new FocusableGridLayoutManager(this, 3, GridLayoutManager.HORIZONTAL, false); tvShowRecyclerView.setLayoutManager(gridLayoutManager); tvShowRecyclerView.addItemDecoration( @@ -124,8 +126,8 @@ private void populateTVShowContent() { return; } - LinearLayoutManager linearLayoutManager = - new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); + LinearLayoutManager linearLayoutManager = new FocusableLinearLayoutManager(this); + linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); tvShowRecyclerView.setLayoutManager(linearLayoutManager); tvShowRecyclerView.addItemDecoration( new SpaceItemDecoration( diff --git a/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/seasons/TVShowSeasonBrowserActivity.java b/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/seasons/TVShowSeasonBrowserActivity.java index 07152ea9d..44fe6ea0c 100644 --- a/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/seasons/TVShowSeasonBrowserActivity.java +++ b/serenity-app/src/main/java/us/nineworlds/serenity/ui/browser/tv/seasons/TVShowSeasonBrowserActivity.java @@ -49,6 +49,8 @@ import us.nineworlds.serenity.ui.activity.SerenityVideoActivity; import us.nineworlds.serenity.ui.adapters.AbstractPosterImageGalleryAdapter; import us.nineworlds.serenity.ui.adapters.MenuDrawerAdapter; +import us.nineworlds.serenity.ui.recyclerview.FocusableGridLayoutManager; +import us.nineworlds.serenity.ui.recyclerview.FocusableLinearLayoutManager; import us.nineworlds.serenity.ui.util.DisplayUtils; import us.nineworlds.serenity.widgets.DrawerLayout; @@ -98,8 +100,9 @@ protected void setupSeasons() { tvShowSeasonsGallery.setClipToPadding(false); tvShowSeasonsGallery.setAdapter(adapter); tvShowSeasonsGallery.setItemAnimator(new FadeInAnimator()); - tvShowSeasonsGallery.setLayoutManager( - new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); + LinearLayoutManager linearLayoutManager = new FocusableLinearLayoutManager(this); + linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); + tvShowSeasonsGallery.setLayoutManager(linearLayoutManager); tvShowSeasonsGallery.addItemDecoration(createItemDecorator()); tvShowSeasonsGallery.setFocusable(true); @@ -112,7 +115,7 @@ protected void setupSeasons() { gridView.setAdapter(episodeAdapter); gridView.addItemDecoration(createItemDecorator()); gridView.setItemAnimator(new FadeInAnimator()); - GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2, GridLayoutManager.HORIZONTAL, false) { + GridLayoutManager gridLayoutManager = new FocusableGridLayoutManager(this, 2, GridLayoutManager.HORIZONTAL, false) { @Override public boolean supportsPredictiveItemAnimations() { return false; } diff --git a/serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableGridLayoutManager.kt b/serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableGridLayoutManager.kt new file mode 100644 index 000000000..be02be861 --- /dev/null +++ b/serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableGridLayoutManager.kt @@ -0,0 +1,17 @@ +package us.nineworlds.serenity.ui.recyclerview + +import android.content.Context +import android.support.v7.widget.GridLayoutManager +import android.support.v7.widget.RecyclerView + +open class FocusableGridLayoutManager(val context: Context, spanCount: Int, orientation: Int, reverseLayout: Boolean) : GridLayoutManager(context, spanCount, orientation, reverseLayout) { + + override fun smoothScrollToPosition(recyclerView: RecyclerView?, state: RecyclerView.State?, position: Int) { + val smoothScroller = FocusableLinearSmoothScroller(context) + + smoothScroller.targetPosition = position + startSmoothScroll(smoothScroller) + } + + +} \ No newline at end of file diff --git a/serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableLinearLayoutManager.kt b/serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableLinearLayoutManager.kt new file mode 100644 index 000000000..bb29c9fe5 --- /dev/null +++ b/serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableLinearLayoutManager.kt @@ -0,0 +1,14 @@ +package us.nineworlds.serenity.ui.recyclerview + +import android.content.Context +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView + +class FocusableLinearLayoutManager(val context: Context) : LinearLayoutManager(context) { + + override fun smoothScrollToPosition(recyclerView: RecyclerView?, state: RecyclerView.State?, position: Int) { + val smoothScroller = FocusableLinearSmoothScroller(context) + smoothScroller.targetPosition = position + startSmoothScroll(smoothScroller) + } +} \ No newline at end of file diff --git a/serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableLinearSmoothScroller.kt b/serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableLinearSmoothScroller.kt new file mode 100644 index 000000000..19fab2a50 --- /dev/null +++ b/serenity-app/src/main/kotlin/us/nineworlds/serenity/ui/recyclerview/FocusableLinearSmoothScroller.kt @@ -0,0 +1,24 @@ +package us.nineworlds.serenity.ui.recyclerview + +import android.content.Context +import android.support.v7.widget.LinearSmoothScroller +import android.support.v7.widget.RecyclerView +import android.view.View + +class FocusableLinearSmoothScroller(context: Context) : LinearSmoothScroller(context) { + + var targetView : View? = null + + override fun onTargetFound(targetView: View?, state: RecyclerView.State?, action: Action?) { + super.onTargetFound(targetView, state, action) + this.targetView = targetView + } + + override fun onStop() { + super.onStop() + + if (targetView != null && !targetView!!.hasFocus()) { + targetView!!.requestFocusFromTouch() + } + } +} \ No newline at end of file