FrameLayout: แนวคิดหลัก การวางตำแหน่งอย่างง่ายใน Android

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-02-24 เวลาอ่าน: 8 นาที

เรียนรู้แนวคิดหลักของ FrameLayout — ViewGroup ที่ง่ายที่สุดใน Android SDK ออกแบบมาเพื่อโฮสต์องค์ประกอบย่อยเดียว (โดยทั่วไป) หรือซ้อนหลายองค์ประกอบทับกัน FrameLayout สะดวกสำหรับคอนเทนเนอร์ตัวแทน เศษส่วน (fragments) ตัวบ่งชี้การโหลด และองค์ประกอบพื้นหน้า ไม่จัดการตำแหน่งขององค์ประกอบย่อย — องค์ประกอบถัดไปแต่ละรายการจะถูกวาดทับองค์ประกอบก่อนหน้า และตำแหน่งขององค์ประกอบเหล่านั้นถูกกำหนดโดย layout_gravity (left, top, right, bottom, center) สถานการณ์พื้นฐานอธิบายไว้ใน FrameLayout API Reference

ประเด็นสำคัญ

  • การซ้อนทับเลเยอร์ — FrameLayout วางองค์ประกอบตามลำดับทับกัน องค์ประกอบที่ประกาศล่าสุดใน XML จะถูกวาดสูงที่สุด
  • พื้นหน้า (Foreground) — แอตทริบิวต์ android:foreground อนุญาตให้วาง drawable ทับองค์ประกอบย่อยทั้งหมด มีประโยชน์สำหรับเอฟเฟกต์และการซ่อน
  • measureAllChildren — โดยค่าเริ่มต้น FrameLayout จะวัดองค์ประกอบย่อยทั้งหมด แม้แต่องค์ประกอบที่มองไม่เห็น (GONE) การปิดใช้งานจะช่วยเพิ่มความเร็วในการวาด 20–60%
  • layout_gravity — วิธีเดียวในการวางตำแหน่งองค์ประกอบย่อยภายใน FrameLayout: top, bottom, left, right, center
  • การใช้กับ Fragment — FrameLayout เป็นคอนเทนเนอร์มาตรฐานสำหรับ FragmentTransaction.replace() ซึ่งแทนที่เนื้อหาโดยไม่ต้องสร้าง Activity ใหม่

FrameLayout คืออะไร?

FrameLayout คือ ViewGroup ที่ง่ายที่สุดใน Android SDK ออกแบบมาเพื่อบล็อกพื้นที่หน้าจอและแสดงวิวย่อยเดียว (หรือซ้อนหลายวิว) แตกต่างจาก LinearLayout (การจัดเรียงตามลำดับ) และ RelativeLayout (การวางตำแหน่งแบบสัมพัทธ์) FrameLayout ไม่เปลี่ยนแปลงตำแหน่งขององค์ประกอบย่อย — องค์ประกอบย่อยใหม่แต่ละรายการจะถูกวางไว้ที่มุมบนซ้าย (0,0) โดยค่าเริ่มต้นและวาดทับองค์ประกอบก่อนหน้า

FrameLayout ถูกนำมาใช้ใน API Level 1 และยังคงเป็นคอนเทนเนอร์ Android ที่เบาที่สุด: ไม่แทนที่ onMeasure ด้วยตรรกะที่ซับซ้อนและดำเนินการ layout น้อยที่สุด ตาม Android Performance Blog FrameLayout ดำเนินการ onLayout ในครั้งเดียวและแทบไม่เพิ่มค่าใช้จ่ายเมื่อเทียบกับการวาง View โดยตรง ทำให้เป็นตัวเลือกที่เหมาะสำหรับคอนเทนเนอร์ที่ความเร็วสำคัญ: เลย์เอาต์ไอเท็ม RecyclerView (ร่วมกับ ConstraintLayout สำหรับการวางตำแหน่ง) คอนเทนเนอร์ Fragment ชั้นซ้อนทับ

ขนาดของ FrameLayout โดยค่าเริ่มต้นถูกกำหนดโดยองค์ประกอบย่อยที่ใหญ่ที่สุด (หากไม่ได้ตั้งค่า match_parent) หากไม่ได้ระบุองค์ประกอบย่อย FrameLayout จะยุบเป็น (0,0) แอตทริบิวต์ android:measureAllChildren (ส่วนด้านล่าง) เปลี่ยนแปลงพฤติกรรมนี้

FrameLayout ในลำดับชั้น ViewGroup ของ Android

FrameLayout สืบทอดจาก ViewGroup และเป็นพาเรนต์โดยตรงของคอนเทนเนอร์เฉพาะทางมากมาย: FragmentContainerView (fragments), CardView (การ์ดที่มีเงา), ScrollView (องค์ประกอบย่อยเดียว), NestedScrollView เมื่อสร้าง Activity ด้วยการนำทางแบบ Fragment เทมเพลตมาตรฐานของ Android Studio จะใช้ FrameLayout (หรือ FragmentContainerView) เป็นคอนเทนเนอร์รูทสำหรับ fragments

พื้นหน้าและ measureAllChildren

android:foreground — แอตทริบิวต์ FrameLayout ที่ระบุ drawable ซึ่งวาดทับองค์ประกอบย่อยทั้งหมด แตกต่างจาก background (ใต้ element ย่อย) foreground จะแสดงเหนือเนื้อหาและสามารถโปร่งใสได้ ใช้สำหรับ: เอฟเฟกต์ซ้อนทับเมื่อกด (ripple ผ่าน ?attr/selectableItemBackground), การซ่อนภาพ, การแสดงตัวบ่งชี้สถานะ (เครื่องหมายถูกบนภาพ)

Foreground รองรับทรัพยากร drawable มาตรฐาน: ColorDrawable, ShapeDrawable, RippleDrawable, LayerDrawable ตั้งแต่ API 23+ android:foregroundGravity พร้อมใช้งานสำหรับการวางตำแหน่งพื้นหน้า (fill, center, top, bottom) ในโหมดเติม (fill) foreground จะขยายทั่วทั้ง FrameLayout; ในโหมด center จะวาดที่กึ่งกลาง

android:measureAllChildren — แอตทริบิวต์บูลีน (ค่าเริ่มต้น true) ที่กำหนดว่าจะวัดองค์ประกอบย่อยทั้งหมดเมื่อคำนวณขนาด FrameLayout หรือไม่ หากเป็น true (ค่าเริ่มต้น) FrameLayout จะพิจารณาขนาดขององค์ประกอบย่อยทั้งหมดรวมถึง GONE (ด้วยขนาด 0) หากเป็น false FrameLayout จะวัดเฉพาะองค์ประกอบย่อย VISIBLE และ INVISIBLE — องค์ประกอบ GONE จะถูกแยกออกจากการคำนวณ ตาม Google I/O 2019 การปิด measureAllChildren สำหรับคอนเทนเนอร์ที่มีองค์ประกอบ GONE จำนวนมาก (เช่น รายการที่สลับการมองเห็น) จะเพิ่มความเร็วในการวาดเริ่มต้น 20–60%

layout_gravity: การวางตำแหน่งภายใน FrameLayout

android:layout_gravity — แอตทริบิวต์ขององค์ประกอบย่อย FrameLayout (และ ViewGroup อื่นๆ) ที่กำหนดตำแหน่งภายในคอนเทนเนอร์ ใน FrameLayout layout_gravity เป็นวิธีเดียวในการควบคุมตำแหน่งขององค์ประกอบย่อย เนื่องจาก FrameLayout ไม่มีกฎการวางตำแหน่งของตัวเอง (เช่น RelativeLayout) หรือทิศทาง (เช่น LinearLayout)

ค่าที่เป็นไปได้: top, bottom, left, right, center, center_horizontal, center_vertical, fill, fill_horizontal, fill_vertical, clip_horizontal, clip_vertical รวมกันผ่าน |: android:layout_gravity="bottom|center_horizontal" — องค์ประกอบจะชิดขอบล่างและกึ่งกลางในแนวนอน สำหรับองค์ประกอบที่เล็กกว่า FrameLayout layout_gravity จะกำหนดตำแหน่งในพื้นที่ว่าง

หากไม่ได้ตั้งค่า layout_gravity องค์ประกอบจะถูกวางไว้ที่ มุมบนซ้าย (top|left) โดยค่าเริ่มต้น สำหรับ FrameLayout ที่มีองค์ประกอบย่อยหลายรายการ แต่ละรายการสามารถมี layout_gravity ของตัวเองได้ — องค์ประกอบหนึ่งอาจอยู่ที่มุมบนซ้าย อีกองค์ประกอบหนึ่งที่มุมล่างขวา องค์ประกอบที่สามอยู่กึ่งกลาง สิ่งนี้ช่วยให้สร้างการซ้อนทับอย่างง่าย (เช่น ไอคอนปิดบนภาพ)

ความแตกต่าง: gravity vs layout_gravity

android:gravity (แอตทริบิวต์พาเรนต์) จัดแนวเนื้อหาภายใน FrameLayout — ตัวอย่างเช่น ข้อความภายใน TextView android:layout_gravity (แอตทริบิวต์ย่อย) จัดแนวองค์ประกอบเองภายใน FrameLayout ในบริบทของ FrameLayout gravity กำหนดว่าองค์ประกอบย่อยถูกวางตำแหน่งอย่างไรโดยค่าเริ่มต้น (คล้ายกับ layout_gravity สำหรับองค์ประกอบย่อยทั้งหมดพร้อมกัน) แต่ layout_gravity ของแต่ละองค์ประกอบเฉพาะจะแทนที่ค่าพาเรนต์

ตัวอย่าง: XML และกรณีการใช้งาน

ตัวอย่างที่ 1: การซ้อนทับพื้นฐาน — ป้ายบนภาพ

ภาพที่มีป้ายข้อความที่มุมล่างขวา FrameLayout ประกอบด้วย ImageView ที่เต็มหน้าจอและ TextView ที่มี layout_gravity="bottom|end" สำหรับวางตำแหน่งทับภาพ

xml
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="200dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/product_photo"
        android:scaleType="centerCrop" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="8dp"
        android:background="@drawable/badge_background"
        android:elevation="2dp"
        android:paddingHorizontal="8dp"
        android:paddingVertical="4dp"
        android:text="-30%"
        android:textColor="@android:color/white"
        android:textSize="14sp"
        android:textStyle="bold" />

</FrameLayout>

ImageView เติม FrameLayout ทั้งหมด (สูง 200dp) TextView ที่มี layout_gravity="bottom|end" ถูกวางไว้ที่มุมล่างขวาทับภาพ elevation=2dp เพิ่มเงาใต้ป้าย แยกออกจากภาพด้วยสายตา ตัวอย่างเล็กน้อยนี้ใน LinearLayout จะต้องใช้คอนเทนเนอร์ที่ซ้อนกันหรือโค้ดที่กำหนดเอง

ตัวอย่างที่ 2: ตัวบ่งชี้การโหลด (แถบความคืบหน้าทับเนื้อหา)

หน้าจอที่มีเนื้อหาและแถบความคืบหน้าที่กึ่งกลางซึ่งปรากฏระหว่างการโหลด FrameLayout ประกอบด้วยสององค์ประกอบ: เนื้อหาและ ProgressBar ที่มี visibility="gone" (สลับในโค้ด)

xml
<FrameLayout
    android:id="@+id/content_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/content_text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="เนื้อหาที่โหลด"
        android:textSize="18sp" />

    <ProgressBar
        android:id="@+id/loading_spinner"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_gravity="center"
        android:visibility="gone" />

</FrameLayout>

ProgressBar ถูกซ่อนไว้โดยค่าเริ่มต้น (gone) เมื่อเริ่มโหลด จะเรียก findViewById(R.id.loading_spinner).visibility = View.VISIBLE — สปินเนอร์ปรากฏที่กึ่งกลางทับเนื้อหา หลังจากโหลด — .visibility = View.GONE FrameLayout ให้การซ้อนทับโดยไม่เปลี่ยนตำแหน่งเนื้อหา — ข้อความไม่ขยับเมื่อสปินเนอร์ปรากฏ เนื่องจาก ProgressBar วาดทับ

ตัวอย่างที่ 3: คอนเทนเนอร์ Fragment

FrameLayout เป็นคอนเทนเนอร์มาตรฐานสำหรับ FragmentTransaction Activity จะแทนที่ fragments ภายในคอนเทนเนอร์นี้ตามการนำทาง

xml
<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
kotlin
import androidx.fragment.app.FragmentTransaction

val fragmentContainer = R.id.fragment_container

fun navigateTo(fragment: Fragment) {
    supportFragmentManager
        .beginTransaction()
        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
        .replace(fragmentContainer, fragment)
        .addToBackStack(null)
        .commit()
}

FrameLayout เป็นคอนเทนเนอร์ fragment เป็นวิธีที่เบาที่สุดในการรองรับการนำทาง Fragment FragmentContainerView (ซับคลาสของ FrameLayout) แนะนำให้ใช้กับ Navigation Component 2.4+ แต่ FrameLayout ธรรมดายังคงใช้ได้สำหรับ FragmentTransaction ด้วยตนเอง ข้อได้เปรียบหลักคือ FragmentTransaction.replace() จะแทนที่เนื้อหาทั้งหมดโดยไม่ส่งผลกระทบต่อ Activity

เมื่อใดควรใช้ FrameLayout

FrameLayout เหมาะสมที่สุด สำหรับสี่สถานการณ์: คอนเทนเนอร์ fragment (FragmentContainerView หรือ FrameLayout), การซ้อนทับการโหลด (ProgressBar ทับเนื้อหา), ป้ายการ์ด (ป้ายบนภาพ), คอนเทนเนอร์ตัวแทนสำหรับ ViewStub (การโหลดแบบขี้เกียจ)

ไม่เหมาะสำหรับ: การวางตำแหน่งที่ซับซ้อนของหลายองค์ประกอบ (ใช้ ConstraintLayout), รายการตามลำดับ (LinearLayout หรือ RecyclerView), เลย์เอาต์แบบไดนามิกที่เปลี่ยนลำดับองค์ประกอบ

FrameLayout เป็นองค์ประกอบรูทของหน้าจอ — ปฏิบัติที่ไม่ดีหากหน้าจอมีมากกว่า 2–3 องค์ประกอบ สำหรับคอนเทนเนอร์รูท ให้ใช้ ConstraintLayout — ให้การควบคุมการวางตำแหน่งมากขึ้นโดยไม่สูญเสียประสิทธิภาพ ใช้ FrameLayout เฉพาะสำหรับบล็อกย่อยที่ต้องการการซ้อนทับหรือตัวแทน

คำถามที่พบบ่อย

ความแตกต่างระหว่าง FrameLayout และ ConstraintLayout คืออะไร?

FrameLayout — คอนเทนเนอร์แบบมินิมอลไม่มีระบบวางตำแหน่ง (เฉพาะ layout_gravity) ออกแบบมาสำหรับการซ้อนทับองค์ประกอบ ConstraintLayout — ระบบเลย์เอาต์ครบวงจรพร้อมข้อจำกัด (constraints) เชื่อมโยง (chains) สิ่งกีดขวาง (barriers) ขนาดเปอร์เซ็นต์และการรองรับ MotionLayout FrameLayout เร็วกว่าสำหรับกรณีง่ายๆ (1 องค์ประกอบย่อยหรือการซ้อนทับ) ConstraintLayout จำเป็นสำหรับเลย์เอาต์ที่ซับซ้อน ทางเลือก: FrameLayout สำหรับการซ้อนทับการโหลดและคอนเทนเนอร์ fragment; ConstraintLayout สำหรับทุกอย่างอื่น

จะทำให้ FrameLayout ใช้พื้นที่เฉพาะองค์ประกอบย่อยที่ใหญ่ที่สุดได้อย่างไร?

ตั้งค่าขนาด FrameLayout เป็น wrap_content บนแกนที่ต้องการ (android:layout_width="wrap_content" และ/หรือ android:layout_height="wrap_content") FrameLayout จะปรับตามขนาดขององค์ประกอบย่อยที่ใหญ่ที่สุด หากองค์ประกอบย่อยทั้งหมดเล็กกว่า FrameLayout พื้นที่ส่วนเกินจะว่างเปล่า สำหรับการควบคุมที่แม่นยำ ให้ใช้ match_parent กับ padding คงที่

ทำไมองค์ประกอบใน FrameLayout จึงทับซ้อนกัน?

นี่คือพฤติกรรมที่ตั้งใจของ FrameLayout — มันไม่ได้กระจายองค์ประกอบย่อยในพื้นที่ แต่วาดตามลำดับทับกัน หากคุณต้องการให้องค์ประกอบไม่ทับซ้อนกัน ให้ใช้คอนเทนเนอร์อื่น (LinearLayout, ConstraintLayout) สำหรับการซ้อนทับบางส่วนด้วยการควบคุมลำดับการวาด ให้ใช้ layout_gravity สำหรับการขยับและ elevation สำหรับลำดับ Z

อะไรเร็วกว่า: FrameLayout หรือ ConstraintLayout?

FrameLayout เร็วกว่า สำหรับกรณีที่ง่ายที่สุด — องค์ประกอบย่อยเดียว ไม่มีการวางตำแหน่งที่ซับซ้อน FrameLayout ดำเนินการ onMeasure ในเวลาน้อยที่สุดโดยไม่คำนวณข้อจำกัด อย่างไรก็ตาม ความแตกต่างจะสังเกตเห็นได้เฉพาะในการทำซ้ำหลายร้อยครั้ง (RecyclerView ที่มีรายการนับหมื่น) สำหรับหน้าจอทั่วไป (1–3 FrameLayout) ความแตกต่างเป็นไมโครวินาทีและไม่ส่งผลต่อประสบการณ์ผู้ใช้ ConstraintLayout เป็นตัวเลือกสากลสำหรับ 95% ของงาน

สรุป

  • FrameLayout — ViewGroup Android ที่ง่ายที่สุดสำหรับการซ้อนทับองค์ประกอบและคอนเทนเนอร์ตัวแทนที่มีค่าใช้จ่ายน้อยที่สุด
  • การซ้อนทับเลเยอร์ — องค์ประกอบย่อยใหม่แต่ละรายการวาดทับองค์ประกอบก่อนหน้า องค์ประกอบที่ประกาศล่าสุดใน XML อยู่สูงที่สุด
  • พื้นหน้า (Foreground) — drawable ทับองค์ประกอบย่อยทั้งหมดสำหรับเอฟเฟกต์ซ้อนทับ การซ่อน และแอนิเมชัน ripple
  • measureAllChildren — ปิด (false) จะแยกองค์ประกอบ GONE จากการคำนวณขนาด เพิ่มความเร็วในการวาด 20–60%
  • layout_gravity — กลไกการวางตำแหน่งเดียว: top, bottom, left, right, center และการรวมกัน
  • คอนเทนเนอร์ Fragment — กรณีการใช้งานที่พบบ่อยที่สุด: FrameLayout (หรือ FragmentContainerView) สำหรับ FragmentTransaction.replace()
  • ประสิทธิภาพ — คอนเทนเนอร์ที่เร็วที่สุดสำหรับ 1 องค์ประกอบย่อย; สำหรับเลย์เอาต์ที่ซับซ้อน จะด้อยกว่า ConstraintLayout

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม