From a379a2cc2b72a0170f6649a06a6539b4d6387429 Mon Sep 17 00:00:00 2001 From: David Sandquist Date: Mon, 31 Aug 2026 17:26:54 +0200 Subject: [PATCH] Derive grid column count from the width the list is given The number of grid columns came straight from the stored zoom preference and never looked at how wide the window actually is, so a phone in portrait and a tablet in landscape both showed the same three columns. onConfigurationChanged only ever clamped the count downwards, which meant rotating to landscape raised the limit but never added a column. Add a GridLayoutManager that works out its own column count from the width it is given during layout, and hand it a target column width instead of a column count. The target width is a dimension resource, so it can be adjusted per screen size later without touching this code. The width has to be read during layout. The display metrics are not a usable substitute: they describe the display rather than the space the list occupies, they are not yet meaningful while the view is being created, and during a rotation they can still describe the previous orientation, which produced a portrait grid laid out to the width of a landscape screen. Pinch to zoom keeps working and is still persisted. It now sets how large the cells are rather than a fixed column count, so a zoom level chosen in portrait carries over to landscape instead of being lost. Addresses part of the grid item of #6769. Signed-off-by: David Sandquist --- .../ui/fragment/AutofitGridLayoutManager.kt | 55 +++++++++++++++++++ .../ui/fragment/ExtendedListFragment.kt | 44 ++++++++------- .../ui/fragment/FileListLayoutManager.kt | 2 +- app/src/main/res/values/dims.xml | 3 + 4 files changed, 83 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/AutofitGridLayoutManager.kt diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/AutofitGridLayoutManager.kt b/app/src/main/java/com/owncloud/android/ui/fragment/AutofitGridLayoutManager.kt new file mode 100644 index 000000000000..653807aa8c3a --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/fragment/AutofitGridLayoutManager.kt @@ -0,0 +1,55 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.fragment + +import android.content.Context +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kotlin.math.max +import kotlin.math.roundToInt + +/** + * A [GridLayoutManager] that works out its own column count from the width it is actually given. + * + * The width is read during layout rather than from the display metrics, because the metrics are + * not a reliable stand in: they describe the display rather than the space this list occupies, + * they are not yet meaningful while the view is being created, and they can still describe the + * previous orientation while a rotation is being delivered. + * + * @param columnWidthProvider target width of a single column in pixels, read on every layout so + * that a changed preference is picked up without recreating the layout manager. + */ +class AutofitGridLayoutManager(context: Context, private val columnWidthProvider: () -> Int) : + GridLayoutManager(context, 1) { + + private var lastWidth = 0 + private var lastColumnWidth = 0 + + override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { + updateSpanCount() + super.onLayoutChildren(recycler, state) + } + + private fun updateSpanCount() { + val columnWidth = columnWidthProvider() + if (width <= 0 || columnWidth <= 0) { + return + } + + if (width == lastWidth && columnWidth == lastColumnWidth) { + return + } + + lastWidth = width + lastColumnWidth = columnWidth + spanCount = max(MIN_COLUMN_COUNT, (width.toFloat() / columnWidth).roundToInt()) + } + + companion object { + private const val MIN_COLUMN_COUNT = 2 + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt index 45f9063369b3..49f54eb736b8 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt @@ -86,8 +86,6 @@ open class ExtendedListFragment : SearchView.OnQueryTextListener, SearchView.OnCloseListener, Injectable { - private var maxColumnSize = 5 - @Inject lateinit var preferences: AppPreferences @@ -146,7 +144,7 @@ open class ExtendedListFragment : open fun switchToGridView() { if (!isGridEnabled) { - recyclerView?.layoutManager = GridLayoutManager(context, columnsCount) + recyclerView?.layoutManager = AutofitGridLayoutManager(requireContext()) { targetColumnWidth } } } @@ -375,9 +373,8 @@ open class ExtendedListFragment : mScale = gridLayoutManager.spanCount.toFloat() } mScale *= 2f - scaleFactor - mScale = max(MIN_COLUMN_SIZE, min(mScale, maxColumnSize.toFloat())) - val scaleInt = mScale.roundToInt() - gridLayoutManager.setSpanCount(scaleInt) + mScale = max(MIN_COLUMN_SIZE, min(mScale, MAX_COLUMN_SCALE)) + gridLayoutManager.requestLayout() mRecyclerView?.adapter?.notifyDataSetChanged() } } @@ -437,14 +434,24 @@ open class ExtendedListFragment : preferences.setGridColumns(mScale) } - open val columnsCount: Int + /** + * Target width of one grid column in pixels. + * + * [mScale] is a zoom level: asking for more columns than the default means asking for + * smaller cells. The column count itself is worked out by [AutofitGridLayoutManager] from + * the width the list is actually given. + */ + internal val targetColumnWidth: Int get() { - if (mScale == -1f) { - return AppPreferencesImpl.DEFAULT_GRID_COLUMN.roundToInt() - } - return mScale.roundToInt() + val zoom = (if (mScale > 0f) mScale else AppPreferencesImpl.DEFAULT_GRID_COLUMN) / + AppPreferencesImpl.DEFAULT_GRID_COLUMN + return (resources.getDimension(R.dimen.grid_item_default_width) / zoom).roundToInt() } + open val columnsCount: Int + get() = (recyclerView?.layoutManager as? GridLayoutManager)?.spanCount + ?: AppPreferencesImpl.DEFAULT_GRID_COLUMN.roundToInt() + /* * Restore index and position */ @@ -778,15 +785,7 @@ open class ExtendedListFragment : override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { - maxColumnSize = 10 - } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { - maxColumnSize = 5 - } - - if (isGridEnabled && columnsCount > maxColumnSize) { - (recyclerView?.layoutManager as GridLayoutManager).spanCount = maxColumnSize - } + // The column count follows the width the list is given, so nothing to do here. } protected fun setLayoutSwitchButton() { @@ -821,5 +820,10 @@ open class ExtendedListFragment : private const val KEY_EMPTY_LIST_MESSAGE = "EMPTY_LIST_MESSAGE" private const val KEY_IS_GRID_VISIBLE = "IS_GRID_VISIBLE" private const val MIN_COLUMN_SIZE: Float = 2.0f + + /** + * Highest number of columns the pinch gesture can select on a reference-width screen. + */ + private const val MAX_COLUMN_SCALE: Float = 5.0f } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileListLayoutManager.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileListLayoutManager.kt index 1f4375466bc7..39b7f146ee23 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileListLayoutManager.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileListLayoutManager.kt @@ -87,7 +87,7 @@ class FileListLayoutManager(private val fragment: OCFileListFragment, private va val layoutManager: RecyclerView.LayoutManager? if (grid) { - layoutManager = GridLayoutManager(context, fragment.columnsCount) + layoutManager = AutofitGridLayoutManager(context) { fragment.targetColumnWidth } layoutManager.spanSizeLookup = object : SpanSizeLookup() { override fun getSpanSize(position: Int): Int = if (position == fragment.adapter.itemCount - 1 || (position == 0 && fragment.adapter.shouldShowHeader()) diff --git a/app/src/main/res/values/dims.xml b/app/src/main/res/values/dims.xml index ffc32ec8b7a8..fc54132e8982 100644 --- a/app/src/main/res/values/dims.xml +++ b/app/src/main/res/values/dims.xml @@ -46,6 +46,9 @@ 12sp 20dp 10dp + + 140dp 2dp 22sp 2dp