web-dev-qa-db-fra.com

Grille d'images à l'intérieur de ScrollView

J'essaie de créer un écran avec du texte et des images. Je souhaite que les images soient disposées comme une grille, comme indiqué ci-dessous, mais je ne souhaite pas qu'elles aient une fonctionnalité de défilement autre que celle fournie par ScrollView. 

Une image illustrera le mieux ma question:

alt text

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

Quel est le meilleur moyen de faire apparaître une grille d'un nombre variable d'images, où la grille n'a pas de fonctionnalité de défilement?

Veuillez noter que la désactivation de la fonctionnalité de défilement pour GridView ne fonctionne pas, car elle désactive simplement les barres de défilement mais n’affiche pas tous les éléments.

Mise à jour: L'image ci-dessous montre son apparence avec les barres de défilement désactivées dans GridView.

alt text

63
hanspeide

Oh mon Dieu, ouais, tu vas avoir des problèmes avec celui-ci. Cela me rend fou que ListViews et GridViews ne peuvent pas être étendus pour envelopper leurs enfants, car nous savons tous qu'ils ont des fonctionnalités plus bénéfiques en plus du défilement et du recyclage de leurs enfants.

Néanmoins, vous pouvez y remédier ou créer votre propre mise en page pour répondre à vos besoins sans trop de difficulté. De mémoire, je peux penser à deux possibilités:

Dans ma propre application, j'ai intégré un ListView à un ScrollView. J'ai fait cela en disant explicitement à ListView d'être exactement aussi haut que son contenu. Je le fais en modifiant les paramètres de présentation directement dans la méthode ListView.onMeasure() comme suit:

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

Cela fonctionne parce que lorsque vous donnez à ListView un mode AT_MOST, il crée et mesure tous ses enfants pour vous, directement dans la méthode onMeasure (j'ai découvert cela en parcourant le code source). Espérons que GridView se comporte de la même manière, mais si ce n’est pas le cas, vous pouvez quand même mesurer tout le contenu de GridView. Mais ce serait plus facile si vous pouviez persuader GridView de le faire pour vous.

N'oubliez pas que cette solution désactiverait complètement le recyclage des vues, ce qui rend GridView si efficace, et toutes ces ImageViews seront en mémoire, même si elles ne sont pas visibles. Même chose avec ma prochaine solution.

L'autre possibilité consiste à abandonner le GridView et à créer votre propre mise en page. Vous pouvez prolonger AbsoluteLayout ou RelativeLayout. Par exemple, si vous étendez RelativeLayout, vous pouvez placer chaque image LEFT_OF la précédente en gardant une trace de la largeur de chaque image jusqu’à épuisement de la rangée, puis commencer par la rangée suivante en plaçant la première image de la nouvelle. row BELOW la plus haute image de la dernière ligne. Pour que les images soient centrées horizontalement ou dans des colonnes équidistantes, vous devrez endurer encore plus de douleur. Peut-être qu'AbsoluteLayout est mieux. De toute façon, un peu pénible.

Bonne chance.

186
Neil Traft

Un GridView avec en-tête et pied de page peut être utilisé au lieu d’essayer d’incorporer GridView dans ScrollView. En-tête et pied de page peuvent être n'importe quoi - textes, images, listes, etc. Il existe un exemple de GridView avec en-tête et pied de page: https://github.com/SergeyBurish/HFGridView

4
Sergey Burish

Vous avez 2 solutions pour celle-ci:

  1. Écrivez votre propre mise en page personnalisée. Ce serait la solution la plus difficile (mais pourrait être considérée comme la bonne).

  2. Définissez la hauteur réelle de votre GridView dans le code. Par exemple:

 RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) monGridView.getLayoutParams (); .__ // lp.setMargins (0, 0, 10, 10); // Si vous avez des marges de présentation, vous devez également les définir 
 lp.height = measureRealHeight (...); 
 myGridView.setLayoutParams (lp); 

La méthode measureRealHeight() devrait ressembler à ceci (j'espère que j'ai bien compris):

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

Le code ci-dessus devrait fonctionner dans Android 1.5+.

3
Edi

Créez une liste non défilable comme ceci:

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

Dans votre fichier de mise en page, créez un élément comme celui-ci:

<com.example.ExpandableListView
           Android:layout_width="match_parent"
           Android:layout_height="wrap_content"/>

Cela devrait marcher.

2
user3282666

J'ai trouvé un moyen de donner à GridView une taille fixe dans ScrollView et de l'activer pour le faire défiler.

Pour ce faire, vous devez implémenter une nouvelle classe qui étend GridView et remplacer onTouchEvent () pour appeler requestDisallowInterceptTouchEvent (true) . Ainsi, la vue parent laissera les événements tactiles d'interception de grille.

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

Ajoutez-le dans votre mise en page avec les caractéristiques souhaitées:

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

Et insérez-le simplement dans votre activité et définissez l'adaptateur, par exemple un 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"}));

J'espère que ça aide =)

1
Lionel T.

Essaye ça

 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();
    }
0
Roman Mosiienko

Pour que GridView avec une autre vue à l'intérieur de la sauvegarde ScrollView fasse défiler tout, allez à ce lien: http://www.londatiga.net/it/programming/Android/make-Android-listview-gridview-expandable-inside -scrollview/# comment-3967742 . C'est utile et j'ai économisé du temps que je viens de passer 5 minutes avec cela quand je ne l'ai jamais su.

Mettre à jour:

À partir du lien que j'ai personnalisez un 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;
    }
}

Pour votre changement XML, passez de GridView à ExpandedGridView qui ont été personnalisés.

<com.your.package.ExpandedGridView
    Android:id="@+id/home_screen_list_goals"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:numColumns="2" />

Usage:

Appelez cela dans votre activité. Si fragmenté, utilisez contentView.findViewById(...). Quel contentView est votre mise en page entière définie.

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

0
Sarith NOB