Gridview avec deux colonnes et des images redimensionnées automatiquement

181

J'essaye de faire une grille avec deux colonnes. Je veux dire deux photos par rangée côte à côte, tout comme cette image.

entrez la description de l'image ici

Mais mes photos ont des espaces entre elles, car elles ne sont pas de la même taille. Voici ce que je reçois.

entrez la description de l'image ici

comme vous pouvez le voir, la première image cache la légende qui montre le nom et le numéro de téléphone du contact. et les autres images ne sont pas étirées correctement.

Voici mon fichier xml GridView . Comme vous pouvez le voir, le columnWidthparamètre est réglé sur 200dp . Je voudrais que ce soit automatique , donc les images se redimensionneront automatiquement pour chaque taille d'écran.

<?xml version="1.0" encoding="utf-8"?>
<GridView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridViewContacts"
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"
    android:numColumns="2"
    android:columnWidth="200dp"
    android:stretchMode="columnWidth"    
    android:gravity="center" />

et voici le fichier xml de l'élément, qui représente chaque élément lui-même.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/imageViewContactIcon"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY" />

    <LinearLayout
        android:id="@+id/linearlayoutContactName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingLeft="5dp"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
        android:background="#99000000"
        android:layout_alignBottom="@+id/imageViewContactIcon">

        <TextView
            android:id="@+id/textViewContactName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FFFFFF"
            android:textStyle="bold"
            android:textSize="15sp"
            android:text="Lorem Ipsum" />       

        <TextView
            android:id="@+id/textViewContactNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FFFFFF"
            android:layout_marginLeft="5dp"
            android:focusable="true"
            android:ellipsize="marquee"
            android:marqueeRepeatLimit="marquee_forever"
            android:textSize="10sp"
            android:text="123456789" />

    </LinearLayout>

</RelativeLayout>

Donc ce que je veux, c'est montrer deux images par ligne, et les images sont redimensionnées automatiquement, quelle que soit la taille de l'écran. Qu'est-ce que je fais mal sur ma mise en page?

Merci.

rogcg
la source
1
Vous devriez peut-être essayer d'encapsuler ImageView dans son propre LinearLayout. De cette façon, vous pouvez configurer la balise LinearLayout exactement comme vous le souhaitez, puis demander simplement à ImageView de la remplir.
dani
@dani qu'est-ce que tu veux dire? je peux toujours configurer l'ImageView exactement comme je veux. pouvez-vous me donner un exemple?
rogcg
Je pense que vous devez créer des pouces à partir d'images, qui sont toutes identiques, ex 100x100 ou tout ce dont vous avez besoin. Si vous pouvez voir sur votre image d'exemple, l'effet que vous recherchez est d'afficher seulement une partie de l'image. Et dans votre projet, vous affichez simplement l'image originale, ce qui est faux si vous devez créer une galerie. Essayez de chercher des pouces sur le site Web de développement Android :)
Vasil Valchev
@VasilValchev mais le problème est que, même si je crée des pouces (par exemple 100x100), je vais devoir le redimensionner pour différentes tailles d'écran. J'utilise déjà la scaleTypepropriété centerCrop sur ImageView. Ce que je dois réaliser, c'est un moyen de faire les images côte à côte et de les redimensionner automatiquement pour différentes tailles d'écran.
rogcg
Vous pouvez toujours obtenir la taille de l'écran en code blanc horizontal et la diviser simplement par / 2. Si vous pouvez voir dans votre projet que le problème est en hauteur, si vous savez quel est exactement votre poids, vous pouvez toujours créer un rectangle parfait. Je ne vois pas où est le problème?
Vasil Valchev

Réponses:

403

Voici une méthode relativement simple pour ce faire. Jetez un GridView dans votre mise en page, définissez le mode d'étirement pour étirer les largeurs de colonne, définissez l'espacement sur 0 (ou ce que vous voulez) et définissez le nombre de colonnes sur 2:

res / layout / main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <GridView
        android:id="@+id/gridview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:verticalSpacing="0dp"
        android:horizontalSpacing="0dp"
        android:stretchMode="columnWidth"
        android:numColumns="2"/>

</FrameLayout>

Créez une personnalisation ImageViewqui conserve ses proportions:

src / com / exemple / graphicstest / SquareImageView.java

public class SquareImageView extends ImageView {
    public SquareImageView(Context context) {
        super(context);
    }

    public SquareImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()); //Snap to width
    }
}

Créez une disposition pour un élément de grille à l'aide de ce SquareImageView et définissez le scaleType sur centerCrop:

res / layout / grid_item.xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <com.example.graphicstest.SquareImageView
        android:id="@+id/picture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="15dp"
        android:paddingBottom="15dp"
        android:layout_gravity="bottom"
        android:textColor="@android:color/white"
        android:background="#55000000"/>

</FrameLayout>

Maintenant, fabriquez une sorte d'adaptateur pour votre GridView:

src / com / exemple / graphicstest / MyAdapter.java

private final class MyAdapter extends BaseAdapter {
    private final List<Item> mItems = new ArrayList<Item>();
    private final LayoutInflater mInflater;

    public MyAdapter(Context context) {
        mInflater = LayoutInflater.from(context);

        mItems.add(new Item("Red",       R.drawable.red));
        mItems.add(new Item("Magenta",   R.drawable.magenta));
        mItems.add(new Item("Dark Gray", R.drawable.dark_gray));
        mItems.add(new Item("Gray",      R.drawable.gray));
        mItems.add(new Item("Green",     R.drawable.green));
        mItems.add(new Item("Cyan",      R.drawable.cyan));
    }

    @Override
    public int getCount() {
        return mItems.size();
    }

    @Override
    public Item getItem(int i) {
        return mItems.get(i);
    }

    @Override
    public long getItemId(int i) {
        return mItems.get(i).drawableId;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        View v = view;
        ImageView picture;
        TextView name;

        if (v == null) {
            v = mInflater.inflate(R.layout.grid_item, viewGroup, false);
            v.setTag(R.id.picture, v.findViewById(R.id.picture));
            v.setTag(R.id.text, v.findViewById(R.id.text));
        }

        picture = (ImageView) v.getTag(R.id.picture);
        name = (TextView) v.getTag(R.id.text);

        Item item = getItem(i);

        picture.setImageResource(item.drawableId);
        name.setText(item.name);

        return v;
    }

    private static class Item {
        public final String name;
        public final int drawableId;

        Item(String name, int drawableId) {
            this.name = name;
            this.drawableId = drawableId;
        }
    }
}

Réglez cet adaptateur sur votre GridView:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    GridView gridView = (GridView)findViewById(R.id.gridview);
    gridView.setAdapter(new MyAdapter(this));
}

Et profitez des résultats:

Exemple GridView

Kevin Coppock
la source
3
ça a marché! mais j'aimerais que vous m'expliquiez la nécessité de créer une classe pour le ImageViewet pourquoi utiliser quelque chose comme ça <com.example.graphicstest.SquareImageView>au lieu d'une simple <ImageView>balise dans le fichier xml . Pourriez-vous m'expliquer plus sur ce rapport hauteur / largeur et comment ne pas créer cette classe affecte les éléments. y a-t-il un moyen d'optimiser cela, sans avoir besoin de la ImageViewclasse personnalisée ? merci pour votre aide de toute façon.
rogcg
10
Fondamentalement, dans la classe ImageView d'Android, il n'y a aucun moyen de spécifier simplement "hé, gardez un rapport hauteur / largeur carré (largeur / hauteur) pour cette vue", sauf si vous codez en dur la largeur et la hauteur. Vous pouvez faire un ajustement manuel de LayoutParams dans le getView de l'adaptateur, mais franchement, il est beaucoup plus simple de laisser ImageView gérer toutes les mesures, et de remplacer simplement les résultats pour dire "Quelle que soit la largeur finit par être, faites que ma hauteur reste la même". Vous n'avez jamais à y penser, c'est toujours carré et cela fonctionne comme prévu. En gros, c'est le moyen le plus simple de garder la vue carrée.
Kevin Coppock
8
Je ne peux pas penser à une raison pour éviter de créer la classe ImageView personnalisée, car vous pouvez faire tout ce que vous pouvez avec un ImageView standard, mais cela vous évite simplement d'avoir à faire des mesures, à instancier LayoutParams et à vérifier partout .
Kevin Coppock
1
Merci pour l'édition, j'ai pensé que ça pourrait être ça, ça marche comme un charme!
Zander
1
@kcoppock: Merci beaucoup d'avoir partagé ce code. Ce que j'aime le plus, c'est que vous pouvez remplacer les couleurs de l'image vue par des images "réelles" et les redimensionner correctement! Ce que je voudrais comprendre, c'est s'il est possible de redimensionner les vues de sorte qu'un certain nombre de vues remplissent tout l'écran.
AntonSack
14

une autre approche simple avec des éléments intégrés modernes comme PercentRelativeLayout est maintenant disponible pour les nouveaux utilisateurs qui rencontrent ce problème. merci à l'équipe Android pour la publication de cet article.

<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
app:layout_widthPercent="50%">

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/picture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="#55000000"
        android:paddingBottom="15dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="15dp"
        android:textColor="@android:color/white" />

</FrameLayout>

et pour de meilleures performances, vous pouvez utiliser des éléments comme le chargeur d'images Picasso qui vous aident à remplir toute la largeur de chaque image parent. par exemple dans votre adaptateur, vous devez utiliser ceci:

int width= context.getResources().getDisplayMetrics().widthPixels;
    com.squareup.picasso.Picasso
            .with(context)
            .load("some url")
            .centerCrop().resize(width/2,width/2)
            .error(R.drawable.placeholder)
            .placeholder(R.drawable.placeholder)
            .into(item.drawableId);

maintenant, vous n'avez plus besoin de la classe CustomImageView.

PS, je recommande d'utiliser ImageView à la place de Type Int dans la classe Item.

espérons que cette aide ..

Setmax
la source
Avec l'API 26.1, cela est devenu obsolète. Voir le lien
Dominikus K.