Android: Create AutofitGridLayoutManager

Extends GridLayoutManager to make span changes much more responsive.
This commit is contained in:
Charles Lombardo 2023-02-21 09:41:03 -05:00
parent 3c4a21315d
commit 5957d85178
2 changed files with 75 additions and 37 deletions

View File

@ -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)
}
}

View File

@ -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)