Сетка изображений внутри ScrollView - PullRequest
63 голосов
/ 24 декабря 2010

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

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

alt text

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

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

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

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

alt text

Ответы [ 7 ]

186 голосов
/ 27 декабря 2010

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

Удачи.

4 голосов
/ 25 июля 2013

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

3 голосов
/ 27 декабря 2010

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

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

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

2 голосов
/ 24 августа 2015

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

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

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

1 голос
/ 10 ноября 2014

Я нашел способ придать 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"}));

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

0 голосов
/ 27 августа 2017

Для GridView с другим View внутри сохранения ScrollView, чтобы сделать все прокрутки, перейдите по этой ссылке: 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_width="match_parent"
    android:layout_height="match_parent"
    android:numColumns="2" />

Использование:

Называйте это в своей деятельности.Если во фрагменте использовать contentView.findViewById(...).Какой контентView определен для всего макета.

ExpandedGridView gridView = (ExpandedGridView) findViewById(R.id.home_screen_list_goals);
//set data into grid view
gridView.setAdapter(YOUR_ADAPTER_OBJECT);
gridView.setExpanded(true);
0 голосов
/ 14 апреля 2016

Попробуйте это

 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();
    }
...