Изменение количества столбцов с помощью GridLayoutManager и RecyclerView

Внутри моего фрагмента я устанавливаю свой 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));
        }
    }

Заранее спасибо!


person fapps    schedule 11.04.2015    source источник
comment
пожалуйста, добавьте журнал сбоев   -  person Bojan Kseneman    schedule 11.04.2015
comment
Я только что посмотрел на свой логарифм, и это не было причиной сбоя. Но это все еще не работает.   -  person fapps    schedule 11.04.2015
comment
Я бы не стал создавать нового менеджера, а вместо этого использовал существующий. Используйте getLayoutManager() в вашем recyclerview, приведите его к GridLayoutManager. В этом диспетчере вызовите setSpanCount(ориентация == портрет ? 2: 4) И ради перерисовки представления вызовите adaper.notifyDatasetChanged() Это должно работать нормально, если ваше представление не перерисовывается каждый раз.   -  person Bojan Kseneman    schedule 12.04.2015
comment
ВОТ ЭТО ДА! Это звучит так, как будто я искал, но я не знал, как достичь. Я попробую и расскажу вам об этом.   -  person fapps    schedule 12.04.2015
comment
Обратите внимание, что всегда рекомендуется проверять ориентацию при создании самого представления. Это вы обычно делаете в onCreate для действий или onCreateView для фрагментов. Убедитесь, что вы сделали это, потому что ваши пользователи могут начать активность в ландшафтном режиме.   -  person Bojan Kseneman    schedule 12.04.2015
comment
Не работало, я думаю, это из-за перерисовки вида.   -  person fapps    schedule 12.04.2015
comment
Да. Вид по умолчанию перерисовывается, когда вы меняете ориентацию, поэтому убедитесь, что вы обрабатываете его там.   -  person Bojan Kseneman    schedule 12.04.2015
comment
@fapps Ваш вопрос отлично сработал для меня, я не знаю, почему у вас произошел сбой. Вы когда-нибудь выясняли, почему?   -  person Jared Burrows    schedule 20.05.2015
comment
Посмотрите на @SobaDeveloper. Я изменил свой onResume() на мой onCreateView()   -  person fapps    schedule 20.05.2015
comment
Я в основном реализовал оба ваших ответа. Я удивлен, что это не привлекло больше внимания. Я вижу, что многие приложения делают это.   -  person Jared Burrows    schedule 20.05.2015


Ответы (8)


Вместо этого попробуйте обработать это внутри вашего метода onCreateView, так как он будет вызываться каждый раз при изменении ориентации:

if(getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){
     mRecycler.setLayoutManager(new GridLayoutManager(mContext, 2));
}
else{
     mRecycler.setLayoutManager(new GridLayoutManager(mContext, 4));
}
person SobaDeveloper    schedule 11.04.2015
comment
Я попробую и расскажу вам об этом. Спасибо! - person fapps; 12.04.2015
comment
Это правильное поведение для выполнения этой задачи? Спасибо - person ciccioska; 06.08.2015
comment
@ciccioska Я думаю, что папки ресурсов лучше, чем выполнять условия getConfiguration() или onConfigurationChanged. Также см. мой ответ на этот вопрос. - person TWiStErRob; 01.10.2015

Если у вас есть более одного условия или вы используете значение в нескольких местах, это может довольно быстро выйти из-под контроля. Предлагаю создать следующую структуру:

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

с res/values/dimens.xml:

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

и res/values-land/dimens.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
}
person TWiStErRob    schedule 30.09.2015

Представление Recycle View поддерживает AutofitRecycleView.

Вам нужно добавить android:numColumns="auto_fit" в ваш xml-файл.

Вы можете обратиться к этой AutofitRecycleViewLink.

person kattashri    schedule 04.05.2016
comment
Отличная реализация! поверните экран, чтобы динамически изменить количество столбцов и сохранить положение прокрутки - person Vladimir Salguero; 07.09.2016
comment
Это, пожалуй, самое идеальное решение, но не в том случае, если ваш дизайнер решит что-то другое. Иногда дизайн просто усложняет нам путь к реализации - person Stoycho Andreev; 06.03.2018

Более надежный способ определить нет. столбцов будет вычислять его на основе ширины экрана и во время выполнения. Я обычно использую для этого следующую функцию.

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
}

Это более динамичный метод для точного расчета нет. столбцов. Это будет более адаптивно для пользователей с разными размерами экрана, не ограничиваясь только двумя возможными значениями.

Примечание. Вы можете изменять значение переменной scalingFactor. Чем меньше, тем больше нет. столбцов, которые вы можете отобразить, и чем больше значение, тем меньше нет. столбцов будет рассчитано. Это коэффициент масштабирования, который можно настроить в соответствии с вашими потребностями.

person Otieno Rowland    schedule 26.06.2017

В дополнение к ответам. Это также можно сделать, используя только 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" />
person Shahbaz Hashmi    schedule 06.09.2020

В событии 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);           
    }
}
person Vladimir Salguero    schedule 19.09.2016

В итоге я обработал это в методе 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();
}

Это отлично сработало для моего приложения.

person user6731701    schedule 12.08.2017

Вы можете реализовать этот метод в вашем 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);
    }
}}

В вашем файле макета 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"/>

Затем установите переменную в ширину каждого элемента в размере файла значений папки 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));
}
person Vladimir Salguero    schedule 07.09.2016