Comment dimensionner une vue Android en fonction des dimensions de son parent

104

Comment puis-je dimensionner une vue en fonction de la taille de sa mise en page parent. Par exemple, j'ai un RelativeLayoutqui remplit le plein écran, et je veux qu'une vue enfant, disons an ImageView, prenne toute la hauteur et 1/2 de la largeur?

J'ai essayé tout remplaçant sur onMeasure, onLayout, onSizeChanged, etc et je ne pouvais pas le faire au travail ....

Mitch
la source
Il est possible de le faire en utilisant ConstraintLayout, vérifiez ceci: stackoverflow.com/questions/37318228/…
Ali Nem

Réponses:

124

Je ne sais pas si quelqu'un lit encore ce fil ou non, mais la solution de Jeff ne vous mènera qu'à mi-chemin (un peu littéralement). Ce que son onMeasure va faire est d'afficher la moitié de l'image sur la moitié du parent. Le problème est que l'appel de super.onMeasure avant le setMeasuredDimensionmesurera tous les enfants de la vue en fonction de la taille d'origine, puis coupera simplement la vue en deux lorsque le setMeasuredDimensionredimensionnera.

Au lieu de cela, vous devez appeler setMeasuredDimension(comme requis pour un remplacement onMeasure) et fournir un nouveau LayoutParamspour votre vue, puis appeler super.onMeasure. N'oubliez pas que vous LayoutParamsêtes dérivé du type parent de votre vue, pas du type de votre vue.

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
   int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
   int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
   this.setMeasuredDimension(parentWidth/2, parentHeight);
   this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Je crois que la seule fois où vous aurez des problèmes avec le parent, LayoutParamsc'est si le parent est un AbsoluteLayout(ce qui est obsolète mais toujours utile).

Vic
la source
12
D'après mon expérience, cela fonctionne rarement, en particulier. lorsque fill / match_parent ou weights sont appelés. Mieux vaut appeler measureChildren après setMeasuredDimension (avec par exemple MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST)).
M. Schenk
1
MeasureSpec.getSize(widthMeasureSpec)Donne- t-il vraiment la bonne largeur du parent? Pour moi, cela renvoie simplement des largeurs aléatoires qui sont assez proches de la largeur réelle, mais pas exactes.
Konsumierer
1
@ M.Schenk l'a. J'ai un VideoView dans une mise en page pondérée. J'ai besoin que la largeur soit un pourcentage de la taille de l'écran et je dois maintenir le rapport hauteur / largeur. Cela étend la vidéo, donc wrap_contentne le fera pas. La réponse de Vic ne fonctionne pas pour ce scénario, contrairement au commentaire de @ M.Schenk. Il enregistre également une passe de mise en page supplémentaire. Vous devriez également le publier comme une réponse pour la visibilité.
dokkaebi
7
L'appel ne va-t-il pas super.onMeasure()simplement remplacer et annuler les effets de l'appel précédent this.setMeasuredDimension()?
Spinner
1
Je suis également curieux, comme @Spinner l'a mentionné, pourquoi l'appel à super.onMeasure()ne remplace pas les calculs précédents.
jwir3
39

Vous pouvez résoudre ce problème en créant une vue personnalisée et en remplaçant la méthode onMeasure (). Si vous utilisez toujours "fill_parent" pour layout_width dans votre XML, le paramètre widthMeasureSpec qui est passé dans la méthode onMeasusre () doit contenir la largeur du parent.

public class MyCustomView extends TextView {

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
        this.setMeasuredDimension(parentWidth / 2, parentHeight);
    }
}   

Votre XML ressemblerait à ceci:

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <view
        class="com.company.MyCustomView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
Jeff
la source
2
@jeff => pourquoi parentWidth / 2 ??
Saeed-rz
6
@Leon_SFS: La question se pose: "toute la hauteur et 1/2 de la largeur"
EdgarT
28

J'ai trouvé qu'il était préférable de ne pas jouer avec le réglage vous-même des dimensions mesurées. Il y a en fait un peu de négociation entre les vues parent et enfant et vous ne voulez pas réécrire tout ce code .

Ce que vous pouvez faire, cependant, c'est modifier les measureSpecs, puis appeler super avec eux. Votre vue ne saura jamais qu'elle reçoit un message modifié de son parent et s'occupera de tout pour vous:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    int myWidth = (int) (parentHeight * 0.5);
    super.onMeasure(MeasureSpec.makeMeasureSpec(myWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}
Phil Kulak
la source
16

Lorsque vous définissez une mise en page et une vue sur XML, vous pouvez spécifier la largeur et la hauteur de la mise en page d'une vue pour qu'elle soit un contenu enveloppant ou un parent de remplissage. Occuper la moitié de la zone est un peu plus difficile, mais si vous aviez quelque chose que vous vouliez sur l'autre moitié, vous pourriez faire quelque chose comme ce qui suit.

   <LinearLayout android:layout_width="fill_parent"
                android:layout_height="fill_parent">
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
   </LinearLayout>

Donner le même poids à deux choses signifie qu'elles s'étireront pour occuper la même proportion de l'écran. Pour plus d'informations sur les mises en page, consultez la documentation des développeurs.

Cheryl Simon
la source
Je ne recommande pas cela, mais ... en tant que hack, si vous utilisez l'approche ci-dessus, vous pouvez faire de l'une de ces vues un "factice" et définir android: visibilité = "invisible" pour remplir la moitié de la zone
RickNotFred
Quel est le but de n'utiliser que la moitié de la surface? Envisagez-vous d'afficher quelque chose dans l'autre moitié? Ce que vous faites avec la zone restante informera la meilleure approche
RickNotFred
cela devrait certainement être le n ° 1 - si vous pouvez le faire dans votre xml, pourquoi le faire dans votre java?
fandang
9

Je pense que l' approche XML de Mayras peut être intéressante. Cependant, il est possible de le rendre plus précis, avec une seule vue en définissant le weightSum. Je n'appellerais plus cela un hack mais à mon avis l'approche la plus simple:

<LinearLayout android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:weightSum="1">
    <ImageView android:layout_height="fill_parent"
               android:layout_width="0dp"
               android:layout_weight="0.5"/>
</LinearLayout>

Comme ça, vous pouvez utiliser n'importe quel poids, 0,6 par exemple (et le centrage) est le poids que j'aime utiliser pour les boutons.

Pinkwerther
la source
3

Essaye ça

int parentWidth = ((parentViewType)childView.getParent()).getWidth();
int parentHeight = ((parentViewType)childView.getParent()).getHeight();

alors vous pouvez utiliser LinearLayout.LayoutParams pour définir les paramètres de chileView

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(childWidth,childLength);
childView.setLayoutParams(params);
Abhishek Gupta
la source
2

Vous pouvez maintenant utiliser PercentRelativeLayout. Boom! Problème résolu.

androidguy
la source
2
PercentRelativeLayout est obsolète au niveau d'API 26.0.0-beta1.
dzikovskyy
Que voulez-vous dire "Au niveau de l'API"? Voulez-vous dire le numéro de version de la bibliothèque? PRL est dans la bibliothèque de support, n'a pas de niveau API.
androidguy
Par niveau d'API, j'entends la version d'Android. Infos d'ici developer.android.com/reference/android/support/percent/… . Plus d'informations sur le niveau d'API ici developer.android.com/guide/topics/manifest/…
dzikovskyy
Vous pouvez toujours utiliser cette classe, sauf si pour une raison quelconque vous avez besoin de la version 26.0.0 ou supérieure.
androidguy
1

Roman, si vous voulez faire votre mise en page en code Java (descendant de ViewGroup), c'est possible. L'astuce est que vous devez implémenter les méthodes onMeasure et onLayout. OnMeasure est appelé en premier et vous devez "mesurer" la sous-vue (en la dimensionnant effectivement à la valeur souhaitée). Vous devez le redimensionner lors de l'appel onLayout. Si vous ne parvenez pas à effectuer cette séquence ou à appeler setMeasuredDimension () à la fin de votre code onMeasure, vous n'obtiendrez pas de résultats. Pourquoi est-ce conçu de manière si compliquée et si fragile me dépasse.

Pavel Lahoda
la source
1

Si l'autre côté est vide. Je suppose que le moyen le plus simple serait d'ajouter une vue vide (par exemple une mise en page linéaire), puis de définir les largeurs des deux vues sur fill_parent et leurs deux poids sur 1.

Cela fonctionne dans un LinearLayout ...

jpm
la source
0

C'est quelque chose comme ça:

<RelativeLayout 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"
    tools:context="com.company.myapp.ActivityOrFragment"
    android:id="@+id/activity_or_fragment">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/linearLayoutFirst"
    android:weightSum="100">   <!-- Overall weights sum of children elements -->

    <Spinner
        android:layout_width="0dp"   <!-- It's 0dp because is determined by android:layout_weight -->
        android:layout_weight="50"
        android:layout_height="wrap_content"
        android:id="@+id/spinner" />

</LinearLayout>


<LinearLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:layout_below="@+id/linearLayoutFirst"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:id="@+id/linearLayoutSecond">

    <EditText
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="75"
        android:inputType="numberDecimal"
        android:id="@+id/input" />

    <TextView
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="25"
        android:id="@+id/result"/>

</LinearLayout>
</RelativeLayout>
Mario
la source
-1

Je me débat avec cette question depuis longtemps, je veux changer la largeur du tiroir de mon DrawerLayout, qui est le deuxième enfant de son parent. Récemment, je l'ai compris, mais je ne sais pas s'il y a une meilleure idée.

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
    super.onLayout(changed, l, t, r, b)
    (getChildAt(1).layoutParams as LayoutParams).width = measuredWidth/2
}
Jean 6
la source