อธิบายพื้นฐานของ LinearLayout — ViewGroup พื้นฐานใน Android SDK สำหรับจัดเรียงองค์ประกอบย่อยในบรรทัดเดียว: แนวตั้ง (LinearLayout.VERTICAL) หรือแนวนอน (LinearLayout.HORIZONTAL) LinearLayout ยังคงเป็นวิธีที่ง่ายและเร็วที่สุดในการสร้างแถวปุ่ม รายการตั้งค่า หรือฟอร์มป้อนข้อมูล เมื่อความซับซ้อนของเลย์เอาต์ไม่เกิน 5–10 องค์ประกอบ แอตทริบิวต์ layout_weight และ orientation จัดการการกระจายพื้นที่และทิศทาง ข้อมูลจำเพาะโดยละเอียด — ใน LinearLayout API Reference
ประเด็นสำคัญ
LinearLayout — หนึ่งใน ViewGroup แรกใน Android SDK (API Level 1) ออกแบบมาเพื่อจัดวางองค์ประกอบย่อยตามลำดับในแถวหรือคอลัมน์เดียว องค์ประกอบใหม่แต่ละรายการจะถูกวางหลังจากรายการก่อนหน้า และทิศทางถูกกำหนดโดยแอตทริบิวต์ android:orientation LinearLayout เป็นคอนเทนเนอร์ Android ที่ง่ายและคาดเดาได้มากที่สุด ทำให้เป็นตัวเลือกที่เหมาะสำหรับอินเทอร์เฟซแบบเส้นตรง: แถวปุ่ม รายการตั้งค่าที่มีไอคอนและข้อความ เมนูแนวนอน
ในอดีต LinearLayout เป็น ViewGroup แรกที่นักพัฒนา Android มือใหม่รู้จัก ข้อดีคือความโปร่งใส: องค์ประกอบย่อยถูกวางตามลำดับการประกาศอย่างเคร่งครัด โดยไม่มีกฎการวางตำแหน่งที่ซ่อนอยู่ แตกต่างจาก RelativeLayout ที่องค์ประกอบสามารถผูกกับเพื่อนบ้านใดก็ได้ หรือ ConstraintLayout ที่มีระบบข้อจำกัด LinearLayout ทำงานบนหลักการ "แรกซ้าย/บน ที่สองถัดไป ที่สามต่อจากที่สอง" ตามข้อมูลจาก Google I/O 2017, 78% ของหน้าจอใน 1,000 แอปพลิเคชันอันดับต้นๆ ของ Google Play ใช้ LinearLayout เป็นคอนเทนเนอร์หลักหรือเสริม
เมตริกหลักของ LinearLayout คือ LinearLayout.MeasureSpec — ระบบจัดการขนาด ด้วยทิศทาง VERTICAL ความกว้างของแต่ละองค์ประกอบเท่ากับความกว้างของพาเรนต์ (match_parent หรือคงที่) และความสูงถูกกำหนดโดยเนื้อหาหรือแอตทริบิวต์ สำหรับ HORIZONTAL ตรงกันข้าม: ความสูงเท่ากับพาเรนต์ ความกว้างตามเนื้อหา หากในแถว HORIZONTAL ความกว้างรวมขององค์ประกอบเกินความกว้างของพาเรนต์ องค์ประกอบอาจล้นออกนอกหน้าจอ (หากไม่ได้ใช้ weight)
LinearLayout แทนที่ onMeasure และ onLayout จาก ViewGroup ด้วยทิศทาง VERTICAL onLayout จะวางองค์ประกอบตามแกน Y: child แรก — y=0, ที่สอง — y=prev.bottom + divider.hidden, และต่อไปเรื่อยๆ ด้วย HORIZONTAL — ตามแกน X ตัวแบ่งถูกกำหนดผ่าน android:divider และ android:showDividers (beginning, middle, end) ตัวแบ่งสามารถเป็นสี drawable หรือทรัพยากร Shape ที่กำหนดเอง
ทิศทาง — แอตทริบิวต์สำคัญของ LinearLayout ที่กำหนดทิศทางการวางองค์ประกอบย่อย android:orientation="vertical" จัดเรียงองค์ประกอบจากบนลงล่าง; android:orientation="horizontal" — จากซ้ายไปขวา (หรือขวาไปซ้ายกับการแปล RTL) หากไม่ได้ตั้งค่าทิศทาง จะใช้ HORIZONTAL โดยค่าเริ่มต้น (ในเวอร์ชันเก่า) — แต่การระบุอย่างชัดเจนจำเป็นสำหรับพฤติกรรมที่คาดเดาได้
android:gravity — จัดเรียงเนื้อหาภายใน LinearLayout ยอมรับการรวมกัน: top, bottom, left, right, center, center_horizontal, center_vertical, fill, clip_vertical, clip_horizontal ค่า gravity="center" สำหรับเลย์เอาต์ VERTICAL จะจัดกึ่งกลางองค์ประกอบทั้งหมดในแนวนอน android:layout_gravity — แอตทริบิวต์ขององค์ประกอบย่อยที่จัดเรียงภายใน LinearLayout พาเรนต์ ตัวอย่าง: ปุ่มที่มี layout_gravity="right" ในเลย์เอาต์ VERTICAL จะดันปุ่มไปที่ขอบขวา
ความแตกต่าง: gravity — คุณสมบัติของพาเรนต์ (วิธีจัดเรียง child ภายใน), layout_gravity — คุณสมบัติของ child (วิธีปฏิบัติตัวภายในพาเรนต์) การสับสนแอตทริบิวต์เหล่านี้เป็นหนึ่งในข้อผิดพลาดที่พบบ่อยที่สุดในผู้เริ่มต้น
สำหรับเลย์เอาต์ HORIZONTAL ที่มีองค์ประกอบข้อความ (TextView, Button, EditText) android:baselineAligned="true" มีประโยชน์ — จัดเรียงข้อความทั้งหมดตามเส้นฐานของฟอนต์ โดยไม่ขึ้นกับ padding ขนาด หรือความสูง ทำให้แถวที่มีไอคอน ชื่อเรื่อง และคำอธิบายดูเป็นระเบียบ: ข้อความไม่กระโดดขึ้นลง
layout_weight — แอตทริบิวต์ขององค์ประกอบย่อยของ LinearLayout ที่กำหนดว่าส่วนใดของพื้นที่ว่างที่จะครอบครอง ทำงานเฉพาะในทิศทางของทิศทาง (ความกว้างสำหรับ HORIZONTAL, ความสูงสำหรับ VERTICAL) สูตรการคำนวณ: ขนาด = ขนาดของตัวเอง + (พื้นที่ว่าง × (น้ำหนักขององค์ประกอบ / ผลรวมของน้ำหนักทั้งหมด)) พื้นที่ว่าง = ขนาดพาเรนต์ — ผลรวมของขนาดตัวเองของ child ทั้งหมด
layout_weight มีประสิทธิภาพมากกว่าขนาดคงที่สำหรับอินเทอร์เฟซแบบปรับตัว ตัวอย่างเช่น หากแผงด้านซ้ายควรครอบครอง 30% ของหน้าจอและด้านขวา 70% ให้ตั้งค่า weight=3 และ weight=7 สำหรับการทำงานที่ถูกต้อง ขนาดในทิศทางมักจะตั้งเป็น 0dp (android:layout_width="0dp" สำหรับ HORIZONTAL) จากนั้นขนาดขององค์ประกอบจะถือเป็นศูนย์ และขนาดทั้งหมดถูกกำหนดโดย weight เท่านั้น นี่คือคำแนะนำมาตรฐานของ Google (Android Developers Guide, 2024)
สำคัญ: น้ำหนักถูกรวมสำหรับองค์ประกอบทั้งหมดใน LinearLayout รวมถึงองค์ประกอบที่ไม่ได้ตั้งค่า weight (น้ำหนักถือเป็น 0) หากองค์ประกอบหนึ่งมี weight=1 และอีกองค์ประกอบมี weight=2 องค์ประกอบแรกจะครอบครอง 1/3 ของพื้นที่ว่าง องค์ประกอบที่สอง — 2/3 weightSum (แอตทริบิวต์ของพาเรนต์) ช่วยให้ตั้งค่าผลรวมน้ำหนักสูงสุด — หากผลรวมน้ำหนักของ child น้อยกว่า weightSum ส่วนต่างจะยังคงเป็นพื้นที่ว่าง
ฟอร์มเข้าสู่ระบบประกอบด้วยสามองค์ประกอบ: ช่องอีเมล (ครอบครอง 2/4), ช่องรหัสผ่าน (1/4) และปุ่ม (1/4) ของความสูงหน้าจอ ทั้งสามองค์ประกอบที่มี weight ทำงานเฉพาะในทิศทาง VERTICAL ตามความสูง
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:hint="Email" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:hint="รหัสผ่าน"
android:inputType="textPassword" />
<Button
android:id="@+id/login_button"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="เข้าสู่ระบบ" />
</LinearLayout>
ความสูงของแต่ละองค์ประกอบ = (parent_height — padding) × (weight / sumWeights) email ได้รับ 2/4 ของความสูง รหัสผ่านและปุ่ม — อย่างละ 1/4 ทั้งหมด height=0dp ดังนั้นขนาดตัวเองไม่ถูกนำมาพิจารณา — เฉพาะ weight เท่านั้นที่ทำงาน
แผงที่มีสามปุ่ม: ไอคอนย้อนกลับ (wrap_content), ชื่อเรื่อง (เติมส่วนที่เหลือ) และไอคอนเมนู (wrap_content) weight สำหรับชื่อเรื่อง = 1, ปุ่มที่ไม่มี weight
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical">
<ImageButton
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow_back" />
<TextView
android:id="@+id/toolbar_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="ชื่อเรื่อง"
android:textSize="18sp" />
<ImageButton
android:id="@+id/btn_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_more_vert" />
</LinearLayout>
ปุ่มย้อนกลับและเมนูใช้พื้นที่เท่าที่จำเป็นสำหรับไอคอน ชื่อเรื่อง (TextView) ที่มี width=0dp และ weight=1 ขยายไปยังพื้นที่ที่เหลือระหว่างพวกเขา gravity="center_vertical" จัดเรียงองค์ประกอบทั้งหมดกึ่งกลางแนวตั้ง
การ์ดสินค้า: แถวแนวนอน (รูปภาพ + บล็อกข้อความแนวตั้ง) LinearLayout ด้านนอก HORIZONTAL ด้านใน — VERTICAL สำหรับชื่อเรื่อง คำอธิบาย และราคา
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/product_image"
android:scaleType="centerCrop" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ชื่อสินค้า"
android:textStyle="bold"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="คำอธิบายสินค้าโดยย่อในสองบรรทัด"
android:textSize="14sp"
android:textColor="@color/gray" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1,299 RUB"
android:textSize="18sp"
android:textColor="@color/accent" />
</LinearLayout>
</LinearLayout>
รูปภาพทางซ้าย (80×80dp) ทางขวา — บล็อกแนวตั้งของสามบรรทัดข้อความ LinearLayout ด้านในมี weight=1 ดังนั้นจึงครอบครองพื้นที่ทั้งหมดหลังจากรูปภาพ นี่คือรูปแบบการ์ดรายการคลาสสิกที่พบในแอปพลิเคชันนับพัน
ข้อเสียหลักของ LinearLayout — ความจำเป็นในการใช้คอนเทนเนอร์ซ้อนสำหรับเลย์เอาต์ที่ซับซ้อน หากคุณต้องการจัดเรียงองค์ประกอบในกริด 2×2 หรือสร้างแถวที่มีการจัดแนวทั้งสองขอบ จำเป็นต้องมีระดับการซ้อนอย่างน้อย 2–3 ระดับ แต่ละระดับ LinearLayout เพิ่ม 2 รอบ onMeasure ซึ่งบนอุปกรณ์ที่อ่อนแอทำให้เกิดความล่าช้าที่เห็นได้ชัดระหว่างการเรนเดอร์
ตาม Android Performance Blog (Google, 2017) แต่ละระดับการซ้อนเพิ่มเติมจะเพิ่มเวลาเลย์เอาต์ 30–50 μs สำหรับรายการ 20 รายการที่มี 3 ระดับการซ้อน ความล่าช้าสามารถถึง 3 ms ต่อองค์ประกอบ — ซึ่งเป็นการกระตุก (jank) ที่เห็นได้ชัดระหว่างการเลื่อน ConstraintLayout แก้ปัญหานี้ผ่านลำดับชั้นแบบราบ แทนที่ 3–4 LinearLayout ที่ซ้อนกันด้วยคอนเทนเนอร์ข้อจำกัดเดียว
คำแนะนำ: ใช้ LinearLayout สำหรับเลย์เอาต์เชิงเส้นอย่างง่าย (สูงสุด 10 องค์ประกอบ หนึ่งระดับการซ้อน) สำหรับการ์ดรายการ ฟอร์มที่มีการจัดแนวต่างกัน และหน้าจอที่มีโครงสร้างตาราง ให้เลือก ConstraintLayout สำหรับองค์ประกอบที่ซ้ำใน RecyclerView ให้ใช้ ConstraintLayout ในไฟล์เลย์เอาต์ของไอเท็ม — สิ่งนี้สำคัญมากสำหรับประสิทธิภาพการเลื่อน
คำถามที่พบบ่อย
สาเหตุที่พบบ่อยที่สุด — ขนาดในทิศทางไม่ได้ตั้งเป็น 0dp สำหรับเลย์เอาต์ HORIZONTAL ต้องใช้ android:layout_width="0dp" สำหรับ VERTICAL — android:layout_height="0dp" หากองค์ประกอบมี wrap_content หรือ match_parent น้ำหนักอาจไม่ทำงานหรือให้ผลลัพธ์ที่ไม่คาดคิด สาเหตุที่สอง — weightSum บนพาเรนต์น้อยกว่าผลรวมน้ำหนักของ child สาเหตุที่สาม — การใช้ match_parent บนองค์ประกอบ child ในทิศทางตรงกันข้าม
layout_weight ถูกตั้งบนแต่ละองค์ประกอบ child และกำหนดส่วนแบ่งของพื้นที่ว่าง weightSum — แอตทริบิวต์ของ LinearLayout พาเรนต์ที่ตั้งค่าผลรวมน้ำหนักสูงสุด โดยค่าเริ่มต้น weightSum = ผลรวมของ layout_weight ทั้งหมดของ child หากคุณตั้ง weightSum = 3 และ child มี weight=1 แต่ละตัวจะครอบครอง 1/3 หากมี child เพียงสองตัวที่มี weight=1 ส่วนที่เหลือ 1/3 จะว่างเปล่า weightSum สะดวกสำหรับการกำหนดสัดส่วนคงที่โดยไม่ต้องคำนวณใหม่เมื่อเพิ่ม/ลบองค์ประกอบ
โดยค่าเริ่มต้นไม่มีช่องว่าง — องค์ประกอบถูกวางติดกัน หากมีช่องว่างปรากฏขึ้น ให้ตรวจสอบ: android:divider และ android:showDividers บนพาเรนต์ android:layout_margin บนองค์ประกอบ child นอกจากนี้ android:weightSum อาจส่งผล — หากองค์ประกอบใช้ weight อาจมีพื้นที่ว่างระหว่างองค์ประกอบเนื่องจากการคำนวณที่ไม่ถูกต้อง หากต้องการให้ไม่มีช่องว่าง ให้ตั้งค่า child margin ทั้งหมดเป็น 0dp และลบตัวแบ่ง
สำหรับเลย์เอาต์ทิศทางเดียวอย่างง่าย (1–5 องค์ประกอบในแถว/คอลัมน์) LinearLayout เร็วกว่าเล็กน้อย — ไม่ใช้ทรัพยากรในการคำนวณข้อจำกัด สำหรับเลย์เอาต์ที่ซับซ้อน (มากกว่า 5 องค์ประกอบ กริด ชนิดการจัดแนวที่แตกต่างกัน) ConstraintLayout เร็วกว่า เพราะใช้ลำดับชั้นแบบราบแทน LinearLayout ที่ซ้อนกัน ตามการทดสอบของ Google (Android Performance Blog) ConstraintLayout ชนะในเลย์เอาต์ที่ต้องการการซ้อน LinearLayout 3+ ระดับ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม