简体   繁体   English

使用 GridLayoutManager 和 RecyclerView 更改列数

[英]Changing number of columns with GridLayoutManager and RecyclerView

Inside my fragment I'm setting my GridLayout in the following way: mRecycler.setLayoutManager(new GridLayoutManager(rootView.getContext(), 2));在我的片段中,我按以下方式设置我的 GridLayout: mRecycler.setLayoutManager(new GridLayoutManager(rootView.getContext(), 2));

So, I just want to change that 2 for a 4 when the user rotates the phone/tablet.所以,我只想在用户旋转手机/平板电脑时将2更改为4 I've read about onConfigurationChanged and I tried to make it work for my case, but it isn't going in the right way.我已经阅读了有关onConfigurationChanged ,并且我试图让它适用于我的情况,但它并没有以正确的方式进行。 When I rotate my phone, the app crashes...当我旋转手机时,应用程序崩溃了......

Could you tell me how to solve this issue?你能告诉我如何解决这个问题吗?

Here is my approach to find the solution, which is not working correctly:这是我找到解决方案的方法,但无法正常工作:

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

If you have more than one condition or use the value in multiple places this can go out of hand pretty fast.如果您有多个条件或在多个地方使用该值,这可能会很快失控。 I suggest to create the following structure:我建议创建以下结构:

res
  - values
    - dimens.xml
  - values-land
    - dimens.xml

with res/values/dimens.xml being:res/values/dimens.xml是:

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

and res/values-land/dimens.xml being:res/values-land/dimens.xml是:

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

And the code then becomes (and forever stays) like this:然后代码变成(并且永远保持)像这样:

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

You can easily see how easy it is to add new ways of determining the column count, for example using -w500dp / -w600dp / -w700dp resource folders instead of -land .您可以很容易地看到添加确定列数的新方法是多么容易,例如使用-w500dp / -w600dp / -w700dp资源文件夹而不是-land

It's also quite easy to group these folders into separate resource folder in case you don't want to clutter your other (more relevant) resources:将这些文件夹分组到单独的资源文件夹中也很容易,以防您不想弄乱其他(更相关的)资源:

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

Try handling this inside your onCreateView method instead since it will be called each time there's an orientation change:尝试在您的 onCreateView 方法中处理此问题,因为每次发生方向更改时都会调用它:

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

The Recycle View supports AutofitRecycleView.回收视图支持 AutofitRecycleView。

You need to add android:numColumns="auto_fit" in your xml file.您需要在 xml 文件中添加android:numColumns="auto_fit"

You can refer to thisAutofitRecycleViewLink可以参考这个AutofitRecycleViewLink

A more robust way to determine the no.一种更可靠的方法来确定编号。 of columns would be to calculate it based on the screen width and at runtime.列数将根据屏幕宽度和运行时进行计算。 I normally use the following function for that.我通常为此使用以下功能。

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
}

It is a more dynamic method to accurately calculate the no.这是一种更动态的方法来准确计算编号。 of columns.列。 This will be more adaptive for users of varying screen sizes without being resticted to only two possible values.这将更适合不同屏幕尺寸的用户,而不会仅限于两个可能的值。

NB: You can vary the value held by the scalingFactor variable.注意:您可以改变由scalingFactor变量保存的值。 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.这是根据您的需求进行调整的比例因子。

In addition to the answers.除了答案。 It can be also done using XML attributes only.也可以仅使用 XML 属性来完成。 Below is the code.下面是代码。

<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" />

In the onCreate () event you can use StaggeredGridLayoutManager在 onCreate() 事件中可以使用 StaggeredGridLayoutManager

mRecyclerView = (RecyclerView) v.findViewById(R.id.recyclerView);      

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

mRecyclerView.setLayoutManager(mStaggeredGridLayoutManager);

Then when the user rotates the screen capture the event, and change the number of columns automatically然后当用户旋转屏幕捕获事件时,并自动更改列数

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

I ended up handling this in the onCreate method.我最终在 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();
}

It worked out perfectly for my app.它非常适合我的应用程序。

You can implement the method in your recyclerView onMeasure.您可以在您的 recyclerView onMeasure 中实现该方法。 First, create the java class AutofitRecyclerView首先,创建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);
    }
}}

In your xlm layout file activity_main.xml在您的 xlm 布局文件 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"/>

Then set the variable to the width of each item in the file size of the values folder values/dimens.xml然后将变量设置为 values 文件夹 values/dimens.xml 的文件大小中每个项目的宽度

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

It can be for different screen resolutions values-xxhdpi/dimens.xml它可以针对不同的屏幕分辨率值-xxhdpi/dimens.xml

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

In your activity in the onCreate event place the following code在 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));
}

I will try to explain it step by step.我将尝试逐步解释它。 And you can check integers.xml, integers.xml (land) and MainFragment files of github project that I shared.你可以查看我分享的github项目的integers.xml、integers.xml(land)和MainFragment文件。 You will see that number of columns will change based on orientation or screen size(tablet vs phone)(I will explain only how to do it when orientation changed, since the question is only about it).您会看到列数会根据方向或屏幕尺寸(平板电脑与手机)发生变化(我只会解释方向改变时如何操作,因为问题只是关于它)。 https://github.com/b-basoglu/NewsApp/blob/master/app/src/main/java/com/news/newsapp/ui/main/MainFragment.kt https://github.com/b-basoglu/NewsApp/blob/master/app/src/main/java/com/news/newsapp/ui/main/MainFragment.kt

  1. Create a resources file called integers.xml.创建一个名为 integers.xml 的资源文件。 -> Open the res folder in the Project > Android pane, right-click on the values folder, and select New > Values resource file. -> 在Project > Android 窗格中打开res 文件夹,右键单击values 文件夹, select New > Values 资源文件。

  2. Name the file integers.xml and click OK.将文件命名为 integers.xml 并单击确定。

  3. Create an integer constant between the tags called grid_column_count and set it equal to 2:在名为 grid_column_count 的标签之间创建一个 integer 常量并将其设置为 2:

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

  4. Create another values resource file, again called integers.xml;创建另一个值资源文件,再次称为 integers.xml; however, the name will be modified as you add resource qualifiers from the Available qualifiers pane.但是,当您从可用限定符窗格中添加资源限定符时,该名称将被修改。 The resource qualifiers are used to label resource configurations for various situations.资源限定符用于 label 各种情况下的资源配置。

  5. Select Orientation in the Available qualifiers pane, and press the >> symbol in the middle of the dialog to assign this qualifier. Select 在可用限定符窗格中的方向,然后按对话框中间的 >> 符号来分配此限定符。

  6. Change the Screen orientation menu to Landscape, and notice how the directory name values-land appears.将屏幕方向菜单更改为横向,并注意目录名称 values-land 的显示方式。 This is the essence of resource qualifiers: the directory name tells Android when to use that specific layout file.这是资源限定符的本质:目录名称告诉 Android 何时使用该特定布局文件。 In this case, that is when the phone is rotated to landscape mode.在这种情况下,即手机旋转到横向模式时。

  7. Click OK to generate the new layout file.单击“确定”以生成新的布局文件。

  8. Copy the integer constant you created into this new resource file, but change the value to 4.将您创建的 integer 常量复制到这个新资源文件中,但将值更改为 4。

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

[You have these files][1] [你有这些文件][1]

  1. Now all you have to do is re-assigning your span count when configuration changed as below:现在您所要做的就是在配置更改时重新分配跨度计数,如下所示:
    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://i.stack.imgur.com/3NZdq.png

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM