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.