#AndroidStudio ตอนที่ 10 สำรวจ Layout และ properties ที่น่าสนใจ 6 Layout
<introduction>
อารัมภบท
เกิดข้อสงสัย จึงอยากชวนไปสำรวจ ว่ามี Layout อยู่เท่าไรที่เราทำเป็น Root layout เพราะ Theme: Basic Activity เตรียม CoordinatorLayout มาให้เราใช้ ส่วน Empty Activity เตรียม ConstraintLayout มาให้ เมื่อเข้า Layout Editor หรือ UI Builder เพื่อสร้าง UI แล้วเข้าส่วน Design มองใน Palatte หัวข้อ Layouts จะพบ 9 ตัวเลือก คือ ConstraintLayout, GridLayout, FrameLayout LinearLayout(horizontal), LinearLayout(vertical), RelativeLayout, TableLayout, TableRow, และ อันที่จริงยังมี Layout มากกว่านี้ อาทิ CoordinatorLayout หรือ AppbarLayout เป็นต้น
ขณะสำรวจ และทดสอบในบทบาท ผู้ใช้โปรแกรม Android Studio ทำการสร้าง Layout ก็ย่อมมีปัญหา ไม่ได้อย่างใจหมายหลายครา ทำให้มีโอกาสสืบค้น และได้พบบทความเกี่ยวกับ Layout ที่ akexorcist.com และ devahoy.com พบว่านักเขียนเล่าอะไรหลายอย่างเกี่ยวกับ Android ทำให้เข้าใจขึ้นมาก ลองไปหาอ่านดูนะครับ
สรุปกระบวนการ ได้ 5 ขั้นตอน ประกอบด้วย 1) สร้าง Project แบบ Basic Activity ไว้เรียก Layout อื่น 2) การปรับ orientation ของ LinearLayout เป็น vertical กับ horizontal 3) การแสดงข้อมูล 3 column โดยใช้ TextView บน LinearLayout 4) การสร้าง relativelayout.xml 5) การทำภาพ overlay แบบ RelativeLayout 6) copy overlay.xml แล้ว paste constraintlayout.xml 7) การสร้าง gridlayout.xml 8) การสร้าง tablelayout.xml
ปล. แฟ้ม Project ผมบีบไว้ และแชร์ให้ Download ทั้ง .rar และ .apk ไปทดสอบกัน
ที่ http://www.thaiall.com/android (ผ่าน 4shared.com)
</introduction>
<definition>
นิยามศัพท์
- LinearLayout คือ เค้าโครงเพื่อจัดวางวัตถุแบบเรียงลำดับ เลือกเรียงแบบ ตามแนวตั้ง (Vertical) หรือตามแนวนอน (Horizontal) เป็น Default Layout เมื่อสร้าง Layout XML File ขึ้นใหม่
- RelativeLayout คือ เค้าโครงเพื่อจัดวางวัตถุที่สัมพันธ์กัน เชื่อมโยง อ้างอิง อาจอ้างอิงวัตถุอื่น หรืออ้างอิง Parent ก็ได้ การใช้ Layout Editor จะช่วยให้จัดวาง เห็นภาพใน Blueprint ทำได้สะดวกขึ้นแทนการเข้าไปกำหนดใน XML file โดยตรง
- ConstraintLayout คือ เค้าโครงแบบเพื่อจัดวางวัตถุแบบใหม่ คล้าย RelativeLayout มาก และ Android สนับสนุนให้ใช้ เป็นเค้าโครงที่เหมาะสำหรับนักพัฒนาที่ไม่ถนัด XML file มี Layout Editor ที่รองรับการจัดวางผ่าน Blueprint และลดจำนวนเค้าโครงที่ซับซ้อนได้
- GridLayout คือ เค้าโครงเพื่อจัดวางวัตถุแบบต่อเนื่อง ในช่องที่จัดเตรียมเป็นตะแกรง หรือจะหย่อนวัตถุไปตามตาที่กำหนดได้ ตัวอย่างการใช้ คือ Image Gallery หรือการแสดงรายการ App บนสมาร์ทโฟน ก็แสดงเป็น Grid
- TableLayout คือ เค้าโครงเพื่อจัดวางวัตถุ โดยกำหนดเป็น Row กับ Column เหมาะกับการวางข้อมูลจากระบบ Database ที่แบ่งข้อมูลเป็น Record กับ Field ไว้แล้ว แต่จะจัดวางเป็นฟอร์มสำหรับ Input ข้อมูลก็สามารถสั่งขยายช่องด้วย span ได้
- CoordinatorLayout คือ เค้าโครงเพื่อจัดวางวัตถุที่แตกต่างรูปแบบในหน้าเดียวกัน อาจมีทั้ง Slide, Panel, Button หรือ Stick และ Animate ก็ทำได้
- ViewGroup คือ เป็นกลุ่มของ View ที่ทำหน้าที่จัดวางเค้าโครง มี subclass ทั้งแบบ Direct (19 Layout) และ Indirect (63 Layout) หากต้องการหา Layout ที่ตรงกับความต้องการให้เข้ามาดูรายชื่อ subclass แล้วเข้าไปดู description และ method ได้
</definition>
กำลังเล่าเรื่อง การใช้โปรแกรม Android Studio สู่เพื่อนนักพัฒนา ผ่าน Blog
<process>
กระบวนการในการพัฒนา APP
เพื่อสำรวจการใช้งาน Layout แต่ละแบบ ว่าใช้งานแตกต่างกันอย่างไร
มีขั้นตอน ดังนี้
1. สร้าง Project แบบ Basic Activity ไว้เรียก Layout อื่น
ตั้งชื่อว่า layoutsample แล้วเตรียม menu, string, activity, layout
ในตอนนี้เราจะ include Layout แล้วเลือก include ผ่าน ViewFlipper
ตามที่เล่าใน ตอนที่ 9 ข้อ 3 ว่าเราสร้าง Menu ผ่าน ToolBar ได้
แล้วเพิ่ม include คู่กับ visible หรือ gone หลายรายการ
เพื่อคุมการ select ใน Menu ให้ได้ผลตามต้องการ
ตัวอย่างนี้ขอปรับการเลือกควบคุมสลับ visible กับ gone
เป็นการใช้ ViewFlipper คลุมใน activity_main.xml และ MainActivity.java
1.1 เริ่มจากสร้าง linearlayout.xml เปล่า ขึ้นมาก่อน
เพื่อเตรียมไว้เป็นตัวเลือกที่ 1 เพราะตัวเลือกแรก คือ content_main.xml
ที่มีมากับ Basic Activity แล้วแสดงคำว่า Hello world!
1.2 ปรับ code ใน activity_main.xml
เพื่อเพิ่ม ViewFlipper เข้าควบคุมการ include
มองหา <include layout="@layout/content_main" /> เปลี่ยนเป็น <ViewFlipper android:id="@+id/view_flipper" android:layout_width="fill_parent" android:layout_height="fill_parent"> <include layout="@layout/content_main" /> <include layout="@layout/linearlayout" /> </ViewFlipper>
1.3 ปรับ code ใน MainActivity.java
เพื่อเปลี่ยนจากการใช้ if ไปใช้ switch คุมการ setDisplayedChild ที่ต้องการ
มองหา if (id == R.id.action_settings) { return true; } เปลี่ยนเป็น import android.widget.ViewFlipper; ViewFlipper vf = (ViewFlipper) this.findViewById(R.id.view_flipper); switch (item.getItemId()) { case R.id.content: vf.setDisplayedChild(0); return true; case R.id.linearlayout: vf.setDisplayedChild(1); return true; }
1.4 ไปเพิ่ม <item> ใน menu_main.xml และ <string> ใน strings.xml
เพื่อให้มีตัวเลือกสำหรับการคลิ๊กใน menu
เพื่อเลือก case R.id.content และ case R.id.linearlayout
เพราะขณะนี้มี 2 layout ที่พร้อมทำงานใน menu
1.5 ระหว่างสร้าง string พบการปัดบรรทัดที่มีผลกับ layout
โดยตัวอักษรที่ปัดบรรทัด หรือ Line Break ด้วย \n
ปัญหานี้เล่าใน ตอนที่ 8.1 แต่มาพบ \n ในตอนนี้
<string name="datawith2br">a\nb\nc</string>
2. การปรับ orientation ของ LinearLayout เป็น vertical กับ horizontal
เมื่อสร้าง linearlayout.xml พบว่าค่า default ไม่ได้กำหนดว่า orientation เป็นอะไร
แต่ default เป็น horizontal แม้ไม่ได้กำหนดก็ตาม
2.1 ทดสอบเพิ่ม TextView ที่มี @string/data
ได้เพิ่มแบบไม่กำหนด id จำนวน 20 tag
ปัญหาแรก พบว่า ข้อมูลเบียนกันจนแน่นในแถวเดียว เป็นปัญหาเรื่องตำแหน่ง
หากจำนวนข้อมูลน้อยกว่า 20 tag ก็จะแสดงผลได้ปกติ แต่กรณีนี้มากไป
และไม่ได้จัดเรียงแบบ wrap ลงมาบรรทัดต่อไป
นี่เป็นค่า default ของ orientation ที่เป็นแบบ horizontal
อีกปัญหา พบว่า ทั้งใน emulator และ smart phone
บน emulator จะแสดงคำว่า data ทับ AppbarLayout
บน smartphone จะคำว่า data ถูกทับด้วย AppbarLayout
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/data" />
[ปัญหา]
สรุปว่า พบปัญหาการแสดงผลในทั้ง 2 อุปกรณ์
เนื่องจากต้องการให้ตำแหน่งเริ่มต้นแสดงผลต่อจาก Appbarlayout
ไม่ใช่ทับกันไปมา ระหว่าง TextView กับ Appbarlayout แบบนั้น
[วิธีแก้ไข]
เพิ่ม android:layout_marginTop=”?attr/actionBarSize” ใน layout
ทดสอบกับทั้ง linearlayout.xml และ content_main.xml
แม้จะเปลี่ยน wrap_content เป็น match_parent แล้ว ก็ไม่มีปัญหา
2.2 ทดสอบเพิ่ม android:orientation=”vertical”
* ใน layout แบบ ConstraintLayout หรือกำหนด width=”match_parent”
ผล คือ data ทับกันในตำแหน่งเดิม แสดงว่า ไม่รองรับการกำหนดเป็น vertical
* ใน layout แบบ LinearLayout
ผล คือ เรียงต่อกันมาตามแนวนอน บรรทัดละ 1 คำอย่างถูกต้อง
แม้กำหนดเป็น wrap_content ทุกตัวก็ตาม
3. การแสดงข้อมูล 3 column โดยใช้ TextView บน LinearLayout
กำหนดให้ แท่งแรกกว้าง 25% (0.25) แท่งที่สอง 50% (0.5) แท่งที่สาม 25% (0.25)
แก้ไขในแฟ้ม linearlayout.xml เดิม
ไปอ่านมาจาก https://devahoy.com/posts/android-layout-linear-layout/
ปรับ properties ที่ใช้ใน TextView ดังนี้
background="#00ff00" คือ สีพื้นเขียว padding="5dp" คือ ช่องว่างระหว่างตัวอักษรกับขอบใน gravity="center" คือ อยู่ตรงกลางของ TextView layout_width="wrap_content" คือ ขนาดเท่าข้อมูล layout_height="match_parent" คือ ขนาดเต็มจอ layout_weight="0.50" คือ กว้าง 50% layout_marginStart="5dp" คือ เว้นว่างทางซ้าย
4. การสร้าง relativelayout.xml
เลือก Layout XML File ที่มี Root Tag = RelativeLayout
แล้วเพิ่มตัวเลือกเข้าไปใน menu จะได้เรียกมาทดสอบได้ง่าย
แก้ให้ครบทุกจุดนะครับ เพราะมีเยอะเลยที่ต้องแก้ให้สอดคล้องกัน
แม้เป็นตัวเลือกที่ 3 แต่ต้องกำหนด vf.setDisplayedChild(2);
อ่านความหมายของ Layout ที่ devahoy.com
เรียบเรียงได้ว่า LinearLayout และ RelativeLayout
ต่างเป็น View Group เหมือน Container มีไว้รวบรวม Child View หรือ Widget
มีสิ่งที่ต่างกันใน 2 Layout คือ การจัดเรียง
ถ้า LinearLayout ก็จัดเรียงแบบ horizontal หรือ vertical
ถ้า RelativeLayout ก็จัดเรียงอย่างมีความสัมพันธ์ ซึ่งกันและกัน
การจัดเรียงค่อนข้างเป็นอิสระ และเชื่อมโยงตามลำดับ ทำให้นึกถึง linked list
ปรับ properties ที่ใช้ใน TextView ดังนี้
id="@+id/tv1" คือ การตั้งชื่อให้ TextView layout_toEndOf="@+id/tv1" คือ อยู่ต่อท้าย tv1 layout_alignParentStart="true" คือ ตำแหน่งเริ่มซ้ายของจอ textSize="16sp" คือ ขนาดตัวอักษร 16 layout_below="@+id/tv1" คือ อยู่ข้างล่างต่อจากภาพ tv1 layout_toEndOf="@+id/tv1" คือ เริ่มต่อจากด้านขวาของ tv1 gravity="center|center_vertical" คือ อักษรอยู่กลาง layout_alignParentEnd="true" คือ ชิดด้านซ้ายของจอภาพ layout_alignParentBottom="true" คือ ชิดด้านล่างขอจอภาพ
5. การทำภาพ overlay แบบ RelativeLayout
สร้าง Layout ใหม่ชื่อ overlay.xml และ Root Tag = RelativeLayout
ส่งภาพเข้า drawable ผมเตรียมภาพขนาด 360*640
เพราะโทรศัพท์ของผมขนาดเท่านี้
https://www.whatismyscreenresolution.com/
ปรับ properties ที่ใช้ใน TextView ดังนี้
android:layout_marginTop="?attr/actionBarSize" คือ ของ RelativeLayout android:contentDescription="@string/data" คือ รายละเอียดของภาพ android:scaleType="fitXY" คือ กำหนดให้ภาพพอดีกับตำแหน่ง X และ Y app:srcCompat="@drawable/lp01" คือ การใส่ภาพจาก drawable gravity="center" คือ ข้อความอยู่ตรงกลาง textSize="60sp" คือ ขนาดตัวอักษร textColor="#000000" คือ สีตัวอักษร background="#00ff00" คือสีพื้น alpha="0.3" คือ การกำหนดระดับความโปร่งแสง layout_alignParentStart="true" คือ ชิดด้านซ้าย
6. copy overlay.xml แล้ว paste constraintlayout.xml
เข้าไปอ่าน blog ของ akeorcist.com เห็นชัดเลยว่า
Android เชียร์ให้ใช้ ConstraintLayout แทน RelativeLayout
เพราะจัดการง่ายผ่าน UI Builder และมีขนาดเล็กกว่า
งานที่เคยทำใน RelativeLayout สามารถแปลงเป็น ConstraintLayout อัตโนมัติ
จึงคัดลอก overlay.xml เป็นวางแฟ้มเป็นชื่อใหม่คือ constraintlayout.xml
เข้า design แล้วคลิ๊กที่ RelativeLayout ใน Component Tree
กด Right Click จะมี Convert RelativeLayout to ConstraintLayout มาให้
แล้วมีให้เลือกทำ Flatten Layout Hierarchy
กรณีของผม ต้องปรับรายละเอียดใน ImageView และ TextView
เพราะ layout เปลี่ยน properties ก็ต้องเปลี่ยน เพี้ยนไปบ้าง
ใน ImageView ก็ต้องทำให้ขอบภาพชิดกับของ parent ดังนี้
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"
แต่ข้อความใน TextView ถูกเปลี่ยนให้เชื่อมกับส่วนอื่น ทำให้อยู่ตรงกลางของจอ
app:layout_constraintRight_toRightOf="@+id/imageView" app:layout_constraintLeft_toLeftOf="@+id/imageView" app:layout_constraintTop_toBottomOf="@+id/textView" app:layout_constraintBottom_toTopOf="@+id/button"
7. การสร้าง gridlayout.xml
พบว่า GridLayout เป็นเค้าโครงคล้ายตาราง ใส่ข้อมูลเป็นช่องตาม row หรือ column
แล้วมี GridView ที่ช่วยให้การใส่ image เป็น gallery ได้ง่าย
รองรับการใส่ข้อมูลจำนวนมาก มีความเป็นอัตโนมัติผ่านการจัด orientation
Default คือ orientation:horizontal คือ เรียงแนวนอนไปทางขวาจนจบแถว
แล้วขึ้นแถวต่อไป
ขณะทดสอบพบว่า SDK ที่ผมใช้ตัวล่างที่ติดตั้งไว้ คือ API 19
แล้วไม่รองรับ layout_weight in LinearLayout. This is supported in API 21.
ตัวอย่างการใส่ TextView เข้าไปใน Row และ Column ที่กำหนด
<TextView android:layout_row="0" android:layout_column="0" android:id="@+id/textView3" android:textSize="16sp" android:background="#00ff00" android:layout_width="80dp" android:layout_height="80dp" android:text="@string/data" />
ตัวอย่างการใส่ Button เข้าไปอัตโนมัติ ช่องไหนว่างตาม orientation ก็เข้าไปเลย
<Button android:id="@+id/button6" android:layout_width="80dp" android:layout_height="80dp" android:text="@string/data" />
8. การสร้าง tablelayout.xml
เป็นการวางเค้าโครงให้รองรับการใส่ข้อมูลเป็น row กับ column
ทำให้นึกถึงการเขียน Table ในภาษา HTML ที่มี tr กับ td
ทำ span เพื่อขยายพื้นที่ของ cell ได้
เมื่อใส่ TextView ลงไปก็เป็นเพียงข้อมูลที่นำมาต่อกันใน 1 cell
แต่ TextView ที่มี android:layout_weight=”1″ จะมีน้ำหนักเป็น cell
มักต้องใส่ในทุก TextView เพื่อประกาศการเป็น cell
ตัวอย่างการเค้าโครงการวาง Tag ของ TableLayout
<TableLayout> <TableRow> <TextView android:layout_weight="1"> <TextView android:layout_weight="1"> </TableRow> </TableLayout>
</process>
<website_guide>
+ https://www.whatismyscreenresolution.com/
+ http://www.akexorcist.com/..new-android-layout.html
+ https://devahoy.com/..relative-layout-tutorial/
+ http://www.journaldev.com/..viewflipper-example-tutorial
+ http://www.programcreek.com/..&method=setDisplayedChild
+ https://developer.android.com/../view/ViewGroup.html
+ https://developer.android.com/guide/topics/ui/layout/grid.html
+ https://developer.android.com/../widget/TableLayout.html
</website_guide>
หมายเหตุ
ถ้าสนใจติดตามเนื้อหาในบล็อกนี้ สามารถ subscribe ด้วย email ที่อยู่ข้างขวา หรือ click here