【问题标题】:How can I change the column count in android automatically based on the content of recyclerview?如何根据recyclerview的内容自动更改android中的列数?
【发布时间】:2020-10-13 13:11:18
【问题描述】:

我在 Android Studio 的一个项目中工作,该项目有一个问答游戏,其中有图像或文本作为答案选项,具体取决于问题(两者都只是 xml 文件中的按钮)。目前这些按钮显示在 2 列中,但如果按钮显示文本而不是图像,则列数应为 1,因为按钮大小必须更宽才能显示整个文本。

在我的适配器中,onBindViewHolder 获取答案选项列表并根据选项类型(图像/文本)设置按钮内容。对于一些测试,我创建了 RecylerView 的子类 AutofitRecyclerView 来自动计算列数,但这并没有解决我的问题。在 AutofitRecyclerView 中,我定义了一个 CenteredGridLayoutManager。此外,我尝试了 setSpanSizeLookup 方法,但这也没有帮助。

AutofitRecyclerView:

public class AutofitRecyclerView extends RecyclerView {

    private GridLayoutManager 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 CenteredGridLayoutManager(getContext(), 1);
        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);
        }
    }


    private class CenteredGridLayoutManager extends GridLayoutManager {

        public CenteredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }

        public CenteredGridLayoutManager(Context context, int spanCount) {
            super(context, spanCount);
        }

        public CenteredGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
            super(context, spanCount, orientation, reverseLayout);
        }

        @Override
        public int getPaddingLeft() {
            final int totalItemWidth = columnWidth * getSpanCount();
            if (totalItemWidth >= AutofitRecyclerView.this.getMeasuredWidth()) {
                return super.getPaddingLeft(); // do nothing
            } else {
                return Math.round((AutofitRecyclerView.this.getMeasuredWidth() / (1f + getSpanCount())) - (totalItemWidth / (1f + getSpanCount())));
            }
        }

        @Override
        public int getPaddingRight() {
            return getPaddingLeft();
        }
    }
}

按钮-XML-文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="14dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constraintLayout_imgButton"
        android:layout_width="125dp"
        android:layout_height="125dp"
        android:layout_marginBottom="0dp"
        android:layout_marginEnd="0dp"
        android:layout_marginStart="0dp"
        android:layout_marginTop="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <Button
            android:id="@+id/option_imgButton"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginStart="1dp"
            android:layout_marginTop="1dp"
            android:layout_marginEnd="1dp"
            android:layout_marginBottom="1dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:text="@string/game_choose_answer_option_button_placeholder_text"
            app:layout_constraintBottom_toBottomOf="@id/constraintLayout_imgButton"
            app:layout_constraintEnd_toEndOf="@id/constraintLayout_imgButton"
            app:layout_constraintStart_toStartOf="@id/constraintLayout_imgButton"
            app:layout_constraintTop_toTopOf="@id/constraintLayout_imgButton" />

    </androidx.constraintlayout.widget.ConstraintLayout>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constraintLayout_txtButton"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="0dp"
        android:layout_marginEnd="0dp"
        android:layout_marginStart="0dp"
        android:layout_marginTop="0dp"
        android:layout_gravity="fill_horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <Button
            android:id="@+id/option_textButton"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="1dp"
            android:layout_marginTop="1dp"
            android:layout_marginEnd="1dp"
            android:layout_marginBottom="1dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:background="@drawable/button_background"
            android:elevation="4dp"
            android:fontFamily="@font/freude"
            android:stateListAnimator="@null"
            android:text="@string/game_choose_answer_option_button_placeholder_text"
            android:textAppearance="@style/TextAppearance.Compat.Notification.Title.Media"
            android:textColor="@color/forest"
            android:textSize="20sp"
            app:layout_constraintBottom_toBottomOf="@id/constraintLayout_txtButton"
            app:layout_constraintEnd_toEndOf="@id/constraintLayout_txtButton"
            app:layout_constraintStart_toStartOf="@id/constraintLayout_txtButton"
            app:layout_constraintTop_toTopOf="@id/constraintLayout_txtButton" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Recyclerview-XML-文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="de.lmu.treeapp.activities.minigames.chooseAnswer.GameActivity_ChooseAnswer">

    <TextView
        android:id="@+id/game_description"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginTop="25dp"
        android:layout_marginEnd="20dp"
        android:layout_marginBottom="20dp"
        android:text="@string/game_description_placeholder"
        app:layout_constraintTop_toTopOf="@+id/guideline_top"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintVertical_bias="0.04" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.02" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.98" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.02" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline_mid"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.3" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline_bot"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.98" />

    <de.lmu.treeapp.activities.minigames.chooseAnswer.AutofitRecyclerView
        android:id="@+id/auto_fit_recycler_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="10dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="10dp"
        android:layout_marginBottom="20dp"
        android:clipToPadding="false"
        android:columnWidth="150dp"
        android:numColumns="auto_fit"
        app:layout_constraintBottom_toBottomOf="@id/guideline_bot"
        app:layout_constraintEnd_toEndOf="@id/guideline_right"
        app:layout_constraintStart_toStartOf="@id/guideline_left"
        app:layout_constraintTop_toBottomOf="@+id/guideline_mid" />

</androidx.constraintlayout.widget.ConstraintLayout>

我现在还没有看到什么?为 2 个不同的按钮提供 2 个不同的 xml 布局可能会更好,但我不知道如何告诉我的 recyclerView 它必须采用哪个布局 xml。

【问题讨论】:

    标签: android android-layout android-recyclerview gridlayoutmanager


    【解决方案1】:

    您可以使用框架布局并将两个按钮都放入其中,然后使两个按钮都消失。框架布局只会显示一个视图,因此如果答案是文本,则只能使文本按钮可见,否则使图像按钮可见。

    【讨论】:

    • 好的,我现在将布局更改为 frameLayout,但仍然存在图像和文本都显示在两列中而不是区分它们的问题。在我的适配器的 onBindViewHolder 中,我区分大小写并将相应的按钮设置为可见。是否有任何选项可以在那时或其他地方澄清列数? - 感谢您的帮助。
    • 我已经把框架布局的代码放在下面了。这些列现在在 AutofitRecyclerView 类中计算,其中 GridLayoutManager 用于设置 recyclerview
    【解决方案2】:

    终于找到了确定列号的方法。在我的适配器类中,我编写了一个获取当前答案选项列表的方法,如果类型是 image,则返回 true。在 AutofitRecyclerView 类中,我将方法 onMeasure 替换为以下代码,以便将 spanCount 设置为 1 或 2:

    @Override
        protected void onMeasure(int widthSpec, int heightSpec) {
            super.onMeasure(widthSpec, heightSpec);
            if (columnWidth > 0) {
                int spanCount;
                if(RecyclerViewAdapter.isImage(RecyclerViewAdapter.options)){
                    spanCount = 2;
                } else spanCount = 1;
    
                manager.setSpanCount(spanCount);
            }
        }
    

    在进行测试时,我发现在 onMeasure 方法计算新值之前为 spanCount 设置默认值非常重要。

    此外,我删除了 CenteredGridLayoutManager 并仅将其替换为 GridLayoutManager,因为我的按钮已经居中。

    谢谢@Fadman,FrameLayout 帮了我很多。在其他布局中,我的文本按钮在它们之间显示有很大的空白,因为不可见的图像按钮也在相应的容器内。

    【讨论】:

      【解决方案3】:

      按钮的布局 xml:

      <?xml version="1.0" encoding="utf-8"?>
      <FrameLayout
          xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_margin="14dp"
          android:id="@+id/option_frame_layout">
      
          <Button
              android:id="@+id/button_img"
              android:layout_width="125dp"
              android:layout_height="125dp"
              android:layout_marginStart="1dp"
              android:layout_marginTop="1dp"
              android:layout_marginEnd="1dp"
              android:layout_marginBottom="1dp"
              android:paddingLeft="10dp"
              android:paddingRight="10dp"
              android:visibility="gone"/>
      
          <Button
              android:id="@+id/button_txt"
              android:layout_width="300dp"
              android:layout_height="wrap_content"
              android:layout_marginStart="1dp"
              android:layout_marginTop="1dp"
              android:layout_marginEnd="1dp"
              android:layout_marginBottom="1dp"
              android:paddingLeft="10dp"
              android:paddingRight="10dp"
              android:background="@drawable/button_background"
              android:elevation="4dp"
              android:fontFamily="@font/freude"
              android:stateListAnimator="@null"
              android:text="@string/game_choose_answer_option_button_placeholder_text"
              android:textAppearance="@style/TextAppearance.Compat.Notification.Title.Media"
              android:textColor="@color/forest"
              android:textSize="20sp"
              android:visibility="gone" />
      </FrameLayout>
      

      两个按钮的可见性都设置为消失,并将在运行时在代码中更改。

      【讨论】:

        【解决方案4】:

        使用--> app:spanCount="3" enter image description here

        【讨论】:

          【解决方案5】:
          <androidx.recyclerview.widget.RecyclerView
              android:id="@+id/recycler_view"
              app:spanCount="3"
              app:layoutManager="StaggeredGridLayoutManager"
              android:layout_height="match_parent"
              android:layout_width="match_parent"
              android:scrollbars="vertical"
          />
          

          【讨论】:

            猜你喜欢
            • 2012-09-28
            • 2016-06-13
            • 2023-04-01
            • 1970-01-01
            • 2019-01-30
            • 2015-04-22
            • 2021-10-30
            • 2020-10-29
            • 2023-01-08
            相关资源
            最近更新 更多