如何根据recyclerview的内容自动更改android中的列数?

Posted

技术标签:

【中文标题】如何根据recyclerview的内容自动更改android中的列数?【英文标题】:How can I change the column count in android automatically based on the content of recyclerview? 【发布时间】: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_
    android:layout_
    android:layout_margin="14dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constraintLayout_imgButton"
        android:layout_
        android:layout_
        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_
            android:layout_
            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_
        android:layout_
        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_
            android:layout_
            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_
    android:layout_
    tools:context="de.lmu.treeapp.activities.minigames.chooseAnswer.GameActivity_ChooseAnswer">

    <TextView
        android:id="@+id/game_description"
        android:layout_
        android:layout_
        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_
        android:layout_
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.02" />

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

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

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

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline_bot"
        android:layout_
        android:layout_
        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_
        android:layout_
        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。

【问题讨论】:

【参考方案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_
    android:layout_
    android:layout_margin="14dp"
    android:id="@+id/option_frame_layout">

    <Button
        android:id="@+id/button_img"
        android:layout_
        android:layout_
        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_
        android:layout_
        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_
    android:layout_
    android:scrollbars="vertical"
/>

【讨论】:

以上是关于如何根据recyclerview的内容自动更改android中的列数?的主要内容,如果未能解决你的问题,请参考以下文章

具有自动布局的多行UILabel,如何在不更改标签框架的情况下根据内容调整字体大小?

如何在启用自动布局的情况下根据 UILabel 的内容自动更改 UILabel 对象的高度和下方其他元素的位置

RecyclerView项的属性在使用DataBinding时自动更改

大家好,你能帮我解决一个问题,当从 json 删除、添加或更新数据时,我的 recyclerview 应该自动更新

如何根据子 SVG 内容对齐父 div 高度

如何使用drawable在本地更改RecyclerView中的图像