From 5957d851783083677a4910046d14b48f5f28287a Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Tue, 21 Feb 2023 09:41:03 -0500 Subject: [PATCH] Android: Create AutofitGridLayoutManager Extends GridLayoutManager to make span changes much more responsive. --- .../layout/AutofitGridLayoutManager.kt | 60 +++++++++++++++++++ .../ui/platform/PlatformGamesFragment.java | 52 +++++----------- 2 files changed, 75 insertions(+), 37 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/layout/AutofitGridLayoutManager.kt diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/layout/AutofitGridLayoutManager.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/layout/AutofitGridLayoutManager.kt new file mode 100644 index 0000000000..8fc52e756e --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/layout/AutofitGridLayoutManager.kt @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.layout + +import android.content.Context +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.Recycler +import org.dolphinemu.dolphinemu.R + +/** + * Cut down version of the solution provided here + * https://stackoverflow.com/questions/26666143/recyclerview-gridlayoutmanager-how-to-auto-detect-span-count + */ +class AutofitGridLayoutManager( + context: Context, + columnWidth: Int +) : GridLayoutManager(context, 1) { + private var columnWidth = 0 + private var isColumnWidthChanged = true + private var lastWidth = 0 + private var lastHeight = 0 + + init { + setColumnWidth(checkedColumnWidth(context, columnWidth)) + } + + private fun checkedColumnWidth(context: Context, columnWidth: Int): Int { + var newColumnWidth = columnWidth + if (newColumnWidth <= 0) { + newColumnWidth = context.resources.getDimensionPixelSize(R.dimen.spacing_xtralarge) + } + return newColumnWidth + } + + fun setColumnWidth(newColumnWidth: Int) { + if (newColumnWidth > 0 && newColumnWidth != columnWidth) { + columnWidth = newColumnWidth + isColumnWidthChanged = true + } + } + + override fun onLayoutChildren(recycler: Recycler, state: RecyclerView.State) { + val width = width + val height = height + if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) { + val totalSpace: Int = if (orientation == VERTICAL) { + width - paddingRight - paddingLeft + } else { + height - paddingTop - paddingBottom + } + val spanCount = 1.coerceAtLeast(totalSpace / columnWidth) + setSpanCount(spanCount) + isColumnWidthChanged = false + } + lastWidth = width + lastHeight = height + super.onLayoutChildren(recycler, state) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java index d76ffce3c5..10dbf10c00 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java @@ -6,7 +6,6 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -14,7 +13,7 @@ import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.google.android.material.color.MaterialColors; @@ -22,13 +21,13 @@ import com.google.android.material.color.MaterialColors; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.adapters.GameAdapter; import org.dolphinemu.dolphinemu.databinding.FragmentGridBinding; +import org.dolphinemu.dolphinemu.layout.AutofitGridLayoutManager; import org.dolphinemu.dolphinemu.services.GameFileCacheManager; public final class PlatformGamesFragment extends Fragment implements PlatformGamesView { private static final String ARG_PLATFORM = "platform"; - private GameAdapter mAdapter; private SwipeRefreshLayout mSwipeRefresh; private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener; @@ -64,37 +63,12 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { mSwipeRefresh = mBinding.swipeRefresh; - mAdapter = new GameAdapter(requireActivity()); - - // Here we have to make sure the fragment is attached to an activity, wait for the layout - // to be drawn, and make sure it is drawn with a width > 0 before finding the correct - // span for our grid layout. Once drawn correctly, we can stop listening for layout changes. - if (isAdded()) - { - view.getViewTreeObserver() - .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() - { - @Override - public void onGlobalLayout() - { - if (mBinding.getRoot().getMeasuredWidth() == 0) - { - return; - } - - int columns = mBinding.getRoot().getMeasuredWidth() / - requireContext().getResources().getDimensionPixelSize(R.dimen.card_width); - if (columns == 0) - { - columns = 1; - } - view.getViewTreeObserver().removeOnGlobalLayoutListener(this); - GridLayoutManager layoutManager = new GridLayoutManager(getActivity(), columns); - mBinding.gridGames.setLayoutManager(layoutManager); - mBinding.gridGames.setAdapter(mAdapter); - } - }); - } + GameAdapter adapter = new GameAdapter(requireActivity()); + adapter.setStateRestorationPolicy( + RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY); + mBinding.gridGames.setAdapter(adapter); + mBinding.gridGames.setLayoutManager(new AutofitGridLayoutManager(requireContext(), + getResources().getDimensionPixelSize(R.dimen.card_width))); // Set theme color to the refresh animation's background mSwipeRefresh.setProgressBackgroundColorSchemeColor( @@ -133,17 +107,21 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam @Override public void showGames() { - if (mAdapter != null) + if (mBinding == null) + return; + + if (mBinding.gridGames.getAdapter() != null) { Platform platform = (Platform) getArguments().getSerializable(ARG_PLATFORM); - mAdapter.swapDataSet(GameFileCacheManager.getGameFilesForPlatform(platform)); + ((GameAdapter) mBinding.gridGames.getAdapter()).swapDataSet( + GameFileCacheManager.getGameFilesForPlatform(platform)); } } @Override public void refetchMetadata() { - mAdapter.refetchMetadata(); + ((GameAdapter) mBinding.gridGames.getAdapter()).refetchMetadata(); } public void setOnRefreshListener(@Nullable SwipeRefreshLayout.OnRefreshListener listener)