จะทำให้ RelativeLayout ทำงานร่วมกับการผสานและรวมได้อย่างไร


114

ฉันพยายามมาสองสามวันแล้วเพื่อทำให้เลย์เอาต์ของฉันมีประสิทธิภาพมากขึ้นโดยการแปลงจากการใช้หลายระดับที่ซ้อนกันLinearLayoutsเป็นหนึ่งRelativeLayoutและพบปัญหาเล็กน้อยที่ฉันไม่สามารถหาวิธีแก้ปัญหาสำหรับ ...

ฉันค้นหากลุ่มผู้เริ่มต้นใช้งาน Android และไซต์นี้แล้วและไม่พบสิ่งใดที่จะช่วยฉันแก้ปัญหาได้

ฉันอ่านในบล็อกหนึ่งที่คุณสามารถรวมเค้าโครงเข้ากับการรวมและรวมแท็กได้ ดังนั้นสิ่งที่ฉันมีคือไฟล์เลย์เอาต์หลักที่มีRelativeLayoutองค์ประกอบรูท ภายในนั้นฉันมี 5 แท็กที่อ้างอิงไฟล์เลย์เอาต์ xml ที่แตกต่างกัน 5 ไฟล์ซึ่งแต่ละไฟล์มีองค์ประกอบผสานสำหรับรูท (ไฟล์ผสานทั้งหมดของฉันเหมือนกันยกเว้นรหัสในนั้น)

ฉันพบปัญหาสองประการซึ่งฉันจะอธิบายหลังจากโพสต์โค้ดเค้าโครงเวอร์ชันที่เรียบง่าย:

ตัวอย่างไฟล์เค้าโครงหลัก:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/translucent_gray" >

    <include 
        android:id="@+id/running_gallery_layout_id"
        layout="@layout/running_gallery_layout" />

    <include 
        android:id="@+id/recent_gallery_layout_id" 
        layout="@layout/recent_gallery_layout"
        android:layout_below="@id/running_gallery_layout_id" />

    <include
        android:id="@+id/service_gallery_layout_id"
        layout="@layout/service_gallery_layout"
        android:layout_below="@id/recent_gallery_layout_id" />

    <include
        android:id="@+id/process_gallery_layout_id"
        layout="@layout/process_gallery_layout"
        android:layout_below="@id/service_gallery_layout_id" />

</RelativeLayout>

ตัวอย่างรวมไฟล์ผสาน:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView 
        style="@style/TitleText"
        android:id="@+id/service_gallery_title_text_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:text="@string/service_title" />

    <Gallery
        android:id="@+id/service_gallery_id"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_below="@id/service_gallery_title_text_id" />

    <TextView 
        style="@style/SubTitleText"
        android:id="@+id/service_gallery_current_text_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/service_gallery_title_text_id"
        android:layout_above="@id/service_gallery_id" />
</merge>

ฉันพบปัญหาสองประการ:

1) android:layout_*ดูเหมือนว่าแอตทริบิวต์จะถูกละเว้นเมื่อใช้ในแท็กรวมและรูปแบบที่ผสานทั้งหมดจะแสดงอยู่ด้านบนของกันและกัน ตามโพสต์นี้ ( http://developer.android.com/resources/articles/layout-tricks-reuse.html ) " android:layout_*แอตทริบิวต์ใด ๆ ที่สามารถใช้กับ<include />แท็ก"

2) เนื่องจากฉันไม่สามารถทำงานนี้ได้ฉันจึงตัดสินใจลองเพิ่มandroid:layout_belowแอตทริบิวต์ให้กับTextViewรายการแรกในไฟล์เค้าโครงผสานแต่ละไฟล์ซึ่งหมายความว่าไฟล์ผสานแต่ละไฟล์จะอ้างอิง id จากไฟล์เค้าโครงการผสานอื่น ... ส่วนใหญ่แล้ว ใช้งานได้จริงและเค้าโครงของฉันก็ดูดี อย่างไรก็ตามฉันได้รับข้อผิดพลาดเกี่ยวกับandroid:layout_belowแอตทริบิวต์หนึ่งที่แจ้งว่าไม่พบรหัสที่ฉันระบุ ... ฉันได้ตรวจสอบรหัสสองครั้งและสามครั้งเพื่อให้แน่ใจว่าถูกต้อง ส่วนที่แปลกที่สุดคือฉันใช้AutoFillคุณลักษณะนี้เพื่อใส่รหัสในแอตทริบิวต์ตั้งแต่แรก

หากใครมีข้อเสนอแนะหรือวิธีแก้ปัญหาฉันยินดีเป็นอย่างยิ่งที่จะลองใช้ นอกจากนี้หากใครสามารถคิดวิธีให้ฉันมีไฟล์เลย์เอาต์ xml ผสานหนึ่งไฟล์แทนที่จะเป็น 5 ที่จะได้รับการชื่นชมอย่างมาก ฉันหาวิธีทำไม่ได้เพราะฉันต้องการเข้าถึงแต่ละรายการในไฟล์เค้าโครงผสานที่รันไทม์ ...

คำตอบ:


214

มีปัญหากับแท็กรวม ตรวจสอบ: https://issuetracker.google.com/issues/36908001

ในการแก้ไขตรวจสอบให้แน่ใจว่าคุณได้เขียนทับทั้งสองlayout_widthและlayout_heightเมื่อรวมไว้มิฉะนั้นทุกอย่างจะถูกละเว้น


15
นี่เป็นวิธีแก้ปัญหาที่ดีกว่าวิธีที่ยอมรับเนื่องจากหลีกเลี่ยงการสร้างวัตถุโครงร่างที่ไม่จำเป็น นอกจากนี้มันแย่แค่ไหนตามที่ Android devs นี่เป็นตกลง
mikołak

4
นี่เป็นวิธีที่ง่ายกว่าฮาร์ดโค้ดน้อยกว่าและมีโซลูชันที่เหมาะสมกว่าการบรรจุ <รวม /> ไว้ในรูปแบบอื่น คิดว่าคุณจะทำอะไรถ้าคุณจะทำงานกับรายการ
teoREtik

2
สิ่งนี้ได้ผลดีกว่าคำตอบที่ยอมรับ ขอบคุณมาก! และ ... c'mon google แก้ไขปัญหานี้แล้วนี่คือ BS! :)
Felipe Caldas

2
@JeffAxelrod ซอร์สโค้ด LayoutInflater แสดงว่าไม่มีการใช้ id, visibility และ layout_ * การลบล้างแท็กเมื่อองค์ประกอบ root เป็นแท็กผสาน เนื่องจากคุณไม่สามารถมี View เป็นรูท xml ได้เราจึงต้องมี ViewGroup เพิ่มเติมที่นั่น ...
Rafael Nobre

13
มันไม่ได้ผลสำหรับฉัน ฉันมีทั้งสองอย่างlayout_widthและตั้งค่าlayout_heightไว้ใน<include>ไฟล์. ฉันพยายามตั้งค่าlayout_widthและlayout_heightกับของฉัน<merge>แต่ไม่ประสบความสำเร็จ ฉันขาดอะไรไป?
dum4ll3

33

ดูคำตอบที่ได้รับการโหวตมากขึ้นด้านล่าง ของฉันล้าสมัยไปมาก


ฉันสามารถแก้ไขปัญหาหนึ่งที่จัสตินยกขึ้น: การไม่สามารถใช้ RelativeLayout เพื่อจัดการตำแหน่งของการรวม (อย่างน้อยก็ในกรณีง่ายๆนี้ในตัวจำลอง 1.6)

CommonsWareแนะนำให้รวมรวมไว้ในคอนเทนเนอร์หลักที่ไม่ซ้ำกัน แต่ทำเช่นนั้นเพื่อช่วยในการระบุที่อยู่และกำหนดขอบเขตมุมมองที่มีชื่อเหมือนกันภายในจัสตินรวมถึง

แต่ละอันจะต้องมีคอนเทนเนอร์หลักที่ไม่ซ้ำกันและคุณจะเรียก findViewById () บนคอนเทนเนอร์นั้น (ViewGroup) แทนที่จะเป็นใน Activity

ในความเป็นจริงคุณต้องทำเพื่อให้ RelativeLayout ทำงานตามที่คาดไว้:

ใช้งานได้ ( ส่วนท้ายอยู่ในตำแหน่งที่ดี):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent">
    <include android:id="@+id/header" layout="@layout/header"
        android:layout_alignParentTop="true" />
    <WebView android:id="@+id/webView" android:layout_below="@id/header"
        android:background="#77CC0000" android:layout_height="wrap_content"
        android:layout_width="fill_parent" android:focusable="false" />
    <LinearLayout android:layout_alignParentBottom="true"
        android:layout_height="wrap_content" android:layout_width="fill_parent">
        <include android:id="@+id/footer" layout="@layout/footer" />
    </LinearLayout>
</RelativeLayout>

สิ่งนี้ไม่ได้ ( ส่วนท้ายลอยอยู่ที่ด้านบนของหน้าจอ):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent">
    <include android:id="@+id/header" layout="@layout/header"
        android:layout_alignParentTop="true" />
    <WebView android:id="@+id/webView" android:layout_below="@id/header"
        android:background="#77CC0000" android:layout_height="wrap_content"
        android:layout_width="fill_parent" android:focusable="false" />
    <include android:id="@+id/footer" layout="@layout/footer"
        android:layout_alignParentBottom="true" />
</RelativeLayout>

ส่วนท้ายเปล่าจะไม่จัดชิดด้านล่างของพาเรนต์หากไม่มี LinearLayout โดยรอบ .. ฉันจะไม่เรียกสิ่งนี้ว่าพฤติกรรมที่คาดหวัง

นอกจากนี้ดูเหมือนว่า WebView จะแนบตัวเองเข้ากับส่วนหัวด้วย ID แต่ฉันเชื่อว่านี่เป็นภาพลวงตาเนื่องจากมันไหลไปด้านล่างส่วนหัวในแนวตั้ง ฉันพยายามตั้งค่าปุ่มที่อยู่เหนือส่วนท้ายด้วย แต่มันก็ลอยและผิดด้วยเช่นกัน

RelativeLayout มีปัญหามากกว่าใน 1.5 แต่ฉันก็ยังชอบ :)


2
ฉันเพิ่มขึ้นทีละ 1 แล้วลดลงกลับเห็นความคิดเห็นของ @Macarse นั่นเป็นวิธีที่ถูกต้องในการทำ
Jayshil Dave

โปรดลบคำตอบที่ทำให้เข้าใจผิดนี้ :(
Daniel Smith

8

ผู้ชายคนนี้เก่า แต่ดูเหมือนว่าจะปรากฏขึ้นที่ด้านบนของการค้นหาดังนั้นฉันจะแสดงความคิดเห็น

ฉันคิดว่าเคล็ดลับตรงนี้คือ<merge>แท็กที่รวมกับ<include>แท็กจะลบกลุ่มมุมมอง "ระดับบนสุด" ทุกประเภทในระดับนั้น ถ้าอย่างนั้นคุณขอให้ "layout_below" เป็นใครกันแน่ ไม่มีใคร. ไม่มีมุมมองในระดับนั้น

<merge>แท็กเตะมุมมองของเด็กและพวกเขาปรากฏลงในปกครองของ<include>แท็ก ดังนั้นคุณต้องขอให้เด็ก ๆ ในเค้าโครงที่คุณรวมไว้เพื่อยึดตัวเองตามนั้น


4

สำหรับการวางตำแหน่งเพื่อทำงานบน RelativeLayout คุณต้องตั้งค่าพารามิเตอร์ layout_ * ในไฟล์ include ไม่ใช่ในไฟล์เค้าโครงหลัก ทางนั้น

main_layout.xml

<RelativeLayout
  android:id="@+id/header"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content">
   ....
</RelativeLayout>

<RelativeLayout 
  android:id="@+id/footer"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_alignParentBottom="true">
    .....
</RelativeLayout>

<include layout="@layout/content_layout" />

content_layout.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
    android:id="@+id/content"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_above="@id/footer"
    android:layout_below="@id/header" >

    ....
</RelativeLayout>
</merge>

เห็นได้ชัดว่านี่ไม่ใช่สิ่งที่นักพัฒนาต้องการ แต่เป็นทางออกเดียวที่ฉันพบเพื่อหลีกเลี่ยงการทำ xml ซ้ำ


1
ทำไมไม่มีการโหวตเพิ่ม สิ่งนี้ได้ผลสำหรับฉัน ฉันไม่ชอบการติดพารามิเตอร์ลงในวัตถุซึ่งอาจรวมอยู่ในเลย์เอาต์ที่ไม่ต้องการ แต่ถ้าเป็น LinLay ดูเหมือนว่า layout_ * ของ RelLay จะถูกละเลย ฉันพลาดอะไรไปรึเปล่า?
QED

1

ดูเหมือนว่าแอตทริบิวต์ android: layout_ * จะถูกละเว้นเมื่อใช้ในแท็กรวมและรูปแบบที่ผสานทั้งหมดจะแสดงอยู่ด้านบนของกันและกัน

ฉันเดาว่าคุณไม่สามารถอ้างอิงจากกฎโครงร่างandroid:idแอตทริบิวต์ที่กำหนดบน<include>องค์ประกอบเฉพาะที่อยู่ในวิดเจ็ตและคอนเทนเนอร์ "จริง"

นอกจากนี้หากใครสามารถคิดวิธีให้ฉันมีไฟล์เลย์เอาต์ xml ผสานหนึ่งไฟล์แทนที่จะเป็น 5 ที่จะได้รับการชื่นชมอย่างมาก

ง่าย ๆ : รวมไว้ในไฟล์เดียว

ฉันหาวิธีทำไม่ได้เพราะฉันต้องการเข้าถึงแต่ละรายการในไฟล์เค้าโครงผสานที่รันไทม์

ไม่ว่าคุณจะมี<include>องค์ประกอบเดียวหรือ 1,000 รายการเนื้อหาทั้งหมดควรเข้าถึงได้ในรันไทม์ ข้อยกเว้นประการหนึ่งคือหากคุณมีandroid:idแอตทริบิวต์ที่ซ้ำกัน- คุณจะต้องกำหนดขอบเขตการfindViewById()โทรของคุณอย่างเหมาะสมเพื่อให้ได้มาซึ่งสิ่งที่ถูกต้องเช่นเดียวกับที่คุณต้องทำเมื่อรับวิดเจ็ตจากแถว ListView

หากคุณสามารถสร้างโปรเจ็กต์ตัวอย่างที่ใช้ไฟล์ผสาน 2+ ไฟล์ที่คุณสามารถแสดงให้เห็นว่าเนื้อหาไม่สามารถเข้าถึงได้ในรันไทม์โปรดแจ้งให้เราทราบ


1
ฉันไม่ต้องการรวมทั้งหมดไว้ในไฟล์เลย์เอาต์ขนาดใหญ่เพียงไฟล์เดียวเพราะมันยากต่อการจัดการในระยะยาว ... นั่นคือเหตุผลที่ฉันขอความเป็นไปได้ที่จะมี xml หลักหนึ่งไฟล์พร้อมไฟล์ผสานหนึ่งไฟล์ ... เพราะตอนนี้ ฉันมีไฟล์ 5 ไฟล์ที่มีองค์ประกอบผสานเป็นรูทที่มีเค้าโครงเหมือนกันทุกประการยกเว้นว่ารหัสจะแตกต่างกัน ฉันทำแบบนั้นเพื่อที่ฉันจะสามารถเข้าถึงได้ในขณะรันไทม์ ดูเหมือนว่าการกำหนดขอบเขตการเรียก findViewById () ของฉันคือสิ่งที่ฉันต้องการทำ คุณกำหนดขอบเขตการเรียกนั้นอย่างไรเพื่อให้คุณสามารถรวมไฟล์เลย์เอาต์เดียวกันหลาย ๆ ครั้งและยังสามารถเข้าถึงส่วนประกอบทั้งหมดในรันไทม์ได้
จัสติน

1
ขอบคุณสำหรับการตอบกลับ ฉันจะดูว่า ในระหว่างนี้ (และบางทีฉันอาจจะเปิดเผยความไม่รู้ของฉันที่นี่) จะไม่รวมแท็กรวมแต่ละแท็กในคอนเทนเนอร์หลักจะทำให้จุดประสงค์ของการใช้ RelativeLayout หรือไม่ hype ทั้งหมดของ RelativeLayout คือการหลีกเลี่ยงเลย์เอาต์ที่ซ้อนกัน ...
จัสติน

3
เหตุผลเดียวที่ฉันถามเกี่ยวกับเรื่องนี้คือเพื่อประหยัดพื้นที่และได้รับการออกแบบที่ดี ... ฉันอ่านเกี่ยวกับการนำกลับมาใช้ใหม่ได้ที่นี่: developer.android.com/resources/articles/…และคิดว่าฟังดูดี เมื่อฉันลองใช้มันฉันพบปัญหาบางอย่าง ตอนนี้ฉันมีเลย์เอาต์ 5 แบบที่ซ้ำกันเป็นหลักยกเว้น id อยู่ในนั้น ... ดังนั้นฉันคิดว่าการใช้เลย์เอาต์ซ้ำน่าจะเป็นตัวเลือกที่ดี บางทีฉันอาจจะพลาดอะไรบางอย่างที่นี่ แต่ดูเหมือนว่า RelativeLayout จะไม่ใช่ทั้งหมดที่ควรจะเป็น ...
Justin

1
ว้าว ... ขอบคุณสำหรับการช่วยเหลืออย่างไม่น่าเชื่อ โดยทั่วไปคำตอบของคุณมีประโยชน์มากดังนั้นฉันจึงไม่รู้ว่าคุณกำลังมีวันที่เลวร้ายหรืออะไร แต่ฉันแค่พยายามทำความเข้าใจแนวคิดเบื้องหลัง RelativeLayout ให้ดีขึ้นแท็กรวมและแท็กผสานตาม บทความที่ฉันได้อ่านและพยายามหาวิธีแก้ปัญหาที่เหมาะสมสำหรับเค้าโครงที่ฉันต้องการบรรลุ
Justin

1
"และสำหรับการนำกลับมาใช้ใหม่อย่างแท้จริงการสร้างคลาส View แบบกำหนดเองที่สำคัญกว่า ได้แก่ " ตกลง ฉันไม่อยากทำแบบนั้นเลยถ้ามีวิธีที่ค่อนข้างง่ายในการใช้เลย์เอาต์พื้นฐาน ... "คุณคัดลอก 'tude - โปรดอย่าแปลกใจเมื่อมีคนตอบสนองต่อสิ่งนั้นและในขณะที่ความคิดเห็นล่าสุดของฉันคือ สูงมากคะแนนยังใช้ได้ "ฉันคัดลอก 'tude เพราะฉันรู้สึกว่าคุณตอบสนองของคุณ "การเปลี่ยนไปใช้แถวใน ListView อาจดีกว่า" Listview ใช้ไม่ได้กับรูปลักษณ์ของแอพของฉัน แอพของฉันในตลาดคือ AppSwipe! ถ้าคุณอยากรู้ว่าฉันกำลังทำอะไร ...
จัสติน


0

ในกรณีของฉันเลย์เอาต์ที่ฉันพยายามจะรวมเริ่มต้นด้วย <mergeแท็ก เมื่อฉันเปลี่ยนเป็นเค้าโครงบอกว่า<RelativeLayoutมันใช้งานได้ ด้านล่างคือภาพประกอบ

การทำงาน

<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">

ไม่ทำงาน

<merge xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">

สิ่งนี้จะสร้างเลเยอร์ที่ซ้อนกันอีกชั้นซึ่งไม่ใช่วิธีแก้ปัญหาที่ดีที่สุด
Silvia H

0

ฉันมีปัญหาเดียวกันและแม้แต่การกำหนดlayout_widthและlayout_heightมันไม่ได้ทำงาน ปัญหาคือเลย์เอาต์ที่ฉันใส่ไว้มีแท็กและหลังจากลบออกทุกอย่างก็ดูมีเสน่ห์ ฉันคิดว่าการผสานไม่ใช่แท็กเค้าโครงและด้วยเหตุนี้จึงไม่สามารถรับพารามิเตอร์การกำหนดตำแหน่งและขนาดได้ เนื่องจากทุกสิ่งที่คุณกำหนดไว้ถูกโอนไปยังเลย์เอาต์หลักภายในการตั้งค่าจึงถูกทิ้งไป

TL: DR: เพียงแค่ลบแท็กย้ายคำจำกัดความ xmlns ไปยังผู้ดูเค้าโครงจริงและคุณก็น่าจะดี

ก่อน:

<merge
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <...ui.component.BorderCardView
        android:layout_width="112dp"
        android:layout_height="32dp"
        app:cardCornerRadius="4dp"
        app:cardUseCompatPadding="true">

        <ImageView
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:src="@drawable/ic_logout"
            android:tint="@color/divider" />

    </...ui.component.BorderCardView>
</merge>

ที่ทำงาน:

<...ui.component.BorderCardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="112dp"
    android:layout_height="32dp"
    app:cardCornerRadius="4dp"
    app:cardUseCompatPadding="true">

    <ImageView
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:src="@drawable/ic_logout"
        android:tint="@color/divider" />

</...ui.component.BorderCardView>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.