如何以编程方式在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();。
Tristan Warner-Smith'2

48
您需要为子视图提供ID:tv1.setId(1); tv2.setId(2); 父视图不会自动为子视图分配ID,并且ID的默认值为NO_ID。id在视图层次结构中不必唯一-因此1、2、3等都是可以使用的优良值-您必须将它们用于相对锚定才能在RelativeLayout中工作。
sechastain 2011年

Answers:


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

所有值得信赖的东西,要以编程方式相对定位您的商品,您必须为其分配ID。

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

然后 addRule(RelativeLayout.RIGHT_OF, tv1.getId());


1
就我而言,除了设置子视图的ID之外,还可以在将子视图添加到父视图中并在设置其他子视图的规则之前在子视图上调用requestLayout()使其工作。希望这对某人有帮助
2cupsOfTech

如果有人仍然遵循此方法:当我有一个视图集合时,我将一个一个地添加,并且我不能确定已将某个项目设置为一个视图below,如何确定它仍会呈现正确吗?据我了解addView,布局会重新渲染,因此,如果我对尚未添加亲戚的项目进行布局,则会崩溃,因为具有该ID的项目尚不存在。
Megakoresh

1
set(id)应该与API级别17中添加的generateViewId()一起使用。它生成适合在setId(int)中使用的值。此值不会与Aapt for R.id在生成时生成的ID值冲突。示例:tv [l_idx] .setId(View.generateViewId());
AndrewBloom'17年

这个答案写得很差。在线性布局中使用RelationalLayout参数的背后原理是什么?以及为什么要将线性布局添加到父视图?
pcodex

嘿@Prab,这个答案现在已经9岁了。如果您对如何满足当今的需求可以改善答案有正面的反馈,请随时单击编辑按钮并提出建议suggest
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)中将参数设置为View并将View添加到其父View中。我总是用两行代码来做到这一点。谢谢!
马特

这对我很有用,并且对我有很大帮助,但是是否有办法将孩子对准下面的东西,然后为孩子设置上面的边距?还是可以为下面的东西设置下面的边距,并且可以正确调整?
Jacob Varner 2014年

当然可以。您可以使用setMargins(左,上,右,下)来设置页边距(不幸的是,编程API不如XML)。在LayoutParams中。请注意,填充是视图的属性,而不是布局参数。
Meymann 2014年

29

刚刚花了4个小时来解决这个问题。最终意识到,您不能使用零作为视图ID。您可能会认为它被允许为NO_ID == -1,但是如果您给出自己的看法,事情就会变得一团糟...


2
我也一样 我正在使用API​​ 7,并查看javadoc,我看到setID(int)确实指出ID应该是一个正整数。这应该在代码中强制执行,而不仅仅是在文档中进行注释。developer.android.com/reference/android/view/…–
OYRM

@OYRM Android
真是

您很可能只是为我节省了很多时间。谢谢!
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,所以我想知道何时在ImageView上调用.setId()。
约书亚G

仅供参考-我在添加视图之前就调用了.setId(),它起作用了,不知道为什么..哈哈
Joshua 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
尼斯是最有用的例子。谢谢。
Vyacheslav

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

这可能会让您知道如何操作

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.