Come disporre le visualizzazioni in RelativeLayout a livello di codice?


234

Sto cercando di ottenere quanto segue a livello di codice (anziché dichiarativamente tramite XML):

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

In altre parole, come faccio a far TextViewapparire il secondo sotto il primo, ma voglio farlo nel codice:

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

Aggiornare:

Grazie, TreeUK. Capisco la direzione generale, ma continua a non funzionare - "B" si sovrappone a "A". Che cosa sto facendo di sbagliato?

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

Nell'esempio di codice, non stai effettivamente aggiungendo la regola di RelativeLayout.BELOW, tv1.getId ();
— Tristan Warner-Smith,

48
devi fornire gli ID alle visualizzazioni dei tuoi figli: tv1.setId (1); tv2.setId (2); Le viste padre non assegnano automaticamente alle viste figlio un ID e il valore predefinito per un ID è NO_ID. Gli ID non devono essere univoci in una vista, quindi 1, 2, 3, ecc. Sono tutti ottimi valori da usare - e devi usarli per l'ancoraggio relativo per funzionare in RelativeLayout.
— restastain

Risposte:


196

Da quello che sono stato in grado di mettere insieme, devi aggiungere la vista usando 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);

Tutto il credito da conservare, per posizionare i tuoi oggetti relativamente a livello di codice, devi assegnare loro degli ID.

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

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


1
nel mio caso, a parte l'impostazione degli ID di una vista figlio, l'aggiunta della vista figlio alla vista padre e la chiamata requestLayout () su vista figlio prima di impostare le regole dell'altra vista figlio ha fatto funzionare le cose. Spero che questo aiuti qualcuno
— 2cupsOfTech,

Nel caso in cui qualcuno segua ancora questo: quando ho una raccolta di viste sto aggiungendo una per una e non posso essere certo che una delle viste su cui è impostato un elemento sia belowgià stata aggiunta, come assicurarmi che sia ancora visualizzato correttamente? addViewSecondo la mia comprensione , il layout viene ridistribuito, quindi se lo faccio per un oggetto che non è stato ancora aggiunto, il suo crash si interromperà poiché l'oggetto con quell'ID non esiste ancora.
— Megakoresh,

1
set (id) dovrebbe essere usato con generateViewId () Aggiunto nel livello API 17. Genera un valore adatto per l'uso in setId (int). Questo valore non si scontrerà con i valori ID generati al momento della creazione da aapt per R.id. Esempio: tv [l_idx] .setId (View.generateViewId ());
— AndrewBloom,

Questa risposta è scritta molto male. Qual è la logica alla base dell'utilizzo dei parametri RelationalLayout per un layout lineare? E perché stai aggiungendo il layout lineare a una vista padre?
— pcodex,

Ehi @Prab, questa risposta ha ora 9 anni. Se hai un feedback positivo su come migliorare la risposta per le esigenze di oggi, non esitare a premere il pulsante di modifica e suggerirlo 👍
— Tristan Warner-Smith,

60

Per farla breve: con il layout relativo posizionate gli elementi all'interno del layout.

  1. creare un nuovo RelativeLayout.LayoutParams

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

    (qualunque cosa ... riempi i contenuti principali o a capo, numeri assoluti se necessario o riferimenti a una risorsa XML)

  2. Aggiungi regole: le regole si riferiscono al genitore o ad altri "fratelli" nella gerarchia.

    lp.addRule(RelativeLayout.BELOW, someOtherView.getId())
    lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
  3. Basta applicare i parametri di layout: il modo più "salutare" per farlo è:

    parentLayout.addView(myView, lp)

Attenzione : non modificare il layout dai callback di layout. È allettante farlo perché è quando le viste hanno le loro dimensioni reali. Tuttavia, in tal caso, sono previsti risultati imprevisti.


Non avevo idea che avresti potuto impostare i parametri su una vista e aggiungere una vista al suo genitore Visualizza tutto in una riga di codice ( parentLayout.addView(myView, lp). L'ho sempre fatto in due righe di codice. Grazie!
— Matt,

Questo ha funzionato per me e ha aiutato molto, ma c'è un modo per allineare il bambino sotto qualcosa e quindi impostare il margine sopra per quel bambino? O posso impostare il margine in basso per la cosa che è in basso e si adatterà correttamente?
— Jacob Varner,

Certo che c'è. Puoi impostare i margini (sfortunatamente, l'API programmatica non è comoda come l'XML) con setMargins (a sinistra, in alto, a destra, in basso). È nel LayoutParams. Si noti che il riempimento è una proprietà della vista, non i parametri di layout.
— Meymann,

29

Ho appena trascorso 4 ore con questo problema. Alla fine ho capito che non devi usare zero come ID di visualizzazione . Penseresti che sia consentito come NO_ID == -1, ma le cose tendono a andare in tilt se lo dai alla tua vista ...


2
Vedo lo stesso. Sto usando API 7, e vedendo javadoc vedo che setID (int) afferma che l'id dovrebbe essere un numero intero positivo. È qualcosa che dovrebbe essere applicato nel codice, non semplicemente commentato nei documenti. developer.android.com/reference/android/view/…
— OYRM

@OYRM Android è un casino
— SpaceMonkey il

Probabilmente mi hai risparmiato un sacco di tempo. Grazie!
— rjr-apps,

9

Esempio di runnable minimo per Android 22

inserisci qui la descrizione dell'immagine

Fonte:

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

Funziona con il progetto predefinito generato da android create project .... Repository GitHub con codice build minimo .


1
Potresti estendere questo esempio a 3 TextView? Ho provato e TextViewmanca il terzo .
— Zizheng Wu,

7

chiamata

tv1.setId(1) 

dopo

tv1.setText("A");

Se posso chiedere, qual è la ragione? Sto riscontrando un problema ma sto utilizzando ImageView e TextView, quindi mi chiedo quando chiamare .setId () su ImageView.
— Joshua G,

Cordiali saluti - Ho chiamato .setId () proprio prima di aggiungere la vista e ha funzionato, non ho idea del perché .. lol
— Joshua G

4

Provare:

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
Bello l'esempio più utile. Grazie.
— Vyacheslav,

2

Questo approccio con ViewGroup.MarginLayoutParams ha funzionato per me:

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

Se vuoi davvero layout manualmente, suggerirei di non usare affatto un layout standard. Fai tutto da solo, qui un esempio di kotlin:

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

Questo potrebbe darti un'idea di come potrebbe funzionare

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.