使用GridLayoutManager和RecyclerView更改列数

70

在我的片段中,我以下列方式设置了GridLayout:

mRecycler.setLayoutManager(new GridLayoutManager(rootView.getContext(), 2));

所以,当用户旋转手机/平板电脑时,我希望将2更改为4。我已经了解了onConfigurationChanged,并尝试让它适用于我的情况,但它并没有按照正确的方式运行。当我旋转我的手机时,应用程序崩溃了...

您能告诉我如何解决这个问题吗?

以下是我寻找解决方案的方法,但它不能正常工作:

  @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        int orientation = newConfig.orientation;

        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            mRecycler.setLayoutManager(new GridLayoutManager(mContext, 2));
        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            mRecycler.setLayoutManager(new GridLayoutManager(mContext, 4));
        }
    }

请添加崩溃日志。 - Bojan Kseneman
1
我不会创建一个新的管理器,而是使用现有的。在您的RecyclerView上使用getLayoutManager(),将其转换为GridLayoutManager。在该管理器上调用setSpanCount(orientation == portrait ? 2 : 4)。为了重新绘制视图,请调用adaper.notifyDatasetChanged()。如果您的视图不是每次都被重绘,这应该可以正常工作。 - Bojan Kseneman
哇!这听起来就像我一直在寻找的,但我不知道如何实现。我会尝试一下并告诉你结果。 - fapps
请注意,在创建视图本身时检查方向始终是一个好主意。对于活动,您通常在onCreate中执行此操作,对于片段,则在onCreateView中执行此操作。请确保这样做,因为您的用户可能会在横向模式下启动活动。 - Bojan Kseneman
没有起作用,我认为是因为视图被重新绘制了。 - fapps
显示剩余5条评论
9个回答

92

如果您有多个条件或在多个地方使用该值,这可能会很快变得混乱。我建议创建以下结构:

res
  - values
    - integers.xml
  - values-land
    - integers.xml

具有res/values/integers.xml文件的内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="gallery_columns">2</integer>
</resources>

以及res/values-land/integers.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="gallery_columns">4</integer>
</resources>

然后代码就变成了(并且永远保持这样):

final int columns = getResources().getInteger(R.integer.gallery_columns);
mRecycler.setLayoutManager(new GridLayoutManager(mContext, columns));

您可以轻松地看到添加新的确定列数的方法有多容易,例如使用-w500dp/-w600dp/-w700dp资源文件夹代替-land

如果您不想混乱您的其他(更相关的)资源,将这些文件夹分组到单独的资源文件夹中也很容易:

android {
    sourceSets.main.res.srcDir 'src/main/res-overrides' // add alongside src/main/res
}

83

考虑在onCreateView方法中处理此问题,因为每次发生方向变化时都会调用它:

if(getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){
     mRecycler.setLayoutManager(new GridLayoutManager(mContext, 2));
}
else{
     mRecycler.setLayoutManager(new GridLayoutManager(mContext, 4));
}

我会试一下并告诉你结果。谢谢! - fapps
3
这个任务的执行方式正确吗?谢谢。 - ciccioska
5
我认为资源文件夹比在getConfiguration()onConfigurationChanged上进行条件判断更好。也请参阅我在这个问题中的答案。 - TWiStErRob

13

除了这些答案外,还可以仅使用XML属性来完成。以下是代码。

<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/pax_seat_map_rv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:spanCount="3"
        android:orientation="vertical"
        app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" />

9

更加健壮的确定列数的方法是基于屏幕宽度和运行时计算。我通常使用以下函数来实现。

public static int calculateNoOfColumns(Context context) {
    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
    float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
    int scalingFactor = 200; // You can vary the value held by the scalingFactor
    // variable. The smaller it is the more no. of columns you can display, and the
    // larger the value the less no. of columns will be calculated. It is the scaling
    // factor to tweak to your needs.
    int columnCount = (int) (dpWidth / scalingFactor);
    return (columnCount>=2?columnCount:2); // if column no. is less than 2, we still display 2 columns
}

这是一种更动态的方法来准确计算列数。这将更适应不同屏幕尺寸的用户,而不仅仅局限于两个可能的值。

NB:您可以改变scalingFactor变量的值。它越小,您就可以显示更多的列,而值越大,计算出的列数就越少。这是一个需要调整以满足您需求的缩放因子。


9

Recycle View支持AutofitRecycleView。

你需要在你的xml文件中添加android:numColumns="auto_fit"

你可以参考这个AutofitRecycleViewLink


优秀的实现!旋转屏幕以动态更改列数并保持滚动位置。 - Vladimir Salguero
这可能是最完美的解决方案,但如果你的设计师决定做出不同的选择,那就不是了。有时候设计会让我们通过艰难的方式来实现它。 - Stoycho Andreev

4
在onCreate()事件中,您可以使用StaggeredGridLayoutManager。
mRecyclerView = (RecyclerView) v.findViewById(R.id.recyclerView);      

mStaggeredGridLayoutManager = new StaggeredGridLayoutManager(
       1, //number of grid columns
       GridLayoutManager.VERTICAL);      

mRecyclerView.setLayoutManager(mStaggeredGridLayoutManager);

当用户旋转屏幕时,触发事件并自动更改列数。

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {           
            mStaggeredGridLayoutManager.setSpanCount(1);

    } else {           
            //show in two columns
            mStaggeredGridLayoutManager.setSpanCount(2);           
    }
}

2
我最终在onCreate方法中处理了这个问题。
private RecyclerView recyclerView = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    recyclerView.setHasFixedSize(true);
    Configuration orientation = new Configuration();
    if(this.recyclerView.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
        recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
    } else if (this.recyclerView.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
        recyclerView.setLayoutManager(new GridLayoutManager(this, 4));
    }
            connectGetApiData();
}

对于我的应用来说,它完美地发挥作用了。


0
我会逐步解释它。您可以查看我分享的github项目的integers.xml、integers.xml(land)和MainFragment文件。您将看到列数将根据方向或屏幕大小(平板电脑与手机)而改变(由于问题仅涉及方向更改,因此我只会解释如何进行操作)。https://github.com/b-basoglu/NewsApp/blob/master/app/src/main/java/com/news/newsapp/ui/main/MainFragment.kt
  1. 创建一个名为integers.xml的资源文件。 -> 在项目 > Android窗格中打开res文件夹,在values文件夹上右键单击,然后选择新建 > 值资源文件。

  2. 将文件命名为integers.xml并单击确定。

  3. 在标签之间创建一个整数常量,称为grid_column_count,并将其设置为2:

    < integer name="grid_column_count">2</ integer >

  4. 创建另一个值资源文件,同样称为integers.xml;但是,随着添加可用限定符来修改名称。资源限定符用于为各种情况标记资源配置。

  5. 在“可用限定符”窗格中选择方向,然后按对话框中心的“>>”符号分配此限定符。

  6. 更改屏幕方向菜单为横向,并注意到目录名称values-land出现。这就是资源限定符的实质:目录名称告诉Android何时使用该特定布局文件。在本例中,当手机旋转到横向模式时。

  7. 单击“确定”生成新的布局文件。

  8. 将所创建的整数常量复制到此新资源文件中,但将值更改为4。

    < integer name="grid_column_count">4</ integer >

[你有这些文件][1]

  1. 现在你所要做的就是在配置更改时重新分配你的跨度计数,如下所示:
    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        gridLayoutManager?.let {
            it.spanCount = resources.getInteger(R.integer.grid_column_count)
        }
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        gridLayoutManager = GridLayoutManager(requireContext(), 
                                resources.getInteger(R.integer.grid_column_count))
        ${yourRecyclerView}.layoutManager = gridLayoutManager
        ...
  [1]: https://istack.dev59.com/3NZdq.webp

0

您可以在recyclerView的onMeasure方法中实现此方法。 首先,创建Java类AutofitRecyclerView。

public class AutofitRecyclerView extends RecyclerView {
//private GridLayoutManager manager;
private StaggeredGridLayoutManager manager;
private int columnWidth = -1;

public AutofitRecyclerView(Context context) {
    super(context);
    init(context, null);
}

public AutofitRecyclerView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs);
}

public AutofitRecyclerView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(context, attrs);
}

private void init(Context context, AttributeSet attrs) {
    if (attrs != null) {
        int[] attrsArray = {
                android.R.attr.columnWidth
        };
        TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
        columnWidth = array.getDimensionPixelSize(0, -1);
        array.recycle();
    }

    manager = new StaggeredGridLayoutManager(1, GridLayoutManager.VERTICAL);
    setLayoutManager(manager);

}

@Override
protected void onMeasure(int widthSpec, int heightSpec) {
    super.onMeasure(widthSpec, heightSpec);
    if (columnWidth > 0) {
        int spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
        manager.setSpanCount(spanCount);
    }
}}

在你的 XML 布局文件 activity_main.xml 中。
<yourpackagename.AutofitRecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:columnWidth="@dimen/column_width"
            android:clipToPadding="false"/>

然后将变量设置为值文件夹 values/dimens.xml 中每个项目的宽度大小

<resources>
  <dimen name="column_width">250dp</dimen>
</resources>

这可以是针对不同屏幕分辨率的值-xxhdpi/dimens.xml

<resources>
 <dimen name="column_width">280dp</dimen>
</resources>

在您的活动中,在onCreate事件中放置以下代码。
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    recyclerView.addItemDecoration(new MarginDecoration(this));
    recyclerView.setHasFixedSize(true);
    recyclerView.setAdapter(new NumberedAdapter(50));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接