Jak programowo rozłożyć widoki w RelativeLayout?


234

Staram się osiągnąć następujące programowo (zamiast deklaratywnie poprzez XML):

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

Innymi słowy, jak sprawić, aby drugi TextViewpojawił się poniżej pierwszego, ale chcę to zrobić w kodzie:

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

Aktualizacja:

Dzięki, TreeUK. Rozumiem ogólny kierunek, ale nadal nie działa - „B” nakłada się na „A”. Co ja robię źle?

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

W przykładowym kodzie nie dodajesz reguły RelativeLayout.BELOW, tv1.getId ();
Tristan Warner-Smith,

48
musisz podać identyfikatory do swoich widoków dziecka: tv1.setId (1); tv2.setId (2); Widoki nadrzędne nie przypisują automatycznie widoków podrzędnych Id, a domyślną wartością Id jest NO_ID. Identyfikatory nie muszą być unikalne w hierarchii widoków - więc 1, 2, 3 itd. Są świetnymi wartościami do użycia - i musisz ich użyć do względnego zakotwiczenia, aby działać w RelativeLayout.
sechastain

Odpowiedzi:


196

Z tego, co udało mi się połączyć, musisz dodać widok za pomocą LayoutParams.

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

Wszystkie zasługi dla sechastain, aby względnie programowo pozycjonować swoje przedmioty, musisz przypisać im identyfikatory.

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

Następnie addRule(RelativeLayout.RIGHT_OF, tv1.getId());


1
w moim przypadku oprócz ustawiania identyfikatorów childview, dodanie childview do Parentview i wywołanie requestLayout () w childview przed ustaleniem reguł drugiego childview sprawiło, że wszystko działało. Mam nadzieję, że to komuś pomoże
2cupsOfTech

Jeśli ktoś nadal tak postępuje: gdy mam kolekcję widoków, dodaję jeden po drugim i nie mogę mieć pewności, że jeden z widoków, dla których ustawiony jest element, belowzostał już dodany, jak upewnić się, że nadal renderuje prawidłowo? Z mojego zrozumienia addViewwynika, że ​​układ renderuje się ponownie, więc jeśli zrobię to dla elementu, którego krewny nie został jeszcze dodany, ulegnie awarii, ponieważ element o tym identyfikatorze jeszcze nie istnieje.
Megakoresh,

1
set (id) powinien być używany z generacjąViewViewId () dodaną do poziomu API 17. Generuje wartość odpowiednią do użycia w setId (int). Ta wartość nie będzie kolidować z wartościami ID wygenerowanymi w czasie kompilacji przez aapt dla R.id. Przykład: tv [l_idx] .setId (View.generateViewId ());
AndrewBloom

Ta odpowiedź jest bardzo źle napisana. Jakie jest uzasadnienie zastosowania parametrów RelationalLayout dla układu liniowego? I dlaczego dodajesz układ liniowy do widoku rodzica?
pcodex

Cześć @Prab, ta odpowiedź ma teraz 9 lat. Jeśli masz jakieś pozytywne opinie na temat tego, jak można poprawić odpowiedź na dzisiejsze potrzeby, możesz nacisnąć przycisk edycji i zasugerować ją 👍
Tristan Warner-Smith

60

Krótko mówiąc: dzięki względnemu układowi umieszczasz elementy wewnątrz układu.

  1. utwórz nowy RelativeLayout.LayoutParams

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

    (cokolwiek ... wypełnij treść nadrzędną lub opakuj, liczby bezwzględne, jeśli musisz lub odwołanie do zasobu XML)

  2. Dodaj reguły: reguły odnoszą się do rodzica lub innych „braci” w hierarchii.

    lp.addRule(RelativeLayout.BELOW, someOtherView.getId())
    lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
  3. Wystarczy zastosować parametry układu: najbardziej „zdrowym” sposobem na to jest:

    parentLayout.addView(myView, lp)

Uwaga : nie zmieniaj układu z wywołania zwrotnego układu. Jest to kuszące, ponieważ wtedy widoki uzyskują swoje rzeczywiste rozmiary. Jednak w takim przypadku oczekuje się nieoczekiwanych wyników.


Nie miałem pojęcia, że ​​możesz ustawić parametry widoku i dodać widok do jego obiektu nadrzędnego Wyświetl wszystko w jednym wierszu kodu ( parentLayout.addView(myView, lp). Zawsze robiłem to w dwóch liniach kodu. Dziękuję Ci!
Matt

To działało dla mnie i bardzo pomogło, ale czy istnieje sposób, aby ustawić dziecko poniżej czegoś, a następnie ustawić margines powyżej dla tego dziecka? Czy też mogę ustawić margines poniżej dla rzeczy, która jest poniżej i czy będzie się odpowiednio dostosowywał?
Jacob Varner,

Pewnie że jest. Możesz ustawić marginesy (niestety programistyczne API nie jest tak wygodne jak XML) za pomocą setMargins (lewy, górny, prawy, dolny). Jest w LayoutParams. Pamiętaj, że wypełnienie jest właściwością widoku, a nie parametrami układu.
Meymann,

29

Właśnie spędziłem 4 godziny z tym problemem. Wreszcie zdałem sobie sprawę, że nie wolno używać zera jako identyfikatora widoku . Można by pomyśleć, że jest to dozwolone jako NO_ID == -1, ale rzeczy mają tendencję do szaleństwa, jeśli podasz to do swojego widoku ...


2
Widzę to samo. Używam API 7 i przeglądając javadoc widzę, że setID (int) stwierdza, że ​​id powinien być dodatnią liczbą całkowitą. Jest to coś, co powinno być egzekwowane w kodzie, a nie tylko komentowane w dokumentach. developer.android.com/reference/android/view/…
OYRM

@OYRM Android to bałagan
SpaceMonkey

Być może właśnie zaoszczędziłeś TONĘ czasu. Dzięki!
rjr-apps

9

Przykład minimalnego uruchomienia Androida 22

wprowadź opis zdjęcia tutaj

Źródło:

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

Działa z domyślnym projektem wygenerowanym przez android create project .... Repozytorium GitHub z minimalnym kodem kompilacji .


1
Czy możesz rozszerzyć ten przykład na 3 TextView? Próbowałem, a trzeciego TextViewbrakuje.
Zizheng Wu,

7

połączenie

tv1.setId(1) 

po

tv1.setText("A");

Jeśli mogę zapytać, jaki jest powód? Mam problem, ale używam zarówno ImageView, jak i TextView, więc zastanawiam się, kiedy wywołać .setId () na ImageView.
Joshua G

FYI - zadzwoniłem do .setId () tuż przed dodaniem widoku i zadziałało, nie mam pojęcia, dlaczego .. lol
Joshua G

4

Próbować:

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
Miły najbardziej przydatny przykład. Dzięki.
Wiaczesław

2

Takie podejście z ViewGroup.MarginLayoutParams działało dla mnie:

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

Jeśli naprawdę chcesz układać ręcznie, radzę w ogóle nie używać standardowego układu. Zrób to sam, oto przykład kotlina:

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

To może dać ci wyobrażenie, jak to może działać

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.