Wie können Ansichten in RelativeLayout programmgesteuert angeordnet werden?


234

Ich versuche, programmgesteuert (und nicht deklarativ über XML) Folgendes zu erreichen:

<RelativeLayout...>
   <TextView ...
      android:id="@+id/label1" />
   <TextView ...
      android:id="@+id/label2"
      android:layout_below: "@id/label1" />
</RelativeLayout>

Mit anderen Worten, wie kann ich die zweite TextViewunter der ersten anzeigen lassen, aber ich möchte dies im Code tun:

RelativeLayout layout = new RelativeLayout(this);
TextView label1 = new TextView(this);
TextView label2 = new TextView(this);
...
layout.addView(label1);
layout.addView(label2);
setContentView(layout);

Aktualisieren:

Danke, TreeUK. Ich verstehe die allgemeine Richtung, aber es funktioniert immer noch nicht - "B" überlappt "A". Was mache ich falsch?

RelativeLayout layout = new RelativeLayout(this);
TextView tv1 = new TextView(this);
tv1.setText("A");

TextView tv2 = new TextView(this);
tv2.setText("B");
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
        RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.FILL_PARENT);
lp.addRule(RelativeLayout.RIGHT_OF, tv1.getId());

layout.addView(tv1);        
layout.addView(tv2, lp);

In Ihrem Codebeispiel fügen Sie nicht die Regel RelativeLayout.BELOW, tv1.getId () hinzu.
Tristan Warner-Smith

48
Sie müssen Ihren untergeordneten Ansichten IDs bereitstellen: tv1.setId (1); tv2.setId (2); Übergeordnete Ansichten weisen untergeordneten Ansichten nicht automatisch eine ID zu, und der Standardwert für eine ID ist NO_ID. IDs müssen in einer Ansicht nicht eindeutig sein - also sind 1, 2, 3 usw. gute Werte - und Sie müssen sie für die relative Verankerung verwenden, damit sie in RelativeLayout funktionieren.
sechastain

Antworten:


196

Nach dem, was ich zusammensetzen konnte, müssen Sie die Ansicht mit LayoutParams hinzufügen.

LinearLayout linearLayout = new LinearLayout(this);

RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(
        LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);

parentView.addView(linearLayout, relativeParams);

Alle Kredite an sechastain. Um Ihre Artikel relativ programmgesteuert zu positionieren, müssen Sie ihnen IDs zuweisen.

TextView tv1 = new TextView(this);
tv1.setId(1);
TextView tv2 = new TextView(this);
tv2.setId(2);

Dann addRule(RelativeLayout.RIGHT_OF, tv1.getId());


1
In meinem Fall, abgesehen vom Festlegen der IDs einer untergeordneten Ansicht, dem Hinzufügen der untergeordneten Ansicht zur übergeordneten Ansicht und dem Aufrufen von requestLayout () in der untergeordneten Ansicht, bevor die Regeln für die andere untergeordnete Ansicht festgelegt wurden, funktionierten die Dinge. Hoffe das hilft jemandem
2cupsOfTech

Falls jemand dies noch befolgt: Wenn ich eine Sammlung von Ansichten habe, füge ich eine nach der anderen hinzu, und ich kann nicht sicher sein, dass eine der Ansichten, für die ein Element festgelegt wurde, belowbereits hinzugefügt wurde, um sicherzustellen, dass es weiterhin gerendert wird korrekt? Nach meinem Verständnis addViewwird das Layout neu gerendert. Wenn ich es also für ein Element mache, dessen Verwandter noch nicht hinzugefügt wurde, stürzt es ab, da ein Element mit dieser ID noch nicht vorhanden ist.
Megakoresh

1
set (id) sollte mit generateViewId () verwendet werden. Hinzugefügt in API-Level 17. Es generiert einen Wert, der für die Verwendung in setId (int) geeignet ist. Dieser Wert kollidiert nicht mit ID-Werten, die zur Erstellungszeit von aapt für R.id generiert wurden. Beispiel: tv [l_idx] .setId (View.generateViewId ());
AndrewBloom

Diese Antwort ist sehr schlecht geschrieben. Was ist der Grund für die Verwendung von RelationalLayout-Parametern für ein lineares Layout? Und warum fügen Sie das lineare Layout einer übergeordneten Ansicht hinzu?
pcodex

Hey @Prab, diese Antwort ist jetzt 9 Jahre alt. Wenn Sie ein positives Feedback dazu haben, wie die Antwort für die heutigen Anforderungen verbessert werden kann, klicken Sie bitte auf die Schaltfläche "Bearbeiten" und schlagen Sie sie vor 👍
Tristan Warner-Smith

60

Um es kurz zu machen: Mit dem relativen Layout positionieren Sie Elemente innerhalb des Layouts.

  1. Erstellen Sie ein neues RelativeLayout.LayoutParams

    RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(...)

    (Was auch immer ... übergeordneten oder umschließenden Inhalt, absolute Zahlen, falls erforderlich, oder Verweis auf eine XML-Ressource)

  2. Regeln hinzufügen: Regeln beziehen sich auf die Eltern oder andere "Brüder" in der Hierarchie.

    lp.addRule(RelativeLayout.BELOW, someOtherView.getId())
    lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
    
  3. Wenden Sie einfach die Layout-Parameter an: Der "gesündeste" Weg, dies zu tun, ist:

    parentLayout.addView(myView, lp)

Achtung : Ändern Sie das Layout nicht aus den Layout-Rückrufen. Dies ist verlockend, da die Ansichten dann ihre tatsächliche Größe erhalten. In diesem Fall werden jedoch unerwartete Ergebnisse erwartet.


Ich hatte keine Ahnung, dass Sie Parameter in einer Ansicht festlegen und der übergeordneten Ansicht eine Ansicht hinzufügen können, alles in einer Codezeile ( parentLayout.addView(myView, lp). Ich habe das immer in zwei Codezeilen gemacht. Danke dir!
Matt

Das hat bei mir funktioniert und mir sehr geholfen, aber gibt es eine Möglichkeit, das Kind unter etwas auszurichten und dann den oberen Rand für dieses Kind festzulegen? Oder kann ich den unteren Rand für das, was darunter liegt, einstellen und wird er korrekt angepasst?
Jacob Varner

Sicher gibt es. Sie können die Ränder (leider ist die programmatische API nicht so komfortabel wie das XML) mit setMargins (links, oben, rechts, unten) festlegen. Es ist in den LayoutParams. Beachten Sie, dass das Auffüllen eine Eigenschaft der Ansicht ist, nicht der Layoutparameter.
Meymann

29

Ich habe gerade 4 Stunden mit diesem Problem verbracht. Endlich wurde klar, dass Sie nicht Null als Ansichts-ID verwenden dürfen . Sie würden denken, dass es als NO_ID == -1 erlaubt ist, aber die Dinge neigen dazu, durcheinander zu kommen, wenn Sie es Ihrer Ansicht geben ...


2
Ich sehe das gleiche. Ich verwende API 7 und sehe im Javadoc, dass setID (int) angibt, dass die ID eine positive Ganzzahl sein sollte. Dies sollte im Code erzwungen und nicht nur in den Dokumenten kommentiert werden. developer.android.com/reference/android/view/…
OYRM

@ OYRM Android ist ein Chaos
SpaceMonkey

Möglicherweise haben Sie mir gerade eine TONNE Zeit gespart. Vielen Dank!
rjr-apps

9

Android 22 minimal lauffähiges Beispiel

Geben Sie hier die Bildbeschreibung ein

Quelle:

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class Main extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final RelativeLayout relativeLayout = new RelativeLayout(this);

        final TextView tv1;
        tv1 = new TextView(this);
        tv1.setText("tv1");
        // Setting an ID is mandatory.
        tv1.setId(View.generateViewId());
        relativeLayout.addView(tv1);

        // tv2.
        final TextView tv2;
        tv2 = new TextView(this);
        tv2.setText("tv2");
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.FILL_PARENT);
        lp.addRule(RelativeLayout.BELOW, tv1.getId());
        relativeLayout.addView(tv2, lp);

        // tv3.
        final TextView tv3;
        tv3 = new TextView(this);
        tv3.setText("tv3");
        RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        );
        lp2.addRule(RelativeLayout.BELOW, tv2.getId());
        relativeLayout.addView(tv3, lp2);

        this.setContentView(relativeLayout);
    }
}

Funktioniert mit dem Standardprojekt, das von generiert wurde android create project .... GitHub-Repository mit minimalem Build-Code .


1
Könnten Sie dieses Beispiel auf 3 erweitern TextView? Ich habe es versucht und der dritte TextViewfehlt.
Zizheng Wu

7

Anruf

tv1.setId(1) 

nach dem

tv1.setText("A");

Wenn ich fragen darf, was ist der Grund? Ich habe ein Problem, verwende aber sowohl ImageView als auch TextView. Daher frage ich mich, wann ich .setId () in ImageView aufrufen soll.
Joshua G

Zu Ihrer Information - Ich habe .setId () angerufen, kurz bevor ich die Ansicht hinzugefügt habe, und es hat funktioniert, keine Ahnung warum .. lol
Joshua G

4

Versuchen:

EditText edt = (EditText) findViewById(R.id.YourEditText);
RelativeLayout.LayoutParams lp =
    new RelativeLayout.LayoutParams
    (
        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT
    );
lp.setMargins(25, 0, 0, 0); // move 25 px to right (increase left margin)
edt.setLayoutParams(lp); // lp.setMargins(left, top, right, bottom);

1
Schön das nützlichste Beispiel. Vielen Dank.
Vyacheslav

2

Dieser Ansatz mit ViewGroup.MarginLayoutParams hat bei mir funktioniert:

RelativeLayout myLayout = (RelativeLayout) findViewById(R.id.my_layout);

TextView someTextView = ...

int leftMargin = Util.getXPos();
int topMargin = Util.getYPos();

RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
    new ViewGroup.MarginLayoutParams(
        RelativeLayout.LayoutParams.WRAP_CONTENT,
        RelativeLayout.LayoutParams.WRAP_CONTENT));

lp.setMargins(leftMargin, topMargin, 0, 0);

myLayout.addView(someTextView, lp);

1
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);

        final RelativeLayout relativeLayout = new RelativeLayout(this);
        final TextView tv1 = new TextView(this);
        tv1.setText("tv1 is here");
        // Setting an ID is mandatory.
        tv1.setId(View.generateViewId());
        relativeLayout.addView(tv1);


        final TextView tv2 = new TextView(this);
        tv2.setText("tv2 is here");

        // We are defining layout params for tv2 which will be added to its  parent relativelayout.
        // The type of the LayoutParams depends on the parent type.
        RelativeLayout.LayoutParams tv2LayoutParams = new  RelativeLayout.LayoutParams(
            RelativeLayout.LayoutParams.WRAP_CONTENT,
            RelativeLayout.LayoutParams.WRAP_CONTENT);

        //Also, we want tv2 to appear below tv1, so we are adding rule to tv2LayoutParams.
        tv2LayoutParams.addRule(RelativeLayout.BELOW, tv1.getId());

        //Now, adding the child view tv2 to relativelayout, and setting tv2LayoutParams to be set on view tv2.
        relativeLayout.addView(tv2);
        tv2.setLayoutParams(tv2LayoutParams);
        //Or we can combined the above two steps in one line of code
        //relativeLayout.addView(tv2, tv2LayoutParams);

        this.setContentView(relativeLayout);
    }

}

0

Wenn Sie wirklich manuell gestalten möchten, würde ich empfehlen, überhaupt kein Standardlayout zu verwenden. Mach alles alleine, hier ein Kotlin-Beispiel:

class ProgrammaticalLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ViewGroup(context, attrs, defStyleAttr) { 
    private val firstTextView = TextView(context).apply {
        test = "First Text"
    }

    private val secondTextView = TextView(context).apply {
        text = "Second Text"
    }

    init {
        addView(firstTextView)
        addView(secondTextView)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        // center the views verticaly and horizontaly
        val firstTextLeft = (measuredWidth - firstTextView.measuredWidth) / 2
        val firstTextTop = (measuredHeight - (firstTextView.measuredHeight + secondTextView.measuredHeight)) / 2
        firstTextView.layout(firstTextLeft,firstTextTop, firstTextLeft + firstTextView.measuredWidth,firstTextTop + firstTextView.measuredHeight)

        val secondTextLeft = (measuredWidth - secondTextView.measuredWidth) / 2
        val secondTextTop = firstTextView.bottom
        secondTextView.layout(secondTextLeft,secondTextTop, secondTextLeft + secondTextView.measuredWidth,secondTextTop + secondTextView.measuredHeight)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 
        // just assume we`re getting measured exactly by the parent
        val measuredWidth = MeasureSpec.getSize(widthMeasureSpec)
        val measuredHeight = MeasureSpec.getSize(heightMeasureSpec)

        firstTextView.measures(MeasureSpec.makeMeasureSpec(meeasuredWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
        secondTextView.measures(MeasureSpec.makeMeasureSpec(meeasuredWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))

        setMeasuredDimension(measuredWidth, measuredHeight)
    }
}

Dies könnte Ihnen eine Vorstellung davon geben, wie dies funktionieren könnte

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.