¿Cómo diseñar vistas en RelativeLayout mediante programación?


234

Estoy tratando de lograr lo siguiente mediante programación (en lugar de declarativamente a través de XML):

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

En otras palabras, ¿cómo hago que el segundo TextViewaparezca debajo del primero, pero quiero hacerlo en código?

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

Actualizar:

Gracias, TreeUK. Entiendo la dirección general, pero todavía no funciona: "B" se superpone a "A". ¿Qué estoy haciendo mal?

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

En su ejemplo de código, en realidad no está agregando la regla de RelativeLayout.BELOW, tv1.getId ();
Tristan Warner-Smith

48
debe proporcionar identificaciones a las vistas de su hijo: tv1.setId (1); tv2.setId (2); Las vistas principales no asignan automáticamente a las vistas secundarias un Id, y el valor predeterminado para un Id es NO_ID. Los ID no tienen que ser únicos en una jerarquía de vistas; por lo tanto, 1, 2, 3, etc. son valores excelentes para usar, y debe usarlos para que el anclaje relativo funcione en RelativeLayout.
Sechastain

Respuestas:


196

Por lo que he podido reconstruir, debe agregar 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);

Todo el crédito para sechastain, para posicionar relativamente sus artículos mediante programación, debe asignarles identificadores.

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

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


1
en mi caso, aparte de establecer los identificadores de una vista de niño, agregar vista de niño a la vista de padre y llamar a requestLayout () en vista de niño antes de establecer las reglas de la otra vista de niño hizo que las cosas funcionaran. Espero que esto ayude a alguien
2cupsOfTech

En caso de que alguien todavía siga esto: cuando tengo una colección de vistas, estoy agregando una por una y no puedo estar seguro de que una de las vistas en la que se configura un elemento belowya se haya agregado, cómo asegurarse de que todavía se procese ¿correctamente? Según tengo entendido addView, el diseño se vuelve a representar, por lo que si lo hago para un elemento cuyo pariente aún no se ha agregado, se bloqueará ya que el elemento con esa ID aún no existe.
Megakoresh

1
set (id) debe usarse con generateViewId () Agregado en el nivel 17 de API. Genera un valor adecuado para usar en setId (int). Este valor no colisionará con los valores de ID generados en tiempo de construcción por aapt para R.id. Ejemplo: tv [l_idx] .setId (View.generateViewId ());
AndrewBloom

Esta respuesta está muy mal escrita. ¿Cuál es la razón detrás del uso de los parámetros RelationalLayout para un diseño lineal? ¿Y por qué agrega el diseño lineal a una vista principal?
pcodex

Hola @Prab, esta respuesta tiene ahora 9 años. Si tiene algún comentario positivo sobre cómo se puede mejorar la respuesta para las necesidades de hoy, siéntase libre de presionar el botón de edición y sugerirlo 👍
Tristan Warner-Smith

60

En pocas palabras: con un diseño relativo, coloca elementos dentro del diseño.

  1. crear un nuevo RelativeLayout.LayoutParams

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

    (lo que sea ... llene el contenido principal o de ajuste, números absolutos si es necesario, o referencia a un recurso XML)

  2. Agregar reglas: las reglas se refieren al padre u otros "hermanos" en la jerarquía.

    lp.addRule(RelativeLayout.BELOW, someOtherView.getId())
    lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
  3. Simplemente aplique los parámetros de diseño: la forma más 'saludable' de hacerlo es:

    parentLayout.addView(myView, lp)

Cuidado : no cambie el diseño de las devoluciones de llamada de diseño. Es tentador hacerlo porque es cuando las vistas obtienen su tamaño real. Sin embargo, en ese caso, se esperan resultados inesperados.


No tenía idea de que podría establecer parámetros para una Vista y agregar una Vista a su Vista principal, todo en una línea de código ( parentLayout.addView(myView, lp). Siempre hice eso en dos líneas de código. ¡Gracias!
Matt

Esto funcionó para mí y me ayudó mucho, pero ¿hay alguna manera de alinear al niño debajo de algo y luego establecer el margen arriba para ese niño? ¿O puedo establecer el margen a continuación para lo que está debajo y se ajustará correctamente?
Jacob Varner

Claro que lo hay. Puede establecer los márgenes (desafortunadamente, la API programática no es tan cómoda como el XML) con setMargins (izquierda, arriba, derecha, abajo). Está en los LayoutParams. Tenga en cuenta que el relleno es una propiedad de la vista, no los parámetros de diseño.
Meymann

29

Acabo de pasar 4 horas con este problema. Finalmente me di cuenta de que no debes usar cero como id de vista . Pensarías que está permitido como NO_ID == -1, pero las cosas tienden a enloquecer si lo das a tu vista ...


2
Yo veo lo mismo. Estoy usando API 7, y al ver el javadoc veo que setID (int) establece que la identificación debe ser un número entero positivo. Es algo que debe aplicarse en el código, no simplemente comentado en los documentos. developer.android.com/reference/android/view/…
OYRM

@OYRM Android es un desastre
SpaceMonkey

Posiblemente me has ahorrado una TONELADA de tiempo. ¡Gracias!
rjr-apps

9

Ejemplo ejecutable mínimo de Android 22

ingrese la descripción de la imagen aquí

Fuente:

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

Funciona con el proyecto predeterminado generado por android create project .... Repositorio de GitHub con un código de compilación mínimo .


1
¿Podría extender este ejemplo a 3 TextView? Lo intenté y TextViewfalta el tercero .
Zizheng Wu

7

llamada

tv1.setId(1) 

después

tv1.setText("A");

Si puedo preguntar, ¿cuál es la razón? Tengo un problema, pero estoy usando ImageView y TextView, así que me pregunto cuándo llamar a .setId () en ImageView.
Joshua G

FYI - Llamé a .setId () justo antes de agregar la vista y funcionó, no tengo idea de por qué ... jajaja
Joshua G

4

Tratar:

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
Bonito el ejemplo más útil. Gracias.
Vyacheslav

2

Este enfoque con ViewGroup.MarginLayoutParams funcionó para mí:

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

Si realmente desea diseñar manualmente, le sugiero que no use un diseño estándar en absoluto. Hágalo todo por su cuenta, aquí un ejemplo de 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)
    }
}

Esto podría darte una idea de cómo podría funcionar

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.