Làm cách nào để bố trí Lượt xem trong RelativeLayout theo chương trình?


234

Tôi đang cố gắng để đạt được các chương trình sau (chứ không phải khai báo qua XML):

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

Nói cách khác, làm cách nào để làm cho cái thứ hai TextViewxuất hiện bên dưới cái thứ nhất, nhưng tôi muốn làm nó trong mã:

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

Cập nhật:

Cảm ơn, TreeUK. Tôi hiểu hướng chung, nhưng nó vẫn không hoạt động - "B" chồng chéo "A". Tôi đang làm gì sai?

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

Trong mẫu mã của bạn, bạn không thực sự thêm quy tắc RelativeLayout.BELOW, tv1.getId ();
Tristan Warner-Smith

48
bạn cần cung cấp id cho các lượt xem của con bạn: tv1.setId (1); tv2.setId (2); Các khung nhìn cha mẹ không tự động gán các khung nhìn con một Id và giá trị mặc định cho một Id là NO_ID. Id không phải là duy nhất trong chế độ xem hiearchy - vì vậy 1, 2, 3, v.v ... đều là các giá trị tốt để sử dụng - và bạn phải sử dụng chúng cho neo tương đối để hoạt động trong RelativeLayout.
sechastain

Câu trả lời:


196

Từ những gì tôi đã có thể ghép lại với nhau, bạn phải thêm chế độ xem bằng 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);

Tất cả tín dụng cho sechastain, để định vị tương đối các mục của bạn theo chương trình, bạn phải gán id cho chúng.

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

Sau đó addRule(RelativeLayout.RIGHT_OF, tv1.getId());


1
trong trường hợp của tôi ngoài việc thiết lập id của một khung nhìn trẻ em, thêm khung nhìn con vào khung nhìn cha mẹ và gọi requestLayout () trên khung nhìn con trước khi đặt các quy tắc của khung nhìn con khác làm cho mọi thứ hoạt động. Hy vọng điều này sẽ giúp được ai đó
2cupsOfTech

Trong trường hợp bất cứ ai vẫn tuân theo điều này: khi tôi có một bộ sưu tập các lượt xem, tôi sẽ thêm từng lượt một và tôi không thể chắc chắn rằng một trong những khung nhìn mà một mục được đặt belowđã được thêm vào, làm thế nào để đảm bảo rằng nó vẫn hiển thị đúng không? Theo hiểu biết của tôi addViewkhiến bố cục hiển thị lại, vì vậy nếu tôi làm điều đó cho một mục mà người thân chưa được thêm vào, nó sẽ bị sập vì mục có ID đó chưa tồn tại.
Megakoresh

1
set (id) nên được sử dụng với GenerViewId () Đã thêm vào cấp độ API 17. Nó tạo ra một giá trị phù hợp để sử dụng trong setId (int). Giá trị này sẽ không va chạm với các giá trị ID được tạo tại thời điểm xây dựng bởi aapt cho R.id. Ví dụ: tv [l_idx] .setId (View.generateViewId ());
AndrewBloom

Câu trả lời này được viết rất tệ. Lý do đằng sau việc sử dụng các tham số RelationalLayout cho bố cục tuyến tính là gì? Và tại sao bạn lại thêm Bố cục tuyến tính vào Chế độ xem phụ huynh?
pcodex

Này @Prab, câu trả lời này đã 9 tuổi rồi. Nếu bạn có một số phản hồi tích cực về cách cải thiện câu trả lời cho nhu cầu ngày nay, xin vui lòng nhấn nút chỉnh sửa và đề xuất nó
Tristan Warner-Smith

60

Cắt ngắn câu chuyện dài: Với bố cục tương đối, bạn định vị các yếu tố bên trong bố cục.

  1. tạo một RelativeLayout.LayoutParams mới

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

    (bất cứ điều gì ... điền vào nội dung cha mẹ hoặc bọc, số tuyệt đối nếu bạn phải hoặc tham chiếu đến tài nguyên XML)

  2. Thêm quy tắc: Quy tắc đề cập đến cha mẹ hoặc các "anh em" khác trong hệ thống phân cấp.

    lp.addRule(RelativeLayout.BELOW, someOtherView.getId())
    lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
  3. Chỉ cần áp dụng các thông số bố cục: Cách 'lành mạnh' nhất để làm điều đó là:

    parentLayout.addView(myView, lp)

Xem ra : Không thay đổi bố cục từ các cuộc gọi lại bố trí. Thật hấp dẫn để làm như vậy bởi vì đây là khi lượt xem có kích thước thực tế của chúng. Tuy nhiên, trong trường hợp đó, kết quả bất ngờ được mong đợi.


Tôi không biết bạn có thể đặt tham số thành Chế độ xem và thêm Chế độ xem cho phụ huynh Xem tất cả trong một dòng mã ( parentLayout.addView(myView, lp). Tôi luôn luôn làm điều đó trong hai dòng mã. Cảm ơn bạn!
Matt

Điều này làm việc cho tôi và giúp đỡ rất nhiều, nhưng có cách nào để sắp xếp đứa trẻ bên dưới một cái gì đó và sau đó đặt lề trên cho đứa trẻ đó không? Hoặc tôi có thể đặt lề dưới cho điều bên dưới và nó có điều chỉnh chính xác không?
Jacob Varner

Chắc chắn là có. Bạn có thể đặt lề (thật không may, API lập trình không thoải mái như XML) với setMargins (trái, trên, phải, dưới). Nó nằm trong LayoutParams. Lưu ý rằng phần đệm là một thuộc tính của khung nhìn, không phải là các tham số bố trí.
Meymann

29

Chỉ cần dành 4 giờ với vấn đề này. Cuối cùng nhận ra rằng bạn không được sử dụng zero làm id xem . Bạn sẽ nghĩ rằng nó được cho phép là NO_ID == -1, nhưng mọi thứ có xu hướng trở nên rắc rối nếu bạn đưa nó vào chế độ xem của mình ...


2
Tôi thấy như vậy. Tôi đang sử dụng API 7 và xem javadoc Tôi thấy rằng setID (int) nói rằng id phải là một số nguyên dương. Đó là một cái gì đó nên được thi hành trong mã, không chỉ đơn giản là nhận xét trong các tài liệu. developer.android.com/reference/android/view/NH
OYRM

@OYRM Android là một mớ hỗn độn
SpaceMonkey

Bạn hoàn toàn có thể chỉ tiết kiệm cho tôi một TẤN thời gian. Cảm ơn!
rjr-apps

9

Ví dụ chạy tối thiểu Android 22

nhập mô tả hình ảnh ở đây

Nguồn:

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

Hoạt động với dự án mặc định được tạo bởi android create project .... Kho GitHub với mã xây dựng tối thiểu .


1
Bạn có thể mở rộng ví dụ này lên 3 TextViewkhông? Tôi đã thử và thứ ba TextViewlà mất tích.
Zizheng Wu

7

gọi

tv1.setId(1) 

sau

tv1.setText("A");

Nếu tôi có thể hỏi, lý do là gì? Tôi đang gặp sự cố nhưng tôi đang sử dụng ImageView cũng như TextView vì vậy tôi tự hỏi khi nào nên gọi .setId () trên ImageView.
Joshua G

FYI - Tôi đã gọi .setId () ngay trước khi tôi thêm chế độ xem và nó hoạt động, không biết tại sao .. lol
Joshua G

4

Thử:

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
Đẹp là ví dụ hữu ích nhất. Cảm ơn.
Vyacheslav

2

Cách tiếp cận này với Viewgroup.MarginLayoutParams làm việc cho tôi:

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

Nếu bạn thực sự muốn bố trí thủ công, tôi khuyên bạn không nên sử dụng bố cục tiêu chuẩn. Tự làm tất cả, đây là một ví dụ về 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)
    }
}

Điều này có thể cho bạn một ý tưởng làm thế nào điều này có thể làm việc

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.