Як програматизувати перегляди в 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. Я розумію загальний напрямок, але він все ще не працює - "B" перекривається "A". Що я роблю неправильно?

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

Увесь кредит на постійне розташування, щоб відносно позиціонувати свої предмети програмно, ви повинні призначити їм ідентифікатори.

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

Тоді addRule(RelativeLayout.RIGHT_OF, tv1.getId());


1
у моєму випадку, крім встановлення ідентифікаторів дитячого перегляду, додавання перегляду дитини до батьківського перегляду та виклику requestLayout () у режимі перегляду дітей перед тим, як встановити правила іншого перегляду дитини, все призвело до роботи. Сподіваюся, що це комусь допоможе
2cupsOfTech

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

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

Ця відповідь дуже погано написана. Яке обґрунтування використання параметрів RelationalLayout для лінійного макета? І чому ви додаєте лінійний макет до виду батьків?
pcodex

Привіт @ Праб, зараз ця відповідь 9 років. Якщо у вас є позитивні відгуки про те, як відповідь можна покращити на сьогоднішні потреби, будь ласка, натисніть кнопку редагування та запропонуйте це 👍
Tristan Warner-Smith

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)

Слідкуйте за тим, щоб не змінювати макет із зворотних викликів макета. Це зробити спокусливо, тому що це коли перегляди набувають свого фактичного розміру. Однак у цьому випадку очікуються несподівані результати.


Я не мав уявлення, що ви можете встановити параметри для представлення даних і додати вигляд до його батьківського списку Переглянути все в одному рядку коду ( parentLayout.addView(myView, lp). Я завжди робив це у двох рядках коду. Дякую!
Метт

Це працювало для мене і багато допомагало, але чи є спосіб вирівняти дитину під щось, а потім встановити верхню межу для цієї дитини? Або я можу встановити поле внизу для того, що воно знаходиться внизу, і чи правильно воно буде скориговано?
Якоб Варнер

Звичайно, є. Ви можете встановити поля (на жаль, програмний API не такий зручний, як XML) за допомогою setMargins (зліва, вгорі, вправо, внизу). Це в LayoutParams. Зауважте, що прокладка - це властивість перегляду, а не параметри компонування.
Мейман

29

Щойно провів 4 години з цією проблемою. Нарешті зрозумів, що не потрібно використовувати нуль як ідентифікатор перегляду . Ви б могли подумати, що це дозволено як NO_ID == -1, але, як правило, речі йдуть якнайкраще, якщо ви надасте його перегляду ...


2
Я бачу те саме. Я використовую API 7 і переглядаючи javadoc, я бачу, що setID (int) констатує, що ідентифікатор повинен бути натуральним числом. Це щось, що повинно бути застосовано в коді, а не просто коментуватися в документах. 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.
Джошуа Г

FYI - я зателефонував .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

Якщо ви дійсно хочете компонувати вручну, я б запропонував взагалі не використовувати стандартний макет. Робіть все самостійно, ось приклад котліна:

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.