63 votes

Grille d'images dans un ScrollView

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

Une image illustrera mieux ma question :

alt text

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

Quelle est la meilleure façon d'afficher une grille contenant un nombre variable d'images, lorsque la grille n'a pas de fonction de défilement ?

Veuillez noter que la désactivation de la fonction de défilement pour le GridView ne fonctionne pas, car elle ne fait que désactiver les barres de défilement mais n'affiche pas tous les éléments.

Mise à jour : L'image ci-dessous montre ce à quoi cela ressemble avec les barres de défilement désactivées dans le GridView.

alt text

0 votes

Le nombre d'images est-il fixe ou peut-il varier ?

0 votes

Avez-vous défini Android:layout_height="wrap_content" pour le GridView lorsque vous avez essayé de désactiver sa fonctionnalité de défilement ?

0 votes

Pearsonartphoto : Le nombre d'images varie.

188voto

Neil Traft Points 6844

Oh garçon, oui, tu vas avoir des problèmes avec celui-là. Cela me rend fou que les ListViews et les GridViews ne puissent pas être étendues pour envelopper leurs enfants, parce que nous savons tous qu'elles ont des fonctionnalités plus bénéfiques en plus de leur défilement et du recyclage de leurs enfants.

Néanmoins, vous pouvez contourner ce problème ou créer votre propre disposition pour répondre à vos besoins sans trop de difficultés. Je pense à deux possibilités :

Dans ma propre application, j'ai intégré une ListView dans une ScrollView. J'ai fait cela en demandant explicitement à la ListView d'être exactement aussi haute que son contenu. Pour ce faire, je modifie les paramètres de mise en page à l'intérieur même de la fonction ListView.onMeasure() de la manière suivante :

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 à la ListView un mode de AT_MOST il crée et mesure tous ses enfants pour vous, à l'intérieur même de l'application. onMeasure (j'ai découvert cela en parcourant le code source). Espérons que le GridView se comporte de la même manière, mais si ce n'est pas le cas, vous pouvez toujours mesurer tout le contenu du GridView vous-même. Mais ce serait plus facile si vous pouviez inciter le GridView à le faire pour vous.

Maintenant, vous devez garder à l'esprit que cette solution désactiverait complètement le recyclage des vues qui rend GridView si efficace, et toutes ces ImageViews seront en mémoire même si elles ne sont pas visibles. Il en va de même avec ma prochaine solution.

L'autre possibilité est de laisser tomber le GridView et de créer votre propre mise en page. Vous pouvez étendre soit AbsoluteLayout o RelativeLayout . Par exemple, si vous étendez RelativeLayout vous pourriez placer chaque image LEFT_OF la précédente, en tenant compte de la largeur de chaque image jusqu'à ce que vous n'ayez plus de place sur cette rangée, puis commencez la rangée suivante en plaçant la première image de la nouvelle rangée BELOW l'image la plus haute de la dernière rangée. Pour centrer les images horizontalement ou les placer dans des colonnes équidistantes, vous devrez vous donner encore plus de mal. Peut-être qu'AbsoluteLayout est meilleur. Dans tous les cas, c'est assez pénible.

Bonne chance.

3 votes

Je pourrais également mentionner que si vous voulez donner à la GridView une taille fixe, et activer le défilement dans une ScrollView, cela peut se faire de la manière suivante est possible sur Android. Mais vous devrez remplacer GridView.onTouchEvent() d'appeler requestDisallowInterceptTouchEvent(true) (voir la documentation de cette petite méthode magique). Mais si vous faites cela, vous risquez de voir des incohérences avec le défilement sur 2.2 et plus, car Romain Guy realmente ne veut pas que vous ayez un défilement imbriqué ;-)

2 votes

Belle solution ! Cependant, je ne pense pas que vous souhaitiez vraiment utiliser le nom "ExpandableListView" ici, car il s'agit également d'un composant interne d'Android. Bien qu'il soit assez facile d'éviter les collisions d'espaces de noms en Java, c'est toujours un peu déroutant.

0 votes

De plus, votre code ne définit jamais la variable expansée, ce qui signifie que chaque fois que onMeasure() est appelé, il essaiera de s'étendre.

4voto

Sergey Burish Points 81

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

3voto

Edi Points 771

Vous avez deux solutions pour cela :

  1. Rédigez votre propre mise en page personnalisée. Il s'agit de la solution la plus difficile (mais elle peut ê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) 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);

El measureRealHeight() devrait ressembler à quelque chose comme 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 sous Android 1.5+.

3 votes

Cela peut devenir compliqué si vous chargez des éléments susceptibles de redimensionner la vue, comme des images. Je déteste vraiment le fait qu'il n'y ait pas de solution simple à ce problème et qu'il y ait un tas d'astuces qui ne fonctionnent pas de manière fiable.

0 votes

Fonctionne bien si vous connaissez à l'avance la taille des éléments de votre grille (l'option X sur le columnWidth ligne)

2voto

user3282666 Points 197

Créez une vue de 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 fonctionner.

1voto

Lionel T. Points 1144

J'ai trouvé un moyen de donner à la GridView une taille fixe à l'intérieur de ScrollView, et de permettre son défilement.

Pour ce faire, vous devez implémenter une nouvelle classe étendant GridView et surcharger onTouchEvent() pour appeler requestDisallowInterceptTouchEvent(true). Ainsi, la vue parent laissera la Grid intercepter les événements tactiles.

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 que vous souhaitez :

<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 il suffit de l'obtenir dans votre activité et de définir l'adaptateur, par exemple une Adaptateur de tableau<> :

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 cela vous aidera =)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X