Bài 3: Hướng dẫn tạo ứng dụng Android có nhiều Tab bằng ViewPager, Fragment và Tablayout

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
    • fragmenttabs
    • Để 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
    • actionbarsetup
    • 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

Screen Shot 2018-06-09 at 2.32.08 AM

———_END ————-

Advertisements