Как программно выложить Views в RelativeLayout?


234

Я пытаюсь достичь следующего программно (а не декларативно через XML):

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

Другими словами, как сделать так, чтобы второе TextViewотображалось под первым, но я хочу сделать это в коде:

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

Обновить:

Спасибо, TreeUK. Я понимаю общее направление, но оно все еще не работает - «Б» перекрывает «А». Что я делаю не так?

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

В вашем примере кода вы фактически не добавляете правило RelativeLayout.BELOW, tv1.getId ();
Тристан Уорнер-Смит

48
вам нужно предоставить идентификаторы для ваших дочерних представлений: tv1.setId (1); tv2.setId (2); Родительские представления автоматически не присваивают дочерним представлениям Id, и значением по умолчанию для Id является NO_ID. Идентификаторы не обязательно должны быть уникальными в иерархии представлений - так что 1, 2, 3 и т. Д. - все это хорошие значения, и вы должны использовать их для относительной привязки, чтобы работать в RelativeLayout.
sechastain

Ответы:


196

Из того, что я смог собрать вместе, вы должны добавить представление, используя 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);

Вся заслуга в sechastain, чтобы программно позиционировать ваши предметы, вы должны назначить им идентификаторы.

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

затем addRule(RelativeLayout.RIGHT_OF, tv1.getId());


1
в моем случае, кроме установки идентификаторов childview, добавление childview к parentview и вызов requestLayout () для childview перед установкой правил другого childview заставили все работать. Надеюсь, это кому-нибудь поможет
2cupsOfTech

Если кто-то все еще следует этому: когда у меня есть коллекция представлений, я добавляю одно за другим, и я не могу быть уверен, что одно из представлений, для которых задан элемент, belowуже добавлено, как убедиться, что оно по-прежнему отображается правильно? addViewНасколько я понимаю, макет перерисовывается, поэтому, если я сделаю это для элемента, родственник которого еще не был добавлен, он вылетит, поскольку элемент с таким идентификатором еще не существует.
Мегакореш

1
set (id) должен использоваться с generateViewId (), добавленным на уровне API 17. Он генерирует значение, подходящее для использования в setId (int). Это значение не будет конфликтовать со значениями идентификатора, сгенерированными во время сборки aapt для R.id. Пример: tv [l_idx] .setId (View.generateViewId ());
AndrewBloom

Этот ответ очень плохо написан. В чем смысл использования параметров RelationalLayout для линейного макета? И почему вы добавляете линейный макет в родительский вид?
pcodex

Привет @Prab, этому ответу сейчас 9 лет. Если у вас есть положительные отзывы о том, как можно улучшить ответ для сегодняшних нужд, пожалуйста, не стесняйтесь нажать кнопку «Изменить» и предложить ее 👍
Тристан Уорнер-Смит,

60

Короче говоря, с относительной разметкой вы размещаете элементы внутри разметки.

  1. создать новый RelativeLayout.LayoutParams

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

    (что угодно ... заполнить родительский или перенос содержимого, абсолютные числа, если необходимо, или ссылку на ресурс XML)

  2. Добавить правила: Правила относятся к родителю или другим «братьям» в иерархии.

    lp.addRule(RelativeLayout.BELOW, someOtherView.getId())
    lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
  3. Просто примените параметры макета: самый «здоровый» способ сделать это:

    parentLayout.addView(myView, lp)

Остерегайтесь : не изменяйте макет из обратных вызовов макета. Это заманчиво, потому что это когда представления получают свои реальные размеры. Однако в этом случае ожидаются неожиданные результаты.


Я понятия не имел, что вы можете установить параметры для View и добавить View к его родительскому View. Все в одной строке кода ( parentLayout.addView(myView, lp). Я всегда делал это в две строки кода. Спасибо!
Мэтт

Это сработало для меня и очень помогло, но есть ли способ выровнять ребенка под чем-то, а затем установить поле выше для этого ребенка? Или я могу установить поле ниже для того, что ниже, и будет ли оно корректироваться?
Джейкоб Варнер

Конечно, есть. Вы можете установить поля (к сожалению, программный API не так удобен, как XML) с помощью setMargins (слева, сверху, справа, снизу). Это в LayoutParams. Обратите внимание, что заполнение является свойством представления, а не параметров макета.
Мейман

29

Просто провел 4 часа с этой проблемой. Наконец понял, что вы не должны использовать ноль в качестве идентификатора представления . Вы могли бы подумать, что это разрешено как NO_ID == -1, но если вы дадите это на ваше усмотрение, все пойдет не так ...


2
Я вижу то же самое. Я использую API 7, и просматривая javadoc, я вижу, что setID (int) действительно утверждает, что id должен быть положительным целым числом. Это то, что должно быть реализовано в коде, а не просто прокомментировано в документации. developer.android.com/reference/android/view/…
OYRM

@OYRM Android - беспорядок
SpaceMonkey

Вы, возможно, просто сэкономили мне тонну времени. Спасибо!
rjr-apps

9

Пример минимального запуска Android 22

введите описание изображения здесь

Источник:

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

Работает с проектом по умолчанию, созданным android create project .... GitHub репозиторий с минимальным кодом сборки .


1
Не могли бы вы расширить этот пример до 3 TextView? Я пытался и третий TextViewотсутствует.
Zizheng Wu

7

вызов

tv1.setId(1) 

после

tv1.setText("A");

Если я могу спросить, в чем причина? У меня проблема, но я использую ImageView, а также TextView, поэтому мне интересно, когда вызывать .setId () для ImageView.
Джошуа G

К вашему сведению - я позвонил .setId () прямо перед тем, как добавить представление, и это сработало, понятия не имею, почему .. LOL
Джошуа G

4

Пытаться:

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
Хороший самый полезный пример. Спасибо.
Вячеслав

2

Этот подход с ViewGroup.MarginLayoutParams работал для меня:

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

Если вы действительно хотите создать макет вручную, я бы посоветовал вообще не использовать стандартный макет. Сделайте все это самостоятельно, вот пример 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)
    }
}

Это может дать вам представление о том, как это может работать

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.