Mục tiêu:
- Biết cách tạo giao diện ứng dụng dạng Tab
1. Lớp Fragment
- Được tích hợp vào API level 4 support libraries nên có thể sử dụng ở các thiết bị cũ
- Nhúng Fragment vào Activity bằng việc kế thừa Activity từ FragmentActivity hoặc AppCompatActivity
- Thêm Fragment thông qua XML thẻ <fragment>
- Hoặc thêm động trên code thông qua FragmentManager và FragmentTransaction
Ví dụ:
FragmentManager fm = getFragmentManager();
FragementTransaction ft = fm.beginTransaction();
SalesFragment sf = new SalesFragment();
ft.add(R.id.salesChart, sf);
ft.commit();
- Giao tiếp giữa các Fragments
- Fragment chỉ nên giao tiếp qua lại với parent Activity
- Không nên giao tiếp trực tiếp giữa các Fragment với nhau
Ví dụ:
// Truy cập tới activity từ Fragment thông qua getActivity()
ListView employeeNames = (ListView) getActivity().findViewById(R.id.namesList);
// Fragment gửi thông tin cho activity bằng việc cài đặt hàm onItemSelectedListener()
// Khi đó Activity phải implement listener đó ở mức class
// Đây cũng là cách gián tiếp các fragment có thể giao tiếp với nhau
// Activity truy cập tới fragment thông qua tham chiếu trực tiếp
SalesFragment salesFragment = (SalesFragment) getFragmentManager().findFragmentById( R.id.SalesFragment );
- Sử dụng Fragment như là Tab trong Activity
- Để nhúng fragments là tab trong activity có thể sử dụng ViewPager
- Khi đó mỗi fragment sẽ là 1 page
- ViewPager quản lý layout và cho phép chuyển qua lại giữa các page
- Thiết lập Action bar
- ActionBar class có thể được sử dụng để thiết lập action bar
- Tuy nhiên để điều khiển các action overflow dễ dàng hơn thì nên sử dụng Toolbar class
- Dùng Toolbar cho phép thay đổi giao diện action bar: thêm logo, nút Up, tiêu đề cho activity…
- Để dùng được Toolbar và loại bỏ ActionBar thì app cần extend AppCompatActivity và sử dụng theme NoActionBar
2. Action Overflow
- Các tính năng ít dùng có thể đưa vào overflow
- Cài đặt onCreateOptionsMenu() để inflate XML menu định nghĩa các item cho mỗi tuỳ chọn.
- Cài đặt code tương ứng trong sự kiện khi item được chọn: onOptionsItemSelected()
3. Thực hành
Mở Android Studio và tạo project với Empty Activity, chỉnh sửa các nội dung như hướng dẫn sau
a. Trong res/layout
Mở activity_main.xml – layout của MainActivity
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="com.ourcompany.catalog.MainActivity"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="@dimen/appbar_padding_top" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways" app:popupTheme="@style/AppTheme.PopupOverlay"> </android.support.v7.widget.Toolbar> <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" /> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> </android.support.design.widget.CoordinatorLayout>
Thêm fragment_catalog.xml – layout cho tab Products
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.ourcompany.catalog.CatalogFragment"> <!-- TODO: Update blank fragment layout --> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="Catalog" /> </FrameLayout>
Tương tự fragment_news.xml, fragment_sales.xml và fragement_maps.xml lần lượt cho các tab News, Sales và Maps
b. Trong res/menu thêm main_menu.xml để chứa các action overflow có nội dung như sau
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:context="com.ourcompany.catalog.MainActivity"> <item android:id="@+id/action_settings" android:orderInCategory="100" android:title="@string/action_settings" app:showAsAction="never" /> </menu>
c. Trong package source code, thêm các lớp cho các Fragment tương ứng
public class CatalogFragment extends Fragment { public CatalogFragment() { // Required empty public constructor } /** * Create new instance of CatalogFragment * @return */ public static CatalogFragment newInstance(){ CatalogFragment fragment = new CatalogFragment(); return fragment; } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // return super.onCreateView(inflater, container, savedInstanceState); // inflate the layout for this fragment return inflater.inflate(R.layout.fragment_catalog, container, false); } }
Tương tự cho NewsFragment.java, SalesFragment.java và MapsFragment.java
d. Trong MainActivity.java, thêm vào inner class SectionPagerAdapter để làm Adapter cho ViewPager
/** * Adapter that represents each page in the app as a fragment */ public class SectionsPagerAdapter extends FragmentPagerAdapter{ public SectionsPagerAdapter(FragmentManager fm){ super(fm); } /** * Return the fragment with the position you specify * @param position * @return */ @Override public Fragment getItem(int position) { // getItem is called to instantiate the fragment for the given page switch (position){ case 0: return CatalogFragment.newInstance(); case 1: return NewsFragment.newInstance(); case 2: return MapsFragment.newInstance(); case 3: return SalesFragment.newInstance(); } return null; } /** * Return the total number of views that are available to the SectionsPagerAdapter * @return */ @Override public int getCount() { // show 4 total pages return 4; } /** * Set the label of the tab in a string format, depending on the tab view's position * @param position * @return */ @Override public CharSequence getPageTitle(int position) { switch (position){ case 0: return "Catalog"; case 1: return "News"; case 2: return "Maps"; case 3: return "Sales"; } return null; } }
Trong lớp MainActivity, khai báo 2 biến sau
private SectionsPagerAdapter mSectionsPagerAdapter; private ViewPager mViewPager;
e. Bổ sung thêm code vào hàm onCreate của MainActivity
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // set up the toolbar // create toolbar using the layout Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); // set as action bar setSupportActionBar(toolbar); // Create adapter that will return a fragment for each of three primary sections of the activity mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); // Set up ViewPager with the sections adapter mViewPager = (ViewPager) findViewById(R.id.container); mViewPager.setAdapter(mSectionsPagerAdapter); // Set up tab layout with the ViewPager TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.setupWithViewPager(mViewPager); }
f. Thực hiện override hàm onCreateOptionsMenu của MainActivity để load các action overflow từ menu_main.xml
/** * Create action overflow menu * @param menu * @return */ @Override public boolean onCreateOptionsMenu(Menu menu) { //return super.onCreateOptionsMenu(menu); // inflate the action overflow based on a menu_main.xml layout file and add items to the menu getMenuInflater().inflate(R.menu.menu_main, menu); return true; }
Chỉnh sửa thuộc tính android:theme của MainActivity trong AndroidManifest để sử dụng tuỳ chọn NoActionBar
<activity android:name="com.ourcompany.catalog.MainActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Lưu ý: build.gradle (Module:app) có các thư viện cần thiết (version có thể khác)
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:26.0.2' compile 'com.android.support:design:26.0.2' compile 'com.android.support:support-v4:26.0.2' }
OK, giờ bạn có thể chạy thử ứng dụng trên Emulator và kết quả như sau
———_END ————-
Hiện tại em có 1 vấn đề như sau, thay vì mình tạo nhiều fragment, nhưng các fragment cùng chung 1 định dạng layout, và menu của mình là 1 array có sẵn đưa vào tablayout, trong array đó có id và tên thì làm cách nào để giải quyết những vấn đề này, để khi mình đưa vào 1 list các tên r dùng id để gọi các fragment theo đúng như tên mình muôn.
ThíchThích