Como organizar vistas no RelativeLayout programaticamente?

234

Estou tentando obter o seguinte programaticamente (em vez de declarativamente via XML):

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

Em outras palavras, como faço para que o segundo TextViewapareça abaixo do primeiro, mas quero fazê-lo no 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);

Atualizar:

Obrigado, TreeUK. Eu entendo a direção geral, mas ainda não funciona - "B" se sobrepõe a "A". O que estou fazendo de errado?

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);
Dan
fonte
No seu exemplo de código, você não está realmente adicionando a regra de RelativeLayout.BELOW, tv1.getId ();
Tristan Warner-Smith
48
você precisa fornecer IDs para as visualizações do seu filho: tv1.setId (1); tv2.setId (2); As visualizações pai não atribuem automaticamente a IDs às visualizações filho, e o valor padrão para uma ID é NO_ID. Os IDs não precisam ser exclusivos em uma visualização de pesquisa - portanto, 1, 2, 3 etc. são todos bons valores para usar - e você deve usá-los para que a ancoragem relativa funcione no RelativeLayout.
precisa

Respostas:

196

Pelo que pude reunir, é necessário adicionar a exibição 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 o crédito a ser mantido, para posicionar relativamente seus itens de forma programática, é necessário atribuir IDs a eles.

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

Então addRule(RelativeLayout.RIGHT_OF, tv1.getId());

Tristan Warner-Smith
fonte
1
no meu caso, além de definir os IDs de uma childview, adicionar childview à parentview e chamar requestLayout () em childview antes de definir as regras da outra childview, as coisas funcionavam. Espero que isso ajude alguém
2cupsOfTech
Caso alguém ainda siga isso: quando eu tenho uma coleção de visualizações, estou adicionando uma por uma e não posso ter certeza de que uma das visualizações em que um item está definido para ser belowjá foi adicionada, como garantir que ele ainda seja renderizado corretamente? Pelo meu entendimento, addViewfaz com que o layout seja re-renderizado; portanto, se eu fizer isso para um item que ainda não foi adicionado, ele será travado, pois o item com esse ID ainda não existe.
21416 Megakoresh
1
set (id) deve ser usado com generateViewId () adicionado no nível 17. da API. Gera um valor adequado para uso em setId (int). Este valor não colidirá com os valores de ID gerados no tempo de construção por aapt para R.id. Exemplo: tv [l_idx] .setId (View.generateViewId ());
AndrewBloom
Esta resposta está muito mal escrita. Qual é a lógica por trás do uso de parâmetros RelationalLayout para um layout Linear? E por que você está adicionando o layout linear a uma exibição principal?
pcodex 28/09/19
Hey @Prab, esta resposta agora tem 9 anos. Se você tem algum feedback positivo sobre a forma como a resposta pode ser melhorado para as necessidades de hoje, sinta-se livre para acertar o botão de edição e sugerem que 👍
Tristan Warner-Smith
60

Resuma a história longa: com o layout relativo, você posiciona elementos dentro do layout.

  1. crie um novo RelativeLayout.LayoutParams

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

    (o que quer que seja ... preencha o conteúdo pai ou quebra automática, números absolutos, se necessário, ou faça referência a um recurso XML)

  2. Adicionar regras: as regras se referem aos pais ou a outros "irmãos" na hierarquia.

    lp.addRule(RelativeLayout.BELOW, someOtherView.getId())
    lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
  3. Basta aplicar os parâmetros de layout: A maneira mais "saudável" de fazer isso é:

    parentLayout.addView(myView, lp)

Cuidado : não altere o layout dos retornos de chamada do layout. É tentador fazê-lo porque é quando as visualizações obtêm seus tamanhos reais. No entanto, nesse caso, são esperados resultados inesperados.

Meymann
fonte
Eu não tinha ideia de que você poderia definir parâmetros para uma Visualização e adicionar uma Visualização à visualização pai em uma linha de código ( parentLayout.addView(myView, lp). Eu sempre fiz isso em duas linhas de código. Obrigado!
6163 Matt
Isso funcionou para mim e ajudou muito, mas existe uma maneira de alinhar a criança abaixo de algo e definir a margem acima para essa criança? Ou posso definir a margem abaixo para a coisa que está abaixo e ela será ajustada corretamente?
Jacob Varner
Claro que existe. Você pode definir as margens (infelizmente, a API programática não é tão confortável quanto o XML) com setMargins (esquerda, superior, direita, inferior). Está no LayoutParams. Observe que o preenchimento é uma propriedade da exibição, não os parâmetros de layout.
Meymann 13/08/14
29

Apenas passei 4 horas com esse problema. Finalmente percebi que você não deve usar zero como ID da visualização . Você pensaria que é permitido como NO_ID == -1, mas as coisas tendem a dar errado se você der a sua opinião ...

fastfox
fonte
2
Eu vejo o mesmo. Estou usando a API 7 e visualizando o javadoc, vejo que setID (int) afirma que o ID deve ser um número inteiro positivo. É algo que deve ser imposto no código, não simplesmente comentado nos documentos. developer.android.com/reference/android/view/…
OYRM
@OYRM Android é uma bagunça
spacemonkey
Você possivelmente só me salvou uma tonelada de tempo. Obrigado!
rjr-apps
9

Exemplo de executável mínimo do Android 22

insira a descrição da imagem aqui

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

Trabalha com o projeto padrão gerado por android create project .... Repositório GitHub com código de compilação mínimo .

Ciro Santilli adicionou uma nova foto
fonte
1
Você poderia estender este exemplo para 3 TextView? Eu tentei e o terceiro TextViewestá faltando.
Zizheng Wu
7

ligar

tv1.setId(1) 

depois de

tv1.setText("A");
user299870
fonte
Se posso perguntar, qual é o motivo? Estou tendo um problema, mas estou usando o ImageView e o TextView, por isso estou pensando em quando chamar .setId () no ImageView.
Joshua G
FYI - Eu chamei .setId () bem antes de eu adicionar a vista e deu certo, nenhuma idéia porque .. lol
Joshua G
4

Experimentar:

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);
Phantômaxx
fonte
1
Bom, o exemplo mais útil. Obrigado.
Vyacheslav
2

Essa abordagem com ViewGroup.MarginLayoutParams funcionou para mim:

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);
Gene Bo
fonte
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);
    }

}
Richa
fonte
0

Se você realmente deseja fazer o layout manualmente, sugiro não usar um layout padrão. Faça tudo por conta própria, aqui está um exemplo do 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)
    }
}

Isso pode lhe dar uma idéia de como isso poderia funcionar

martyglaubitz
fonte