ScrollView 内的图像网格

Posted

技术标签:

【中文标题】ScrollView 内的图像网格【英文标题】:Grid of images inside ScrollView 【发布时间】:2011-05-30 05:53:34 【问题描述】:

我正在尝试创建一个包含文本和图像的屏幕。我希望图像像网格一样布局,如下所示,但我希望它们除了周围的 ScrollView 提供的滚动功能之外没有滚动功能。

一张图片最能说明我的问题:

<ScrollView>
    <LinearLayout>
        <ImageView />
        <TextView />
        <GridView />
        <TextView />
    </LinearLayout>
</ScrollView>

在网格没有滚动功能的情况下,显示不同数量图像的网格的最佳方法是什么?

请注意,禁用 GridView 的滚动功能不起作用,因为这只会禁用滚动条,但不会显示所有项目。

更新: 下图显示了在 GridView 中禁用滚动条后的样子。

【问题讨论】:

图片数量是固定的,还是可以变化的? 当您尝试禁用其滚动功能时,您是否为 GridView 设置了 android:layout_? Pearsonartphoto:图片数量不同。 MisterSquonk:是的,那行不通。此外,从我读到的内容来看,ScrollViews 中的 GridViews 不被认为是犹太教。例如。 Romain Guy 写道:“ListView 和 GridView 不应该相互嵌入(ScrollView 也是如此。)当我说它们不应该这样做时,不支持这样做。”我已经用屏幕截图更新了原始帖子,以显示禁用滚动条和 ndroid:layout_height 设置为“wrap_content”的样子 Romain Guy 应该在 GridView 中包含一个 addHeader()。他没有,因此,我们必须彻底破解它!! 【参考方案1】:

哦,男孩,是的,你会遇到这个问题的。让我抓狂的是 ListViews 和 GridViews 不能扩展来包裹他们的孩子,因为我们都知道他们除了滚动和回收他们的孩子之外还有更多有益的功能。

不过,您可以轻松解决这个问题或创建自己的布局以满足您的需求。在我的脑海中,我能想到两种可能性:

在我自己的应用程序中,我在 ScrollView 中嵌入了 ListView。我通过明确告诉 ListView 与其内容一样高来做到这一点。我通过更改 ListView.onMeasure() 方法中的布局参数来做到这一点,如下所示:

public class ExpandableListView extends ListView 

    boolean expanded = false;

    public ExpandableListView(Context context, AttributeSet attrs, int defaultStyle) 
        super(context, attrs, defaultStyle);
    

    public boolean isExpanded() 
        return expanded;
    

    public void setExpanded(boolean expanded) 
        this.expanded = expanded;
    

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        // HACK!  TAKE THAT ANDROID!
        if (isExpanded())          
            // Calculate entire height by providing a very large height hint.
            // View.MEASURED_SIZE_MASK represents the largest height possible.
            int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK,
                        MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);

            LayoutParams params = getLayoutParams();
            params.height = getMeasuredHeight();
         else 
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        
    

这是因为当你给 ListView 一个模式 AT_MOST 时,它会在 onMeasure 方法中为你创建和测量它的所有子视图(我通过浏览源代码发现了这一点)。希望 GridView 的行为相同,但如果不是,您仍然可以自己测量 GridView 的所有内容。但是如果你能欺骗 GridView 为你做这件事会更容易。

现在,您必须记住,此解决方案将完全禁用使 GridView 如此高效的视图回收,并且所有这些 ImageView 将在内存中,即使它们不可见。我的下一个解决方案也是如此。

另一种可能性是放弃 GridView 并创建自己的布局。您可以扩展AbsoluteLayoutRelativeLayout。例如,如果您扩展 RelativeLayout,您可以将每个图像 LEFT_OF 放置在前一个图像上,跟踪每个图像的宽度,直到您用完该行的空间,然后通过放置第一个图像开始下一行新行的图像BELOW 最后一行的最高图像。要使图像水平居中或在等间距的列中,您将不得不经历更多的痛苦。也许 AbsoluteLayout 更好。不管怎样,有点痛苦。

祝你好运。

【讨论】:

我还可能提到,如果你想给 GridView 一个固定的大小,并启用 scrolling-within-a-ScrollView,那么 在 Android 上是可能的。但是您必须覆盖GridView.onTouchEvent() 才能调用requestDisallowInterceptTouchEvent(true)(请参阅该神奇小方法的文档)。但是如果你这样做,你可能会在 2.2 及更高版本上看到滚动不一致,因为 Romain Guy 真的 不希望你有嵌套滚动。 ;-) 不错的解决方案!但是,我不认为您真的想在这里使用名称“ExpandableListView”,因为它也是一个内部 android 组件。尽管在 Java 中避免命名空间冲突很容易,但还是有点令人困惑。 另外,您的代码从不设置扩展变量,这意味着每次调用 onMeasure() 时,它都会尝试扩展自身。 这个名字对我们的实现很有意义。你在这里看到的是一个内部类。外部类根据所需的行为设置expanded 变量。我们的疯狂是有原因的。 gridview 的问题是它只占用第一列中单元格的高度。否则,它的工作原理!【参考方案2】:

可以使用带有页眉和页脚的 GridView,而不是尝试在 ScrollView 中嵌入 GridView。页眉和页脚可以是任何东西 - 文本、图像、列表等。有一个带有页眉和页脚的 GridView 示例:https://github.com/SergeyBurish/HFGridView

【讨论】:

【参考方案3】:

你有 2 个解决方案:

    编写您自己的自定义布局。这将是更难的解决方案(但可能被认为是正确的解决方案)。

    在代码中设置GridView 的实际高度。例如:

RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) myGridView.getLayoutParams(); // lp.setMargins(0, 0, 10, 10); // 如果你有布局边距,你也必须设置它们 lp.height = measureRealHeight(...); myGridView.setLayoutParams(lp);

measureRealHeight() 方法应该看起来像这样(希望我没看错):

private int measureRealHeight(...)

  final int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
  final double screenDensity = getResources().getDisplayMetrics().density;
  final int paddingLeft = (int) (X * screenDensity + 0.5f); // where X is your desired padding
  final int paddingRight = ...;
  final int horizontalSpacing = (int) (X * screenDensity + 0.5f); // the spacing between your columns
  final int verticalSpacing = ...; // the spacing between your rows
  final int columnWidth = (int) (X * screenDensity + 0.5f);             
  final int columnsCount = (screenWidth - paddingLeft - paddingRight + horizontalSpacing - myGridView.getVerticalScrollbarWidth()) / (columnWidth + horizontalSpacing);
  final int rowsCount = picsCount / columnsCount + (picsCount % columnsCount == 0 ? 0 : 1);

  return columnWidth * rowsCount + verticalSpacing * (rowsCount - 1);

以上代码应该可以在 Android 1.5+ 中运行。

【讨论】:

如果您加载的项目可能会调整视图大小,例如图像,这可能会变得混乱。我真的很讨厌没有简单的解决方案和一堆不能可靠工作的黑客。 如果您事先知道网格项目的大小(columnWidth 行上的 X),则可以正常工作【参考方案4】:

像这样创建一个不可滚动的列表视图:

public class ExpandableListView extends ListView

public ExpandableListView(Context context) 
    super(context);


public ExpandableListView(Context context, AttributeSet attrs, int defaultStyle) 
    super(context, attrs, defaultStyle);


public ExpandableListView(Context context, AttributeSet attrs) 
    super(context, attrs);


@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
            Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
    ViewGroup.LayoutParams params = getLayoutParams();
    params.height = getMeasuredHeight();
  

在你的布局文件中创建一个像这样的元素:

<com.example.ExpandableListView
           android:layout_
           android:layout_/>

这应该可行。

【讨论】:

【参考方案5】:

我找到了一种在 ScrollView 中为 GridView 提供固定大小并启用滚动的方法。

为此,您必须实现一个扩展 GridView 的新类并重写 onTouchEvent() 以调用 requestDisallowInterceptTouchEvent(true)。 这样,父视图就会离开 Grid 拦截触摸事件。

GridViewScrollable.java:

package com.example;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.GridView;

public class GridViewScrollable extends GridView 

    public GridViewAdjuntos(Context context) 
        super(context);
    

    public GridViewAdjuntos(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    public GridViewAdjuntos(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
    

    @Override
    public boolean onTouchEvent(MotionEvent ev)
        // Called when a child does not want this parent and its ancestors to intercept touch events.
        requestDisallowInterceptTouchEvent(true);
        return super.onTouchEvent(ev);
    

在你的布局中添加你想要的特性:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:isScrollContainer="true" >

    <com.example.GridViewScrollable
    android:id="@+id/myGVS"
    android:layout_
    android:layout_
    android:clickable="true"
    android:numColumns="auto_fit"
    android:columnWidth="100dp"
    android:stretchMode="columnWidth" />

</ScrollView>

只需将它放入您的活动并设置适配器,例如 ArrayAdapter

GridViewScrollable mGridView = (GridViewScrollable) findViewById(R.id.myGVS);
mGridView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new String[]"one", "two", "three", "four", "five"));

希望对你有帮助=)

【讨论】:

以上代码适合您吗?因为我正在尝试,但它不起作用。你能在这里分享示例代码吗?这对我很有帮助。 是的,它对我有用,我正在使用它。你有什么错误?不要忘记将布局定义为 ScrollView。我已经编辑了我的回复,您可以更好地理解。 请多解释“没用”。您使用的是哪种适配器?我扩展了一个包含ArrayList&lt;String&gt; (图像路径) 的 ArrayAdapter,解码并放入 ImageView。想帮助你们,但我需要更多细节。也许您应该将 GridView 设置为可点击android:clickable="true" 我没有收到任何错误,但实际上我的视图没有滚动。我正在使用 ArrayAdapter 并将 string[] 传递给它。你能用适配器取悦你的 MainActivity 吗? 我只是在我的答案中加入了一个带有 ArrayAdapter 的示例,它可以工作。我不知道发生了什么事。也许如果你把你的代码我可以提供更好的帮助。【参考方案6】:

试试这个

 public static void setGridViewHeightBasedOnChildren(GridView gridView, int columns) 
        ListAdapter listAdapter = gridView.getAdapter();
        if (listAdapter == null)
            return;
        int desiredWidth = View.MeasureSpec.makeMeasureSpec(gridView.getWidth(), View.MeasureSpec.UNSPECIFIED);
        int totalHeight = 0;
        View view = null;
        int rows = listAdapter.getCount() / columns;
        if(listAdapter.getCount() % columns> 0)
            rows++;
        
        for (int i = 0; i < rows; i++) 
            view = listAdapter.getView(i, view, gridView);
            if (i == 0)
                view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, LinearLayout.LayoutParams.WRAP_CONTENT));

            view.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
            totalHeight += view.getMeasuredHeight();
        
        ViewGroup.LayoutParams params = gridView.getLayoutParams();
        params.height = totalHeight + (gridView.getHorizontalSpacing() * rows);
        gridView.setLayoutParams(params);
        gridView.requestLayout();
    

【讨论】:

【参考方案7】:

对于 GridView 与保存 ScrollView 内的其他 View 以使其全部滚动,请转到此链接:http://www.londatiga.net/it/programming/android/make-android-listview-gridview-expandable-inside-scrollview/#comment-3967742。这很有帮助,并且节省了我的时间,我只花了 5 分钟来处理它,而我从来不知道它。

更新:

从链接中我自定义了一个 ExpandedGridView:

public class ExpandedGridView extends GridView 
    boolean expanded = false;

    public ExpandedGridView(Context context) 
        super(context);
    

    public ExpandedGridView(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    public ExpandedGridView(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
    

    public boolean isExpanded() 
        return expanded;
    

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        // HACK! TAKE THAT ANDROID!
        if (isExpanded()) 
            // Calculate entire height by providing a very large height hint.
            // But do not use the highest 2 bits of this integer; those are
            // reserved for the MeasureSpec mode.
            int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
            ViewGroup.LayoutParams params = getLayoutParams();
            params.height = getMeasuredHeight();
         else 
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        
    

    public void setExpanded(boolean expanded) 
        this.expanded = expanded;
    

对于您的 xml 从 GridView 更改为已自定义的 ExpandedGridView。

<com.your.package.ExpandedGridView
    android:id="@+id/home_screen_list_goals"
    android:layout_
    android:layout_
    android:numColumns="2" />

用法:

在您的活动中调用它。如果在片段中使用contentView.findViewById(...)。哪个 contentView 是您定义的整个布局。

ExpandedGridView gridView = (ExpandedGridView) findViewById(R.id.home_screen_list_goals);
//set data into grid view
gridView.setAdapter(YOUR_ADAPTER_OBJECT);
gridView.setExpanded(true);

【讨论】:

虽然该链接可能对某些用户有所帮助,但最好在您的帖子中包含一个简短的摘要,以防该链接将来不再有效。然后人们只会得到 404,这根本没有帮助。 好的,谢谢!我更新了答案。请为得到答案帮助的人+1。谢谢!

以上是关于ScrollView 内的图像网格的主要内容,如果未能解决你的问题,请参考以下文章

Swift - Parallax Header View - ScrollView 重叠单元格

SwiftUI 中的 ForEach

WatchOs 上不显示 ScrollView 内的列表

如何使网格内的图像控件居中?

在 SwiftUI 中,如何通过动态变化的滚动区域来缩放 scrollView 内的内容?

在另一个类的滚动视图中添加图像?