Сетка изображений внутри ScrollView

Я пытаюсь создать экран с текстом и изображениями. Я хочу, чтобы изображения были выложены как сетка, как показано ниже, но я хочу, чтобы у них не было никаких функций прокрутки, кроме того, что предусмотрено окружением ScrollView.

Изображение лучше всего проиллюстрирует мой вопрос:

alt text

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

Каков наилучший способ показать сетку различного количества изображений, где сетка не имеет функций прокрутки?

Обратите внимание, что отключение функций прокрутки для GridView не работает, так как это просто отключает полосы прокрутки, но не отображает все элементы.

Обновление: На приведенном ниже изображении показано, как это выглядит, если в GridView отключены полосы прокрутки.

alt text

Ответ 1

О, мальчик, да, у тебя будут проблемы с этим. Это сводит меня с ума, что ListViews и GridViews не могут быть расширены, чтобы обернуть их детей, потому что все мы знаем, что они имеют более полезные функции в дополнение к их прокрутке и переработке своих детей.

Тем не менее, вы можете взломать это или создать свой собственный макет в соответствии с вашими потребностями без особых трудностей. С головы до ног я могу представить две возможности:

В моем собственном приложении я включил ListView в ScrollView. Я сделал это, явно указав, что 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 настолько эффективным, и все эти ImageViews будут в памяти, даже если они не видны. То же самое происходит с моим следующим решением.

Другая возможность - вырезать GridView и создать собственный макет. Вы можете расширить либо AbsoluteLayout, либо RelativeLayout. Например, если вы расширяете RelativeLayout, вы можете поместить каждое изображение LEFT_OF в предыдущее, отслеживая ширину каждого изображения до тех пор, пока не выйдете из комнаты в этой строке, а затем запустите следующую строку, поставив первую образ новой строки BELOW - самое высокое изображение последней строки. Чтобы получить изображения по горизонтали или по разнесенным столбцам, вам придется пройти еще большую боль. Может быть, AbsoluteLayout лучше. В любом случае, вид боли.

Удачи.

Ответ 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); // if you have layout margins, you have to set them too
  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 +.

Ответ 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_width="match_parent"
           android:layout_height="wrap_content"/>

Это должно работать.

Ответ 5

Я нашел способ предоставить GridView фиксированный размер внутри ScrollView и включить прокрутку.

Для этого вам нужно будет реализовать новый класс, расширяющий GridView и переопределяющий onTouchEvent() для вызова requestDisallowInterceptTouchEvent (true). Таким образом, родительский вид оставит события касания перехвата сетки.

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_width="match_parent"
android:layout_height="match_parent"
android:isScrollContainer="true" >

    <com.example.GridViewScrollable
    android:id="@+id/myGVS"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    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"}));

Я надеюсь, что это поможет =)

Ответ 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 с другим представлением внутри Save ScrollView, чтобы сделать все это прокруткой, перейдите по этой ссылке: http://www.londatiga.net/it/programming/android/make-android-listview-gridview-expandable-inside-scrollview/#comment-3967742. Это полезно и сэкономило мое время, которое я просто трачу на 5 минут, когда я никогда не знаю об этом.

Update:

Из ссылки я настроил ExpandedGridView:

package org.undp.kh.sustainabledevelopmentgoals.model;

import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.GridView;

/**
 * Created by sarithnob on 8/28/17.
 */

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;
    }
}