So dimensionieren Sie eine Android-Ansicht basierend auf den Abmessungen ihrer Eltern

104

Wie kann ich die Größe einer Ansicht basierend auf der Größe des übergeordneten Layouts anpassen? Zum Beispiel habe ich eine RelativeLayout, die den gesamten Bildschirm ausfüllt, und ich möchte, dass eine untergeordnete Ansicht, z. B. eine ImageView, die gesamte Höhe und die Hälfte der Breite einnimmt?

Ich habe überschreiben alle anprobiert onMeasure, onLayout, onSizeChanged, etc. und ich konnte es nicht an die Arbeit ....

Mitch
quelle
Es ist möglich, dies mit ConstraintLayout zu tun. Überprüfen Sie dies: stackoverflow.com/questions/37318228/…
Ali Nem

Antworten:

124

Ich weiß nicht, ob noch jemand diesen Thread liest oder nicht, aber Jeffs Lösung bringt Sie nur auf halbem Weg dorthin (irgendwie wörtlich). Mit seiner onMeasure wird die Hälfte des Bildes in der Hälfte des übergeordneten Elements angezeigt. Das Problem ist, dass beim Aufrufen von super.onMeasure vor dem das Ändern setMeasuredDimensionaller untergeordneten Elemente in der Ansicht anhand der Originalgröße erfolgt und die Ansicht dann nur halbiert wird, wenn die setMeasuredDimensionGröße geändert wird.

Stattdessen müssen Sie anrufen setMeasuredDimension(wie für eine OnMeasure-Überschreibung erforderlich) und eine neue LayoutParamsfür Ihre Ansicht bereitstellen und dann anrufen super.onMeasure. Denken Sie daran, dass Sie LayoutParamsvom übergeordneten Typ Ihrer Ansicht abgeleitet sind, nicht vom Typ Ihrer Ansicht.

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

Ich glaube, das einzige Mal, wenn Sie Probleme mit dem Elternteil haben, LayoutParamsist, wenn das Elternteil ein ist AbsoluteLayout(was veraltet, aber manchmal immer noch nützlich ist).

Vic
quelle
12
Nach meiner Erfahrung funktioniert dies selten, insb. wenn fill / match_parent oder weight aufgerufen werden. Rufen Sie MeasureChildren besser nach setMeasuredDimension auf (z. B. mit MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST)).
M. Schenk
1
Gibt MeasureSpec.getSize(widthMeasureSpec)wirklich die richtige Elternbreite an? Für mich gibt es nur zufällige Breiten zurück, die ziemlich nahe an der tatsächlichen Breite liegen, aber nicht genau sind.
Konsumierer
1
@ M.Schenk hat es. Ich habe eine Videoansicht in einem gewichteten Layout. Die Breite muss ein Prozentsatz der Bildschirmgröße sein, und ich muss das Seitenverhältnis beibehalten. Dadurch wird das Video gedehnt, was wrap_contentnicht der Fall ist. Die Antwort von Vic funktioniert in diesem Szenario nicht, der Kommentar von @ M.Schenk jedoch. Es spart auch einen zusätzlichen Layout-Pass. Sie sollten es auch als Antwort für die Sichtbarkeit veröffentlichen.
Dokkaebi
7
Wird der Aufruf nicht super.onMeasure()einfach die Auswirkungen des früheren Aufrufs an überschreiben und aufheben this.setMeasuredDimension()?
Spinner
1
Ich bin auch neugierig, wie @Spinner erwähnte, warum der Aufruf von super.onMeasure()die früheren Berechnungen nicht überschreibt.
Jwir3
39

Sie können dieses Problem lösen, indem Sie eine benutzerdefinierte Ansicht erstellen und die onMeasure () -Methode überschreiben. Wenn Sie in Ihrer XML-Datei immer "fill_parent" für die layout_width verwenden, sollte der Parameter widthMeasureSpec, der an die onMeasusre () -Methode übergeben wird, die Breite des übergeordneten Elements enthalten.

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

Ihr XML würde ungefähr so ​​aussehen:

<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
quelle
2
@jeff => warum parentWidth / 2 ??
Saeed-rz
6
@Leon_SFS: Die Frage danach: "ganze Höhe und 1/2 der Breite"
EdgarT
28

Ich habe festgestellt, dass es am besten ist, die gemessenen Abmessungen nicht selbst einzustellen. Es gibt tatsächlich einige Verhandlungen zwischen der übergeordneten und der untergeordneten Ansicht, und Sie möchten den gesamten Code nicht neu schreiben .

Sie können jedoch die MeasureSpecs ändern und dann mit ihnen Super aufrufen. Ihre Ansicht wird nie erfahren, dass sie eine geänderte Nachricht von ihrem Elternteil erhält, und sich um alles für Sie kümmern:

@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
quelle
16

Wenn Sie ein Layout und eine Ansicht in XML definieren, können Sie die Layoutbreite und -höhe einer Ansicht angeben, die entweder als Wrap-Inhalt oder als übergeordnetes Element gefüllt werden soll. Die Hälfte der Fläche einzunehmen ist etwas schwieriger, aber wenn Sie etwas hatten, das Sie auf der anderen Hälfte wollten, könnten Sie etwas wie das Folgende tun.

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

Wenn zwei Dinge das gleiche Gewicht haben, werden sie gedehnt, um den gleichen Anteil des Bildschirms einzunehmen. Weitere Informationen zu Layouts finden Sie in den Entwicklungsdokumenten.

Cheryl Simon
quelle
Ich empfehle dies nicht, aber ... als Hack, wenn Sie den obigen Ansatz verwenden, könnten Sie eine dieser Ansichten zu einem "Dummy" machen und android: sichtbarkeit = "unsichtbar" setzen, um die Hälfte des Bereichs
auszufüllen
Was ist der Zweck, nur die Hälfte der Fläche zu nutzen? Planen Sie, in der anderen Hälfte etwas anzuzeigen? Was Sie mit dem verbleibenden Bereich tun, wird den besten Ansatz informieren
RickNotFred
Dies sollte definitiv die Nummer 1 sein - wenn Sie es in Ihrer XML tun können, warum tun Sie es in Ihrem Java?
Fandang
9

Ich glaube, dass Mayras XML-Ansatz ordentlich kommen kann. Es ist jedoch möglich, die Genauigkeit mit nur einer Ansicht zu erhöhen, indem Sie die weightSum einstellen. Ich würde das nicht mehr als Hack bezeichnen, aber meiner Meinung nach der einfachste Ansatz:

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

So können Sie jedes Gewicht verwenden, zum Beispiel 0,6 (und Zentrierung) ist das Gewicht, das ich gerne für Schaltflächen verwende.

pinkwerther
quelle
3

Versuche dies

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

Dann können Sie LinearLayout.LayoutParams verwenden, um die Parameter von chileView festzulegen

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

Sie können jetzt PercentRelativeLayout verwenden. Boom! Problem gelöst.

androidguy
quelle
2
PercentRelativeLayout war in API-Level 26.0.0-beta1 veraltet.
Dzikovskyy
Was meinst du mit "In API Level"? Meinen Sie die Versionsnummer der Bibliothek? PRL befindet sich in der Support-Bibliothek und hat keine API-Ebene.
AndroidGy
Mit API-Level meine ich die Version von Android. Infos von hier developer.android.com/reference/android/support/percent/… . Weitere Informationen zur API-Ebene finden Sie hier. Developer.android.com/guide/topics/manifest/…
dzikovskyy
Sie können diese Klasse weiterhin verwenden, es sei denn, Sie benötigen aus irgendeinem Grund Version 26.0.0 oder höher.
AndroidGy
1

Roman, wenn Sie Ihr Layout in Java-Code (ViewGroup-Nachkomme) erstellen möchten, ist dies möglich. Der Trick besteht darin, dass Sie sowohl die onMeasure- als auch die onLayout-Methode implementieren müssen. Das onMeasure wird zuerst aufgerufen und Sie müssen die Unteransicht dort "messen" (effektiv auf den gewünschten Wert zuschneiden). Sie müssen die Größe im Aufruf von onLayout erneut anpassen. Wenn Sie diese Sequenz nicht ausführen oder setMeasuredDimension () am Ende Ihres onMeasure-Codes nicht aufrufen, erhalten Sie keine Ergebnisse. Warum dies so kompliziert und fragil gestaltet ist, ist mir ein Rätsel.

Pavel Lahoda
quelle
1

Wenn die andere Seite leer ist. Ich denke, der einfachste Weg wäre, eine leere Ansicht (z. B. ein lineares Layout) hinzuzufügen und dann die Breite beider Ansichten auf fill_parent und beide Gewichte auf 1 zu setzen.

Dies funktioniert in einem LinearLayout ...

jpm
quelle
0

Es ist ungefähr so:

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

Ich habe lange mit dieser Frage zu kämpfen. Ich möchte die Schubladenbreite meines DrawerLayout ändern, das das zweite Kind seines Elternteils ist. Kürzlich habe ich es herausgefunden, weiß aber nicht, ob es eine bessere Idee gibt.

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
}
Johannes 6
quelle