web-dev-qa-db-fra.com

Rapport d'aspect fixe Voir

Comment dois-je procéder pour implémenter un rapport hauteur/largeur fixe View? J'aimerais avoir des éléments avec un rapport d'aspect de 1: 1 dans un GridView. Je pense qu'il vaut mieux sous-classer les enfants que les GridView?

EDIT: Je suppose que cela doit être fait par programme, ce n'est pas un problème. De plus, je ne veux pas limiter la taille, seulement le rapport d'aspect.

41
Jani

J'ai implémenté FixedAspectRatioFrameLayout, donc je peux le réutiliser et avoir n'importe quelle vue hébergée avec un rapport d'aspect fixe:

public class FixedAspectRatioFrameLayout extends FrameLayout
{
    private int mAspectRatioWidth;
    private int mAspectRatioHeight;

    public FixedAspectRatioFrameLayout(Context context)
    {
        super(context);
    }

    public FixedAspectRatioFrameLayout(Context context, AttributeSet attrs)
    {
        super(context, attrs);

        init(context, attrs);
    }

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

        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs)
    {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FixedAspectRatioFrameLayout);

        mAspectRatioWidth = a.getInt(R.styleable.FixedAspectRatioFrameLayout_aspectRatioWidth, 4);
        mAspectRatioHeight = a.getInt(R.styleable.FixedAspectRatioFrameLayout_aspectRatioHeight, 3);

        a.recycle();
    }
    // **overrides**

    @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
    {
        int originalWidth = MeasureSpec.getSize(widthMeasureSpec);

        int originalHeight = MeasureSpec.getSize(heightMeasureSpec);

        int calculatedHeight = originalWidth * mAspectRatioHeight / mAspectRatioWidth;

        int finalWidth, finalHeight;

        if (calculatedHeight > originalHeight)
        {
            finalWidth = originalHeight * mAspectRatioWidth / mAspectRatioHeight; 
            finalHeight = originalHeight;
        }
        else
        {
            finalWidth = originalWidth;
            finalHeight = calculatedHeight;
        }

        super.onMeasure(
                MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY), 
                MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY));
    }
}
76
TalL

Pour les nouveaux utilisateurs, voici une meilleure solution non-code:

Une nouvelle bibliothèque de support appelée ( Bibliothèque de support en pourcentage est disponible dans Android SDK v22 (MinAPI est 7 me pense, pas sûr):

src: Android-developers.blogspot.in

La bibliothèque de prise en charge de pourcentage fournit des dimensions et des marges basées sur un pourcentage et, nouveau dans cette version, la possibilité de définir un rapport hauteur/largeur personnalisé via app: aspectRatio. En ne définissant qu'une seule largeur ou hauteur et en utilisant aspectRatio, PercentFrameLayout ou PercentRelativeLayout ajustera automatiquement l'autre dimension afin que la mise en page utilise un rapport d'aspect défini.

Pour l'inclure, ajoutez ceci à votre build.gradle:

compile 'com.Android.support:percent:23.1.1'

Enveloppez maintenant votre vue (celle qui doit être carrée) avec un PercentRelativeLayout / PercentFrameLayout :

<Android.support.percent.PercentRelativeLayout
         Android:layout_width="match_parent"
         Android:layout_height="wrap_content">
     <ImageView
         app:layout_aspectRatio="100%"
         app:layout_widthPercent="100%"/>
 </Android.support.percent.PercentRelativeLayout>

Vous pouvez voir un exemple ici .

38
ShahiM

Pour ne pas utiliser de solution tierce et compte tenu du fait que PercentFrameLayout et PercentRelativeLayout ont été dépréciés dans 26.0.0, je vous suggère d'envisager d'utiliser ConstraintLayout comme disposition racine pour vos éléments de grille.

Votre item_grid.xml pourrait ressembler à:

<Android.support.constraint.ConstraintLayout
    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="wrap_content">

    <ImageView
        Android:id="@+id/imageview_item"
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        Android:scaleType="centerCrop"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintDimensionRatio="H,1:1" />

</Android.support.constraint.ConstraintLayout>

En conséquence, vous obtenez quelque chose comme ceci:

Fixed ratio sized grid items

19
Eugene Brusov

J'ai récemment créé une classe d'aide pour ce problème et j'ai écrit un blog à ce sujet .

La viande du code est la suivante:

/**
 * Measure with a specific aspect ratio<br />
 * <br />
 * @param widthMeasureSpec The width <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method
 * @param heightMeasureSpec The height <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method
 * @param aspectRatio The aspect ratio to calculate measurements in respect to 
 */
public void measure(int widthMeasureSpec, int heightMeasureSpec, double aspectRatio) {
    int widthMode = MeasureSpec.getMode( widthMeasureSpec );
    int widthSize = widthMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : MeasureSpec.getSize( widthMeasureSpec );
    int heightMode = MeasureSpec.getMode( heightMeasureSpec );
    int heightSize = heightMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : MeasureSpec.getSize( heightMeasureSpec );

    if ( heightMode == MeasureSpec.EXACTLY && widthMode == MeasureSpec.EXACTLY ) {
        /* 
         * Possibility 1: Both width and height fixed
         */
        measuredWidth = widthSize;
        measuredHeight = heightSize;

    } else if ( heightMode == MeasureSpec.EXACTLY ) {
        /*
         * Possibility 2: Width dynamic, height fixed
         */
        measuredWidth = (int) Math.min( widthSize, heightSize * aspectRatio );
        measuredHeight = (int) (measuredWidth / aspectRatio);

    } else if ( widthMode == MeasureSpec.EXACTLY ) {
        /*
         * Possibility 3: Width fixed, height dynamic
         */
        measuredHeight = (int) Math.min( heightSize, widthSize / aspectRatio );
        measuredWidth = (int) (measuredHeight * aspectRatio);

    } else {
        /* 
         * Possibility 4: Both width and height dynamic
         */
        if ( widthSize > heightSize * aspectRatio ) {
            measuredHeight = heightSize;
            measuredWidth = (int)( measuredHeight * aspectRatio );
        } else {
            measuredWidth = widthSize;
            measuredHeight = (int) (measuredWidth / aspectRatio);
        }

    }
}
9
JesperB

J'ai créé une bibliothèque de mise en page en utilisant la réponse de TalL . Sentez-vous libre de l'utiliser.

RatioLayouts

Installation

Ajoutez ceci en haut du fichier

repositories {
    maven {
        url  "http://dl.bintray.com/riteshakya037/maven" 
    }
}


dependencies {
    compile 'com.ritesh:ratiolayout:1.0.0'
}

Usage

Définissez l'espace de noms "app" sur la vue racine dans votre mise en page

xmlns:app="http://schemas.Android.com/apk/res-auto"

Inclure cette bibliothèque dans votre mise en page

<com.ritesh.ratiolayout.RatioRelativeLayout
        Android:id="@+id/activity_main_ratio_layout"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        app:fixed_attribute="WIDTH" // Fix one side of the layout 
        app:horizontal_ratio="2" // ratio of 2:3
        app:vertical_ratio="3">

Mise à jour

Avec l'introduction de ConstraintLayout vous n'avez pas besoin d'écrire une seule ligne de code ou d'utiliser des tiers ou de vous fier à PercentFrameLayout qui étaient obsolètes dans 26.0.0.

Voici l'exemple de la façon de conserver le rapport d'aspect 1: 1 pour votre mise en page en utilisant ConstraintLayout:

<Android.support.constraint.ConstraintLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <LinearLayout
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        Android:layout_marginEnd="0dp"
        Android:layout_marginStart="0dp"
        Android:layout_marginTop="0dp"
        Android:background="@Android:color/black"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </LinearLayout>

</Android.support.constraint.ConstraintLayout>
4
Ritesh Shakya

J'ai utilisé et aimé l'implémentation de ImageView de Jake Wharton (devrait aller de même pour les autres vues), d'autres pourraient en profiter aussi:

AspectRatioImageView.Java - ImageView qui respecte un rapport hauteur/largeur appliqué à une mesure spécifique

Heureusement qu'il est déjà stylable en XML.

4
Czechnology

Remplacez simplement onSizeChanged et calculez le ratio à cet endroit.

La formule du rapport hauteur/largeur est:

newHeight =  original_height / original_width x new_width

cela vous donnerait quelque chose comme ça:

 @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    //3:5 ratio
    float RATIO = 5/3;
    setLayoutParams(new LayoutParams((int)RATIO * w, w));

}

j'espère que cela t'aides!

3
Jmorvan

L'ExoPlayer de Google est livré avec un AspectRatioFrameLayout que vous utilisez comme ceci:

<com.google.Android.exoplayer2.ui.AspectRatioFrameLayout
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    app:resize_mode="fixed_width">
    <!-- https://exoplayer.dev/doc/reference/com/google/Android/exoplayer2/ui/AspectRatioFrameLayout.html#RESIZE_MODE_FIXED_WIDTH -->

    <ImageView
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />

</com.google.Android.exoplayer2.ui.AspectRatioFrameLayout>

Ensuite, vous devez définir le rapport d'aspect par programme:

aspectRatioFrameLayout.setAspectRatio(16f/9f)

Notez que vous pouvez également définir le mode de redimensionnement par programme avec setResizeMode .


Comme vous n'allez évidemment pas récupérer l'intégralité de la bibliothèque ExoPlayer pour cette classe unique, vous pouvez simplement copier-coller le fichier de GitHub dans votre projet (c'est Open source):

https://github.com/google/ExoPlayer/blob/release-v2/library/ui/src/main/Java/com/google/Android/exoplayer2/ui/AspectRatioFrameLayout.Java

N'oubliez pas de récupérer l'attribut resize_mode aussi:

https://github.com/google/ExoPlayer/blob/release-v2/library/ui/src/main/res/values/attrs.xml#L18-L25

<attr name="resize_mode" format="enum">
  <enum name="fit" value="0"/>
  <enum name="fixed_width" value="1"/>
  <enum name="fixed_height" value="2"/>
  <enum name="fill" value="3"/>
  <enum name="zoom" value="4"/>
</attr>
0