使GridView的项目变为正方形

78

我希望我的GridView的项是正方形的。该视图有两列,项的宽度为fill_parent(即它们尽可能占用水平空间)。这些项是自定义视图。

如何使这些项的高度等于它们可变的宽度?

12个回答

105

当GridView的列被拉伸时,有一个更简单的解决方案。只需重写GridView项布局的onMeasure方法即可...

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}

12
关于1:这就是使它成为正方形的原因。 - mDroidd
2
@KarthikPalanivelu 这将用于网格项的布局,而不是GridView本身。您可以使用此代码片段扩展RelativeLayout为SquareRelativeLayout。 - jdamcd
不错的方法。当网格视图拉伸您的视图宽度(而不是高度)时,似乎是获得平方视图的正确方式。 - Alvaro Luis Bustamante
1
如果我在GridView中这样做,我会使GridView变成正方形,而不是它的子元素。 - Daniel Bo
5
可以给出完整的答案吗?你的意思是要扩展GridView吗? - StarWind0
显示剩余3条评论

56

或者,如果您想扩展一个视图(View),只需简单地执行以下操作:

public class SquareImageView extends ImageView
{

    public SquareImageView(final Context context)
    {
        super(context);
    }

    public SquareImageView(final Context context, final AttributeSet attrs)
    {
        super(context, attrs);
    }

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


    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
    {
        final int width = getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec);
        setMeasuredDimension(width, width);
    }

    @Override
    protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh)
    {
        super.onSizeChanged(w, w, oldw, oldh);
    }
}

值得注意的是,不需要使用 super.onMeasure()onMeasured 的要求是必须调用 setMeasuredDimension

2
@Chris.Jenkins,我不同意你的最后评论:如果我不调用super.onMeasure(),那么放置在其中的ImageView将不会被绘制。因此,它似乎是onMeasure方法的必要第一行。 - cyrilchampier
@nerith,这是在特定设备上吗?我已经使用这段代码很长时间了,从未遇到过问题。很想看看如何重现你所看到的问题。 - Chris.Jenkins
@Chris.Jenkins 不是在特定设备上。我的SquareFrameLayout是从xml加载的。你是通过编程方式添加子项的吗? - cyrilchampier
哦,是的,这个只适用于视图而不是视图组。也许我应该提一下。 - Chris.Jenkins
更具可重用性的解决方案,最佳答案 - FireZenk

16
我不确定目前的小部件是否可以实现这一点。你最好的选择可能是将自定义视图放在自定义的 "SquareView" 中。该视图可以只包含一个子视图,并在其 onLayout 方法被调用时强制高度等于宽度。
我从未尝试过这样的操作,但我认为这并不应该太困难。另外一种(可能稍微更容易的)解决方案是对你的自定义视图的根布局进行子类化(例如,如果它是 LinearLayout,则创建一个 SquareLinearLayout),然后将其用作容器。
编辑:这里有一个基本的实现方式,看起来对我来说可行:
package com.mantano.widget;

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

public class SquareView extends ViewGroup {

    public SquareView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int u, int r, int d) {
        getChildAt(0).layout(0, 0, r-l, d-u); // Layout with max size
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        View child = getChildAt(0);
        child.measure(widthMeasureSpec, widthMeasureSpec);
        int width = resolveSize(child.getMeasuredWidth(), widthMeasureSpec);
        child.measure(width, width); // 2nd pass with the correct size
        setMeasuredDimension(width, width);
    }
}

为了简单起见,我忽略了所有检查,但它被设计为只有一个独特的子元素。基本思路是使用GridView设置的宽度/高度参数来测量子元素(在我的情况下,它使用numColumns = 4来计算宽度),然后进行第二次遍历并使用最终尺寸,其中高度=宽度...

布局只是一个简单的布局,将独特的子元素布局在(0, 0)处,并具有所需的尺寸(从右到左,从下到上)。

这里是用于GridView项的XML:

<?xml version="1.0" encoding="utf-8"?>
<com.mantano.widget.SquareView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="wrap_content">

    <LinearLayout android:id="@+id/item" android:layout_width="fill_parent"
        android:layout_height="fill_parent" android:orientation="horizontal"
        android:gravity="center">

        <TextView android:id="@+id/text" android:layout_width="fill_parent"
            android:layout_height="wrap_content" android:text="Hello world" />
    </LinearLayout>
</com.mantano.widget.SquareView>

我在SquareView中使用LinearLayout,以便让它管理所有的重力可能性、边距等。

我不确定这个小部件在方向和尺寸变化方面的反应如何,但它似乎能够正确地工作。


这听起来都不错,唯一的问题是正确编写onLayout(或onMeasure)。目前我在ItemAdapter.getView()中设置高度/宽度,但我不喜欢这个解决方案。 - anagaf
@anagaf 我编辑了我的答案并添加了刚测试过且似乎有效的代码。它相当容易理解,因此如果您发现某些问题,应该很容易修复 ^^(不确定我是否应该编辑或发表第二个答案,如果我做错了,请原谅!) - Gregory
1
上述的布局不起作用--我不知道为什么,但子视图存在微妙的布局问题,比如TextViews不能自动换行,RelativeLayouts会混淆。不过,我在这里找到了一个相等的解决方法:https://dev59.com/XXA85IYBdhLWcg3wF_cO......虽然我还需要进行一些调整(它基于最大尺寸而不是仅宽度大小进行尺寸设置)。 - David Given

13

最终得到网格项正方形的方法实际上非常简单。

在您的网格适配器的“GetView”方法中,只需执行以下操作:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    GridView grid = (GridView)parent;
    int size = grid.getRequestedColumnWidth();

    TextView text = new TextView(getContext());
    text.setLayoutParams(new GridView.LayoutParams(size, size));

    // Whatever else you need to set on your view

    return text;
}

这是唯一对我有效的解决方案...非常感谢!! - Zeba
4
我必须将"getRequestedColumnWidth"替换为"getColumnWidth",这对我有用..谢谢! - Ayad Kara Kâhya
1
一样的问题:我不得不用getColumnWidth替换getRequestedColumnWidth,这对我起作用了...谢谢! - Yahya
对于现代用法,请确保将 new GridView.LayoutParams(size, size); 替换为 new GridView.LayoutParams(new ViewGroup.LayoutParams(size, size)); - Prajjwal Srivastav

6

我不知道这是否是最好的方法,但是我通过制作自定义视图来实现覆盖onSizeChanged的方式:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    if (getLayoutParams() != null && w != h) {
        getLayoutParams().height = w;
        setLayoutParams(getLayoutParams());
    }
}

我在LinearLayout和GridView中使用这个自定义视图,在两者中都显示为正方形!


哦,太感谢了,我已经为此奋斗了好几天了。Gregory的答案很好,除非你有很多子视图;因为一个评论指出,TextViews不会自动换行,大多数视图也不会重新调整它们的边界。但这个修复了它! - Makario

6

请尝试

<GridView
...
android:stretchMode="columnWidth" />

您还可以尝试其他模式。

6

这对我很有效!

如果像我一样,由于您的minSdk低于16而无法使用getRequestedColumnWidth(),我建议使用以下选项。

  1. 在您的fragment中:

    DisplayMetrics dm = new DisplayMetrics(); getActivity()。getWindowManager()。getDefaultDisplay()。getMetrics(dm); int size = dm.widthPixels / nbColumns; CustomAdapter adapter = new CustomAdapter(list, size, getActivity());

  2. 在您的adapter中(在getView(int position,View convertView,ViewGroup parent)中):

    v.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, size));

  3. fragment.xml:

    <GridView android:id="@+id/gridView" android:layout_width="match_parent" android:layout_height="match_parent" android:horizontalSpacing="3dp" android:verticalSpacing="3dp" android:numColumns="4" android:stretchMode="columnWidth" />

  4. custom_item.xml(例如):

    <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/picture" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" />

希望对某些人有所帮助!


5
如果您在XML中设置了静态列数,则可以将视图的宽度除以列数来确定每列的宽度。
如果使用了auto_fit,那么要获取列数会有一些难度(因为没有getColumnCount()方法)。此外,这个问题可能会对您有所帮助。
以下是我在适配器的getView(...)方法中使用的代码(使用固定列数):
    item_side = mFragment.holder.grid.getWidth() / N_COL;
    v.getLayoutParams().height = item_side;
    v.getLayoutParams().width = item_side;

4

这是我正在做的事情,用于在GridView中显示正方形单元格。我不确定你是否想要正方形单元格或其他内容。

GridView grid = new GridView( this );
grid.setColumnWidth( UIScheme.cellSize );
grid.setVerticalSpacing( UIScheme.gap );
grid.setStretchMode( GridView.STRETCH_COLUMN_WIDTH );
grid.setNumColumns( GridView.AUTO_FIT );

我需要为每个单元格创建的视图做以下操作:

cell.setLayoutParams( new GridView.LayoutParams(iconSize, iconSize) );

3

根据SteveBorkman的答案,该API适用于16+:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    GridView grid = (GridView)parent;
    int size = grid.getColumnWidth();

    if (convertView == null){
        convertView = LayoutInflater.from(context).inflate(R.layout.your_item, null);
        convertView.setLayoutParams(new GridView.LayoutParams(size, size));
    }

     //Modify your convertView here

     return convertView;
}

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