如何根据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时自动更改