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

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

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

альтернативный текст

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

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

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

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

альтернативный текст

7 ответов

Решение

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

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

В моем собственном приложении я встроил 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 лучше. В любом случае, какая-то боль.

Удачи.

GridView с верхним и нижним колонтитулами можно использовать вместо попытки встроить GridView в ScrollView. Верхний и нижний колонтитулы могут быть любыми: тексты, изображения, списки и т. Д. Существует пример GridView с верхним и нижним колонтитулами: https://github.com/SergeyBurish/HFGridView

У вас есть 2 решения для этого:

  1. Напишите свой собственный макет. Это будет более сложным решением (но может считаться правильным).

  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+.

Создайте не прокручиваемый вид списка следующим образом:

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"/>

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

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

Для этого вам нужно будет реализовать новый класс, расширяющий 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_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"}));

Надеюсь это поможет =)

Чтобы GridView с другим View внутри сохраненного ScrollView сделал все это прокручивающимся, перейдите по этой ссылке: http://www.londatiga.net/it/programming/android/make-android-listview-gridview-expandable-inside-scrollview/. Это полезно и сэкономило мое время, которое я потратил на это 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_width="match_parent"
    android:layout_height="match_parent"
    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);

Попробуй это

 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();
    }
Другие вопросы по тегам