การสร้าง Tab บน Android ด้วยการใช้ TabHost และ ActionBar Tab

บทความ Android วันนี้ ขอนำเสนอเรื่อง การสร้าง Tab ให้กับแอพ Android ตัวอย่าง Tab ใน Android จะมีหน้าตาแบบนี้
การสร้าง Tab บน Android ทำได้ 2 วิธีคือ
สร้างโปรเจ็ค
เริ่มต้นมา ก็ให้ทำการสร้างโปรเจ็คขึ้นมาครับ
สร้าง Tab โดยใช้ TabHost
สำหรับวิธีแรก เป็นการสร้าง Tab โดยใช้ TabHost ซึ่งมันทำได้ทั้งการ extends TabActivity
ซึ่งปัจจุบันมัน deprecated
ไปแล้ว นั่นหมายถึงในอนาคตมันถูกยกเลิกไปแล้ว แต่ว่าปัจจุบันยังใช้ได้อยู่ แค่ไม่นิยม และไม่แนะนำ ให้ใช้ และการทำ TabHost โดยไม่ใช้ TabActivity
TabHost เป็นเลเอาท์ชนิดหนึ่ง ที่ภายในจะมี TabWidget ไว้แสดงชื่อของแท็ป และอีกส่วนคือ FrameLayout เอาไว้เป็นส่วนแสดงข้อมูล
ตัวอย่างโครงสร้างของ TabHost เมื่ออยู่ในรูป xml โดยแก้ไขไฟล์ activity_my.xml
เป็นดังนี้ ( ตัว TabHost จะประกาศ id เป็นชื่อเราเอง ส่วน TabWidget และ FrameLayout จะใช้ id ของทาง Android นั่นก็คือ @android:id/tabs
และ @android:id/tabcontent
)
<TabHost android:id="@+id/tabhost" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
<LinearLayout android:id="@+id/linear" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content"> </TabWidget>
<FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="match_parent"> </FrameLayout>
</LinearLayout>
</TabHost>
จากนั้นเปิดไฟล์ MyActivity
ขึ้นมา ในส่วนของ onCreate()
จะเป็นดังนี้
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my);
TabHost tabHost = (TabHost) findViewById(R.id.tabhost);
TabHost.TabSpec tabSpec = tabHost.newTabSpec("tab1") .setIndicator("KitKat") .setContent(new Intent(this, Tab1.class));
TabHost.TabSpec tabSpec2 = tabHost.newTabSpec("tab2") .setIndicator("Jelly Bean") .setContent(new Intent(this, Tab2.class));
TabHost.TabSpec tabSpec3 = tabHost.newTabSpec("tab3") .setIndicator("Gingerbread") .setContent(new Intent(this, Tab3.class));
tabHost.addTab(tabSpec); tabHost.addTab(tabSpec2); tabHost.addTab(tabSpec3);}
ด้านบนเป็นการ เชื่อม TabHost ที่ประกาศไว้ใน Layout xml จากนั้นก็สร้าง TabHost โดยหลักการ และขั้นตอนการสร้าง TabHost มีดังนี้
- สร้างออปเจ็ค
TabHost
ขึ้นมา โดยใช้ TabHost ที่ประกาศไว้ใน xml และทำการเรียก.setup()
- สร้าง
TabSpec
ขึ้นมา ภายในTabSpec
ประกอบไปด้วย 2.1newTabSpec()
: เป็นเหมือนการกำหนดชื่อ tag 2.2setIndicator()
: ใส่ชื่อ Title ที่เราต้องการให้มันโชว์ในส่วนแท็ป 2.3setContent()
: เลือก Content เป็น Activity ที่เราต้องการ - เพิ่ม
TabSpec
เข้าไปในTabHost
ด้วยคำสั่งaddTab()
่ต่อมาเรายังไม่มี Activity ที่ชื่อ Tab1
, Tab2
และ Tab3
ก็ทำการสร้างขึ้นมาครับ โดยทั้งสามตัวใช้ไฟล์ layout ร่วมกัน ต้องการให้แค่เปลี่ยนสี background เท่านั้น
ไฟล์ tab.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:id="@+id/linear" android:layout_width="match_parent" android:layout_height="match_parent">
</LinearLayout>
ไฟล์ Tab1.java
package com.devahoy.android.simpletab;
import android.app.Activity;import android.graphics.Color;import android.os.Bundle;import android.widget.LinearLayout;
public class Tab1 extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tab);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.linear); linearLayout.setBackgroundColor(Color.parseColor("#f62355")); }}
ไฟล์ Tab2.java
package com.devahoy.android.simpletab;
import android.app.Activity;import android.graphics.Color;import android.os.Bundle;import android.widget.LinearLayout;
public class Tab2 extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tab);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.linear); linearLayout.setBackgroundColor(Color.parseColor("#00b06b")); }}
ไฟล์ Tab3.java
package com.devahoy.android.simpletab;
import android.app.Activity;import android.graphics.Color;import android.os.Bundle;import android.widget.LinearLayout;
public class Tab3 extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tab);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.linear); linearLayout.setBackgroundColor(Color.parseColor("#3982d7")); }}
ทำการแก้ไขไฟล์ AndroidManifest.xml
ด้วย เนื่องจากเราทำการเพิ่ม Activity เข้าไป
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.devahoy.android.simpletab" >
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MyActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
<activity android:name=".Tab1" /> <activity android:name=".Tab2" /> <activity android:name=".Tab3" /> </application>
</manifest>
ทดสอบรันโปรแกรมดู ซักหน่อย…
ปรากฎว่ามี error
Caused by: java.lang.IllegalStateException: Did you forget to call 'public void setup(LocalActivityManager activityGroup)'?
เนื่องจากว่าตัว TabHost มันจำเป็นต้องใช้ ActivityGroup
ด้วย ซึ่งจริงๆถ้าเราใช้ TabActivity
มันจะไม่มีปัญหาอะไร เพราะว่า TabActivity
มันก็ extends มาจาก ActivityGroup
อีกทีนึง
แต่เมื่อไม่มี ActivityGroup
เราก็แก้ปัญหาด้วยการใช้ LocalActivityManager
เข้ามาช่วยครับ โดยการประกาศตัวแปรเป็น global จากนั้นก็ส่งตัวแปรใช้ tabHost.setUp()
ดังนี้
public class MyActivity extends Activity {
LocalActivityManager mLocalActivityManager;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my);
mLocalActivityManager = new LocalActivityManager(this, false); mLocalActivityManager.dispatchCreate(savedInstanceState);
TabHost tabHost = (TabHost) findViewById(R.id.tabhost); tabHost.setup(mLocalActivityManager);
... }}
จากนั้นเพิ่มเมธอด onResume()
และ onPause()
สุดท้ายไฟล์จะได้ดังนี้
package com.devahoy.android.simpletab;
import android.app.Activity;import android.app.LocalActivityManager;import android.content.Intent;import android.os.Bundle;import android.widget.TabHost;
public class MyActivity extends Activity {
LocalActivityManager mLocalActivityManager;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); mLocalActivityManager = new LocalActivityManager(this, false); mLocalActivityManager.dispatchCreate(savedInstanceState);
TabHost tabHost = (TabHost) findViewById(R.id.tabhost); tabHost.setup(mLocalActivityManager);
TabHost.TabSpec tabSpec = tabHost.newTabSpec("tab1") .setIndicator("KitKat") .setContent(new Intent(this, Tab1.class)); TabHost.TabSpec tabSpec2 = tabHost.newTabSpec("tab2") .setIndicator("Jelly Bean") .setContent(new Intent(this, Tab2.class)); TabHost.TabSpec tabSpec3 = tabHost.newTabSpec("tab3") .setIndicator("Gingerbread") .setContent(new Intent(this, Tab3.class));
tabHost.addTab(tabSpec); tabHost.addTab(tabSpec2); tabHost.addTab(tabSpec3); }
@Override protected void onPause() { super.onPause(); mLocalActivityManager.dispatchPause(!isFinishing());
}
@Override protected void onResume() { super.onResume(); mLocalActivityManager.dispatchResume(); }}
ทดสอบรันโปรแกรมอีกครั้ง
ทีนี้ถ้าอยากแก้ไขข้อมูลแต่ละ Tab ก็ไปจัดการหน้า Tab1, Tab2, Tab3 เหมือนเป็นหน้า Activity อีกหน้าได้เลย
สร้าง Tab โดยใช้ ActionBar
การสร้าง Tab โดยใช้ ActionBar นั้นเป็นวิธีดีกว่า TabHost เนื่องจากว่ามันทันสมัยกว่า เป็นปัจจุบันกว่า รวมถึงง่ายกว่าด้วย
ขั้นตอนการสร้าง Tab จาก ActionBar นั้น มีขั้นตอนดังนี้
- สร้าง
ActionBar
ขึ้นมา จากนั้นก็สั่งให้เป็นโหมด Tab - สร้าง
ActionBar.Tab
ขึ้นมา โดยใช้ActionBar.newTab()
และต้องทำการกำหนดTabListener
ให้กับ Tab ทุกอันด้วย - เพิ่ม Tab เข้าไปใน
ActionBar
ด้วยActionBar.addTab()
เมื่อมีขั้นตอนการสร้างแล้ว ฉะนั้นก็มาเริ่มต้นเลย เริ่มจาก สร้างคลาสใหม่ ขึ้นมาคลาสนึงชื่อว่า ActionBarTabActivity.java
มีโค๊ดดังนี้
package com.devahoy.android.simpletab;
import android.app.ActionBar;import android.app.Activity;import android.app.FragmentTransaction;import android.os.Bundle;
public class ActionBarTabActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); }}
ด้านบนเป็นการกำหนดให้ ActionBar อยู่ในโหมด Tab ภาพด้านล่างเปรียบเทียบว่า Tab กับไม่มี Tab แตกต่างกันอย่างไร
ต่อมาก็ทำการสร้าง ActionBar โดยทำตามขั้นตอนที่กล่าวไว้ด้านบน จะได้โค๊ดดังนี้
package com.devahoy.android.simpletab;
import android.app.ActionBar;import android.app.Activity;import android.app.FragmentTransaction;import android.os.Bundle;
public class ActionBarTabActivity extends Activity implements ActionBar.TabListener {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for (int i = 1; i <= 3; i++) { ActionBar.Tab tab = actionBar.newTab() .setText("Tab#" + i) .setTabListener(this); actionBar.addTab(tab); } }
@Override public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}}
ทดสอบรันโปรแกรม
เราก็จะได้ตัว Tab ขึ้นมาแล้ว ต่อไป ก็จะเป็นการใส่ content ให้กับ Tab แต่ละหน้า การใส่คอนเท้น เราจะใช้ Fragment เพื่อเอาไว้แสดงข้อมูล และใช้การ implement ActionBar.TabListener
สร้างคลาส MyFragment
ขึ้นมา เป็น inner class ไปเลยครับ ดังนี้
package com.devahoy.android.simpletab;
import android.app.ActionBar;import android.app.Activity;import android.app.Fragment;import android.app.FragmentTransaction;import android.graphics.Color;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.LinearLayout;
public class ActionBarTabActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for (int i = 1; i <= 3; i++) { ActionBar.Tab tab = actionBar.newTab() .setText("Tab#" + i) .setTabListener(this); actionBar.addTab(tab); } }
public static class MyFragment extends Fragment {
private String color; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); color = args.getString("color"); }
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.tab, container, false);
LinearLayout linearLayout = (LinearLayout) view.findViewById(R.id.linear); linearLayout.setBackgroundColor(Color.parseColor(color));
return view; } }}
ด้านบนเป็นการสร้าง Fragment
ขึ้นมา เพื่อจะเอาไว้แสดงเนื้อหา ซึ่งตัวเนื้อหา เป็นเพียงแค่ การเปลี่ยนสีพื้นหลังเฉยๆ โดยรับ argument เป็นโค๊ดสี ที่เมธอด onCreate
และ onCreateView()
ก็นำค่าที่ได้ มาแสดงเป็นพื้นหลัง
ต่อมาก็ทำการ implements ActionBar.TabListener
โดยมี 3 เมธอด ดังนี้
package com.devahoy.android.simpletab;
import android.app.ActionBar;import android.app.Activity;import android.app.Fragment;import android.app.FragmentTransaction;import android.graphics.Color;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.LinearLayout;
public class ActionBarTabActivity extends Activity implements ActionBar.TabListener {
@Override protected void onCreate(Bundle savedInstanceState) { ... }
@Override public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}}
ส่วนที่เราต้องการคือ เมื่อไหร่ที่ทำการกด Tab ก็จะให้แสดงเนื้อหาขึ้นมา ฉะนั้น เราก็จะทำในเมธอด onTabSelected()
แล้วก็ทำการสร้าง Fragment ขึ้นภายใน Listener นี้ดังนี้
@Overridepublic void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
Fragment fragment = new MyFragment(); Bundle args = new Bundle();
switch (tab.getPosition()) { case 0: args.putString("color", "#2574a9"); fragment.setArguments(args); break; case 1: args.putString("color", "#36d5b5"); fragment.setArguments(args); break; case 2: args.putString("color", "#f9640f"); fragment.setArguments(args); break; }
ft.replace(android.R.id.content, fragment);}
ด้านบนก็เป็นการสร้าง Fragment โดยการส่ง argument เป็นค่าสีไปด้วย คำสั่ง setArguments()
ค่านี้ จะสามารถเข้าถึงด้วย getArguments().getString()
ในคลาส Fragment สุดท้ายลองรันโปรแกรม จะได้ผลลัพธ์ดังนี้
อีกบทความนึง ที่พูดถึงการทำ ActionBar Tab ร่วมกับ ViewPager ครับ อ่านได้ที่นี่ สอนการใช้งาน ViewPager
สรุป
ถึงแม้ว่าการสร้าง Tab บน Android จะสามารถสร้างได้หลายวิธี แต่วิธีปัจจุบันที่ดีที่สุด ควรจะใช้ ActionBar.Tab
มากกว่า เนื่องจากมันถูกออกแบบมาให้ใช้ร่วมกับ Fragment
และ FragmentManager
ส่วนการทำ TabHost หรือใช้ TabActivity มันก็สร้างได้เหมือนกัน แต่ว่ามันถูก deprecated ตั้งแต่ API 13 แล้ว ฉะนั้นเลี่ยงได้ก็เลียง ไม่ต้องใช้ครับ เพราะมันการันตีแน่นอน ว่าอนาคต มันจะถูกยกเลิกถาวร จะได้ไม่ต้องมานั่งแก้ไขบ่อยๆ เพื่อให้มองเห็นภาพรวมสั้นๆง่ายๆคือ
- ActionBar.Tab : เป็นรูปแบบใหม่ ใช้ Fragment
- TabHost : เป็นรูปแบบเก่า ใช้ Activity
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust