安卓中网格布局自适应屏幕大小

15

我创建了一个包含大量文本的网格,我希望文本能够根据屏幕大小自适应调整。我尝试过以下代码:

    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
    float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
    int noOfColumns = (int) (dpWidth / 50);
    return noOfColumns;

我希望输出类似于这样的东西

image2

imag2它并不像我所需的那样工作。请帮忙。谢谢。


这段代码有什么问题? - Vyacheslav
我想要自适应但它没有执行自适应。@Vyacheslav - Anusha
你的意思是你无法计算字体的高度,对吗? - Vyacheslav
不,我只是希望栅格项能够根据屏幕大小进行适配。@Vyacheslav - Anusha
您的意思是宽屏应该有更多的列,还是说同样数量的列,只是它们应该拉伸以填满屏幕? - lionscribe
是的,我希望在宽屏幕上有更多的列。它应该拉伸以填满屏幕。我想在网格布局中实现。@lionscribe - Anusha
7个回答

6
这里有一个自定义实现的GridLayout,可以满足您的需求:AutoGridLayout
public class AutoGridLayout extends GridLayout {

    private int defaultColumnCount;
    private int columnWidth;

    public AutoGridLayout(Context context) {
        super(context);
        init(null, 0);
    }

    public AutoGridLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public AutoGridLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs, defStyleAttr);
    }

    private void init(AttributeSet attrs, int defStyleAttr) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.AutoGridLayout, 0, defStyleAttr);
        try {
            columnWidth = a.getDimensionPixelSize(R.styleable.AutoGridLayout_columnWidth, 0);

            int[] set = { android.R.attr.columnCount /* id 0 */ };
            a = getContext().obtainStyledAttributes(attrs, set, 0, defStyleAttr);
            defaultColumnCount = a.getInt(0, 10);
        } finally {
            a.recycle();
        }

        /* Initially set columnCount to 1, will be changed automatically later. */
        setColumnCount(1);
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        super.onMeasure(widthSpec, heightSpec);

        int width = MeasureSpec.getSize(widthSpec);
        if (columnWidth > 0 && width > 0) {
            int totalSpace = width - getPaddingRight() - getPaddingLeft();
            int columnCount = Math.max(1, totalSpace / columnWidth);
            setColumnCount(columnCount);
        } else {
            setColumnCount(defaultColumnCount);
        }
    }
}

只需像这样在您的XML布局文件中添加:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="match_parent">

    <com.km.myproject.customview.AutoGridLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:columnCount="5"
        app:columnWidth="50dp"/>

</FrameLayout>

使用columnWidth会尝试计算可以容纳多少列并自动设置最佳跨度计数。如果未使用(或由于某些原因无法测量),则将使用columnCount属性。
希望这能帮到你!

这在使用卡片视图的RecyclerView上可以工作吗?需要遵循哪些步骤?好工作! - Fo Nko
1
我找不到R.styleable.AutoGridLayout和R.styleable.AutoGridLayout_columnWidth。 - David Kariuki
请将以下内容添加到attrs.xml文件中:<declare-styleable name="AutoGridLayout"> <attr name="columnWidth" format="dimension" /> </declare-styleable> - HPage

4
使用 FlexboxLayout 布局和 RecyclerView 来处理这种类型的布局
RecyclerView recyclerView = (RecyclerView) context.findViewById(R.id.recyclerview);
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(context);
layoutManager.setFlexDirection(FlexDirection.COLUMN);
layoutManager.setJustifyContent(JustifyContent.FLEX_START);
recyclerView.setLayoutManager(layoutManager);

更多内容请查看FlexboxLayout

FlexboxLayout也能像Gallery中的图片一样处理不同宽度和高度的视图。


2
这是一个实现网格布局的代码,可以为您完成所有计算。它将所有子视图放置在等间距和等边距的网格中。它还优化了列数,以避免所有行都尽可能地填满(例如,9个子视图可以放在3行上,分别为4,4,1,但是3,3,3看起来更好)。所有内容都是动态的,所以不用担心横屏/竖屏/手机/平板电脑/电视。
package .......;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/* A labor-saving layout
/  dynamically places all children on an equally-spaced grid.
   All children get the width/height of the largest child - so they all look similar
 */
public class AutoGridLayout extends ViewGroup
{

    private int mMaxHeight;
    private int mMaxWidth;

    public AutoGridLayout(Context context)
    {
        super(context);
    }

    public AutoGridLayout(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }

    public AutoGridLayout(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean shouldDelayChildPressedState()
    {
        return false;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        int count = getChildCount();

        mMaxHeight = 0;
        mMaxWidth = 0;
        int childState = 0;

        for (int i = 0; i < count; i++)
        {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE)
            {
                measureChild(child, widthMeasureSpec,  heightMeasureSpec);

                mMaxWidth = Math.max(mMaxWidth, child.getMeasuredWidth());
                mMaxHeight = Math.max(mMaxHeight, child.getMeasuredHeight());
            }
        }

        mMaxHeight = Math.max(mMaxHeight, getSuggestedMinimumHeight());
        mMaxWidth = Math.max(mMaxWidth, getSuggestedMinimumWidth());

        setMeasuredDimension(resolveSizeAndState(mMaxWidth, widthMeasureSpec, childState),
                             resolveSizeAndState(mMaxHeight, heightMeasureSpec,
                                                 childState << MEASURED_HEIGHT_STATE_SHIFT));
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom)
    {
        final float pxWidth = (right - left + 1);
        final float pxHeight  = (1 + bottom - top);
        final int totalChildCount = getChildCount();
        int count = 0;
        for (int i = 0; i < totalChildCount; i++)
        {
            if (getChildAt(i).getVisibility() != GONE)
                count++;
        }

        final float minSpacing = pxWidth / 20;
        final int maxPossibleColumns = (int) (pxWidth / (mMaxWidth + minSpacing));
        final int rows = (int)Math.ceil((float)count / maxPossibleColumns);
        final int voidsAtLastRow = (rows * maxPossibleColumns - count);
        // distribute the voids to get an even distribution is possible
        final int nColumns = maxPossibleColumns - voidsAtLastRow / rows;

        // equal spaces as margins and between childs (#spaces = #child + 1)
        final int xSpace = (int)Math.max(0,(pxWidth - nColumns * mMaxWidth) / (nColumns + 1));
        final int ySpace = (int)Math.max(0,(pxHeight - rows * mMaxHeight) / (rows + 1));

        int n = 0;
        for (int i = 0; i < totalChildCount; i++)
        {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE)
            {
                final int col = n % nColumns;
                final int x = xSpace + (xSpace + mMaxWidth) * col;

                final int row = n / nColumns;
                final int y = ySpace + (ySpace + mMaxHeight) * row;

                // Place the child.
                child.layout(x, y,x+mMaxWidth,y+mMaxHeight);
                n++;
            }
        }
    }

}

Sample


0

请按照以下步骤操作:

  • 在onCreate或onCreateView中使用您的代码计算列数。请注意列之间的间距。

  • 使用上述计算结果调用GridLayout的setColumnCount方法。

  • 在xml文件中,将android:layout_columnWeight="1"属性添加到GridLayout中的所有项目。这将导致API 21及以上版本中的列拉伸。

  • 在API 21之前的版本中,您可以将GridView本身水平居中,以使其看起来更好。或者更好的方法是,计算首选列宽(gridWidth / columnCount),并迭代网格(ViewGroup)中的所有项目,并将它们的宽度设置为columnWidth。


0

使用GridLayout实现您的需求可能会很困难:

  • 尽管您可以从DisplayMetrics读取屏幕宽度,但该方法与支持分屏的新版本Android不兼容。您需要父视图的宽度,而不是整个屏幕的宽度。

  • 即使假设显示度量为您提供了可靠的用于显示活动的窗口的宽度,只要将布局放在另一个片段旁边的片段中,此逻辑就会中断。创建如此不灵活的东西是没有意义的。

  • 父视图的宽度在布局完成之前是未知的。这并不意味着无法使用,只是有点棘手。

相反,简单的方法是使用GridView,因为它具有autofit选项来设置列数,更适合此目的。


实际上,看起来 getMetrics 返回的是拆分窗口的虚拟大小,因此它会很好地工作。要获取完整屏幕的尺寸,您可以使用 getRealMetrics - lionscribe
谢谢你的建议,我会相信你的话。但我仍然担心RemixOS、三星和其他硬件公司在皮肤方面使用的各种方案,以及在我没有测试过的某些设备上可能发生的情况。 - x-code
实际上我自己从来没有测试过,我只是引用了https://dev59.com/kJbfa4cB1Zd3GeqPqTQ_上的内容。 - lionscribe
实际上,我想在网格内实现拖放功能。我尝试使用 Grid view 的自适应功能,但是出现了错误。请查看我的问题:http://stackoverflow.com/questions/39434558/java-lang-unsupportedoperationexception-addviewview-int-is-not-supported-in。因此,我尝试使用 Grid Layout 实现相同的功能。@lionscribe - Anusha
我尝试了网格视图,但是遇到了其他错误。请看一下我的错误信息:http://stackoverflow.com/questions/39434558/java-lang-unsupportedoperationexception-addviewview-int-is-not-supported-in @x-code - Anusha
根据您提出的另一个问题,您需要支持拖放功能。既然这样,您是正确的,网格视图不是一个好选择。很抱歉我没有更简单的答案。 - x-code

0

-1

对于GridLayout布局

 <?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:columnCount="4"
    android:orientation="horizontal" >
    <Button android:text="1" />
    <Button android:text="2" />
    <Button android:text="3" />
    <Button android:text="4" />
    <Button android:text="5" />
    <Button android:text="6" />
    <Button android:text="7" />
    <Button android:text="8" />
    <Button android:text="9" />
    <Button android:text="10" />
    <Button android:text="11" />
    <Button android:text="12" />
    <Button android:text="13" />
    <Button android:text="14" />
    <Button android:text="15" />
    <Button android:text="16" />
</GridLayout>

编程方式:

GridLayout gridLayout = new GridLayout(this);
gridLayout .setColumnCount(4);
///...

一个例子:https://dev59.com/dmYr5IYBdhLWcg3wnrd0#14715333

使用此方法来调整GridView:

private void adjustGridView() {
  gv.setNumColumns(GridView.AUTO_FIT);
  gv.setColumnWidth(70);
}

我们能在网格布局中实现相同的功能吗?因为我在网格布局中找不到auto_fit函数。@Vyacheslav - Anusha
1
我想让列自适应宽度,而不是硬编码的值。@Vyacheslav - Anusha
我不想像你一样指定列数,而是想要自动适应。如何在gridlayout中设置为auto_fit?@Vyacheslav - Anusha
它们在网格视图中自适应了,但是网格布局怎么办?我已经参考了所有这些链接,但没有帮助。@Vyacheslav - Anusha
看一下我给的网址。 - Vyacheslav
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接