Khóa học lập trình Android cơ bản

Serial tutorial hướng dẫn lập trình Android cơ bản

Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản 5/5 (68 reviews)

Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản

Đã đăng 2016-10-17 08:23:50 bởi HowKteam
14 bình luận 18523 lượt xem
Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản 5 /5 stars (2 reviews)
 

Dẫn nhập

Ở các bài học trước, chúng ta đã cùng nhau tìm hiểu về cách tạo danh sách đơn giản sử dụng 2 loại View phổ biến là LISTVIEW và cao cấp hơn là RECYCLERVIEW. Chúng đều là những View hỗ trợ hiển thị danh sách mạnh, dù rằng có một số điểm khác nhau.

Bài học này chúng ta tiếp tục tìm hiểu một dạng “danh sách” đặc biệt khác là View Pager và Tab, mà các bạn thường thấy trong các ứng dụng như Viber, Zalo, các ứng dụng đọc truyện tranh,…


Nội dung

Để đọc hiểu bài này tốt nhất các bạn nên có kiến thức cơ bản về các phần:

Trong bài học này, chúng ta sẽ cùng tìm hiểu các vấn đề:

  • ViewPager.
  • Tab trong Android và liên kết tab với ViewPager.
  • Khuyến mãi thêm: CoordinatorLayout.

Mại dzô!


ViewPager và FragmentPagerAdapter

Các bạn nhìn hình sau có thấy kích thích không…

(Ảnh hơi nặng)

Đó, những gì các bạn thấy nó được gọi là ViewPager, vậy ViewPager là…

  • Một dạng cấu trúc View trượt ngang.
  • Bao gồm nhiều trang, mỗi trang là một Fragment con.
  • Nếu như trong ListView / RecyclerView, các item muốn đổ vào ListView hay RecyclerView thì phải qua Adapter. Ở đây ta có thể coi như mỗi Fragment chính là một Item, và Adapter ở đây được gọi là FragmentPagerAdapter.

Một ví dụ khác là ứng dụng lịch của Android, ở đây minh họa phiên bản 4.1:

Sau đây chúng ta cùng tìm hiểu cách tạo ra một trang tương tự, đi kèm với một thanh Tab có dạng như:


Xây dựng View Pager

Bước 1: Như thường lệ, chúng ta tạo một project mới toanh mang tên ViewPagerExample và chọn Empty Activity:

Bước 2: Chúng ta vẫn tạo ra một cặp MainActivity.java activity_main.xml như thường lệ. À các bạn không cần phải tạo từng file một đâu, chúng ta tạo được cả cặp bằng cách chuột phải vào package chính (khoanh đỏ như hình dưới), chọn New > Activity > Empty Activity:

Đặt tên cho Activity như sau:

Và chúng ta được MainActivity.java ban đầu:

package com.howkteam.viewpagerexample;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;

import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }
}

Bước 3: Ý tưởng thực hiện như sau: Chúng ta sẽ tạo ra một Activity chứa ViewPager, ViewPager chứa 3 Fragment khác nhau và có màu sắc là xanh / đỏ / vàng.

Tạo một file layout dành cho fragment có tên fragment_main.xml:

<RelativeLayout
    android:id="@+id/rl_fragment"
    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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.howkteam.viewpagerexample.MainActivity$PlaceholderFragment">

    <TextView
        android:id="@+id/section_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

Do nội dung của Fragment không có gì đặc biệt, mặt khác nó lại là thành phần của ViewPager nên chúng ta sẽ viết Fragment dưới dạng class con của MainActivity.java. Chúng ta để class là static, nhằm tách biệt sự ràng buộc với class mẹ.

public static class PlaceholderFragment extends Fragment {

    private static final String KEY_COLOR = "key_color";

    public PlaceholderFragment() {
    }

    // Method static dạng singleton, cho phép tạo fragment mới, lấy tham số đầu vào để cài đặt màu sắc.
    public static PlaceholderFragment newInstance(int color) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle args = new Bundle();
        args.putInt(KEY_COLOR, color);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        RelativeLayout relativeLayout = (RelativeLayout) rootView.findViewById(R.id.rl_fragment);

        /**
         * Số 1: Màu xanh.
         * Số 2: Màu đỏ.
         * Số 3: Màu vàng.
         */
        switch (getArguments().getInt(KEY_COLOR)) {
            case 1:
                relativeLayout.setBackgroundColor(Color.GREEN);
                break;
            case 2:
                relativeLayout.setBackgroundColor(Color.RED);
                break;
            case 3:
                relativeLayout.setBackgroundColor(Color.YELLOW);
                break;
            default:
                relativeLayout.setBackgroundColor(Color.GREEN);
                break;
        }

        TextView textView = (TextView) rootView.findViewById(R.id.section_label);
        textView.setText("Kteam");
        return rootView;
    }
}

Mình xin giải thích code một chút:

  1. KEY_COLOR: Ở đây mình đặt dữ liệu màu sắc dưới dạng số, các giá trị được lưu dạng key-value (khóa-giá trị) trong gói dữ liệu Bundle của Android (xem lại bài  INTENT & MANIFEST để hiểu thêm).
  2. Hàm newInstance() ở đây được sử dụng thay cho constructor và cũng là để dễ hiểu hơn, đỡ phải dùng constructor cồng kềnh. Chỉ cần truyền vào tham số int color và lưu vào Bundle là đủ.
  3. Tại hàm onCreatedView, như chúng ta đã biết, các view của fragment được khởi tạo tại đây. Vì thế chúng ta lấy con số được đưa vào Bundle khi nãy ra dùng, và ở đây đặt là:

1: Màu xanh.

2: Màu đỏ.

3: Màu vàng.

Bước 4: Chúng ta tạo tiếp một class con cũng nằm trong MainActivity.java, class này kế thừa từ Adapter dành cho ViewPager (FragmentPagerAdapter):

public class SectionsPagerAdapter extends FragmentPagerAdapter {

    public SectionsPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        // position + 1 vì position bắt đầu từ số 0.
        return PlaceholderFragment.newInstance(position + 1);
    }

    @Override
    public int getCount() {
        return 3;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
            case 0:
                return "SECTION 1";
            case 1:
                return "SECTION 2";
            case 2:
                return "SECTION 3";
        }
        return null;
    }
}

Nào nào, xem chúng ta có gì nào:

  • Hàm getItem(): Đây chính là hàm mà ta cần trả về Fragment tương ứng, và nó chính là cái Fragment chúng ta khởi tạo và xử lý ở bước 3.
  • Hàm getCount(): Ở đây bạn chỉ định số lượng Fragment được tạo ra. Hiện đang là 3, cho thành 4-5 gì đấy vô tư.

Bước 5: Lúc này phần thân hàm chính của MainActivity.java sẽ có nội dung:

private SectionsPagerAdapter mSectionsPagerAdapter;

private ViewPager mViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

    mViewPager = (ViewPager) findViewById(R.id.container);
    mViewPager.setAdapter(mSectionsPagerAdapter);

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(mViewPager);
}

Chúng ta khởi tạo cho nó một thanh Toolbar phía trên và một thanh Tab điều hướng ở ngay phía dưới.

TabLayout ở đây chính là thanh có 3 chữ SECTION 1-2-3. Các tiêu đề này được đặt theo đúng PageTitle lấy từ getPageTitle() trong SectionPagerAdapter.

Và nội dung activity_main.xml tương ứng sẽ là:

<?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.howkteam.viewpagerexample.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>

À giải thích từng phần như trên có lẽ các bạn sẽ thấy code hơi rời rạc nhỉ? Trả lại các bạn code đầy đủ sau khi thực hiện các bước trên của MainActivity.java nhé:

package com.howkteam.viewpagerexample;

import android.graphics.Color;
import android.os.Bundle; 
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private SectionsPagerAdapter mSectionsPagerAdapter;

    private ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

        mViewPager = (ViewPager) findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(mViewPager);
    }

    public static class PlaceholderFragment extends Fragment {

        private static final String KEY_COLOR = "key_color";

        public PlaceholderFragment() {
        }

        // Method static dạng singleton, cho phép tạo fragment mới, lấy tham số đầu vào để cài đặt màu sắc.
        public static PlaceholderFragment newInstance(int color) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(KEY_COLOR, color);
            fragment.setArguments(args);
            return fragment;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            RelativeLayout relativeLayout = (RelativeLayout) rootView.findViewById(R.id.rl_fragment);

            /**
             * Số 1: Màu xanh.
             * Số 2: Màu đỏ.
             * Số 3: Màu vàng.
             */
            switch (getArguments().getInt(KEY_COLOR)) {
                case 1:
                    relativeLayout.setBackgroundColor(Color.GREEN);
                    break;
                case 2:
                    relativeLayout.setBackgroundColor(Color.RED);
                    break;
                case 3:
                    relativeLayout.setBackgroundColor(Color.YELLOW);
                    break;
                default:
                    relativeLayout.setBackgroundColor(Color.GREEN);
                    break;
            }

            TextView textView = (TextView) rootView.findViewById(R.id.section_label);
            textView.setText("Kteam");
            return rootView;
        }
    }

    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            // position + 1 vì position bắt đầu từ số 0.
            return PlaceholderFragment.newInstance(position + 1);
        }

        @Override
        public int getCount() {
            return 3;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
                case 0:
                    return "SECTION 1";
                case 1:
                    return "SECTION 2";
                case 2:
                    return "SECTION 3";
            }
            return null;
        }
    }
}

Lúc này chúng ta chỉ việc chạy và build. Nếu như gặp lỗi lúc build thì nhiều khả năng là do bạn đã thiếu dependencies trong project. Hãy chắc rằng có 2 dòng sau trong file build.gradle trong thư mục /app

compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:design:24.2.1'

Kết quả đầu ra khá là mỹ mãn: Link kết quả


Kết luận

  • Qua bài này chúng ta đã nắm được ViewPager là gì. Nó liên kết thế nào với Fragment cũng như biết thên một công dụng mới của Fragment.
  • Bài sau chúng ta sẽ tìm hiểu tổng quan về ANIMATION & TRANSITION TRONG ANDROID. Ngắn thôi, nhưng nhìn thích lắm.
  • Cảm ơn các bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập – Thử thách – Không ngại khó”.

Tài liệu 

Nhằm phục vụ mục đích học tập Offline của cộng đồng, Kteam hỗ trợ tính năng lưu trữ nội dung bài học Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản dưới dạng file PDF trong link bên dưới.

Ngoài ra, bạn cũng có thể tìm thấy các tài liệu được đóng góp từ cộng đồng ở mục TÀI LIỆU trên thư viện Howkteam.com

Đừng quên like hoặc +1 Google để ủng hộ Kteam và tác giả nhé! 


Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần BÌNH LUẬN bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng. 

 

Chia sẻ:
Thảo luận Hỏi và đáp Báo lỗi bài viết
Hủy bỏ   hoặc  
Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản
harleyquinnidol 2018-07-22 08:47:12

làm cách nào để mình lướt màn hình theo chiều dọc thì các fragment cũng di chuyển theo chiều dọc ạ?

0 bình chọn
Reply
Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản
nguyenxuantoan1998 2018-04-22 16:04:10

cho mình hỏi chút.ví dụ mình có 3 tab:tab1,tab2,tab3.

mình cho ở trong mỗi tab là một cái Button.vậy xử lí button trong các tab đó kiểu gì ạ???

1 bình chọn
Reply
Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản
noivvd00071 2017-05-28 18:40:51

Theo mình nghĩ
Code ở trên cần bổ sung thêm các code sau mới chay được:
 AndroidManifest.xml :<activity android:name="com.vuvannoi.viewpagerexample.MainActivity"
            android:theme="@style/AppTheme.NoActionBar">
styles.xml:<style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

1 bình chọn
Reply
Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản
Nguyen Ba Trinh 2017-05-19 01:15:45

anh ơi em bị lỗi ở "setSupportActionBar(toolbar);" 

 Caused by: java.lang.IllegalStateException: This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.

sửa làm sao anh ơi TT-TT

0 bình chọn
Reply
View all 1 comments
Kteam - Howkteam Free Education
noivvd00071 2017-05-28 18:36:43
Bạn vô AndroidManifest.xml thêm dòng code sau đây : (Tiếp đó qua ) styles.xml và thêm code sau đây: Chú ý search file dùng click đúp Shift
0 bình chọn
Reply
Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản
beehomiez 2016-12-26 18:39:38
e chạy bị lỗi dòng android:theme="@style/AppTheme.AppBarOverlay" trong activity_main a ơi, hình như cái apptheme này chưa được định nghĩa trong đây n k hiểu, e chưa biết thêm kiểu gì
1 bình chọn
Reply
View all 4 comments
Kteam - Howkteam Free Education
duyledat197 2016-12-28 10:58:51
mình cũng bị y chang bạn, ko biết sửa kiểu gì @@
0 bình chọn
Reply
Kteam - Howkteam Free Education
K9 2016-12-28 22:04:07
e thử code của a chưa
0 bình chọn
Reply
Kteam - Howkteam Free Education
beehomiez 2016-12-30 09:45:38
e làm theo như a mà n bị lỗi đấy
0 bình chọn
Reply
Kteam - Howkteam Free Education
Trúc 2017-01-09 12:33:22
Mình sửa được rồi bạn..Vô file styles.xml thêm các dòng khai báo này nè ( <style name="AppTheme.NoActionBar"> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style> )
0 bình chọn
Reply
Giao diện trượt ngang với ViewPager và Tab trong Android cơ bản
Trúc 2016-12-23 21:31:32
em thêm dòng compile 'com.android.support:design:24.2.1' này vào nhưng bị lỗi, dòng trên của em là compile 'com.android.support:appcompat-v7:25.0.1' vậy em có cần sửa phiên bản dòng dưới lại không ạ? và sửa ra sao mong các anh giúp đỡ.
0 bình chọn
Reply
View all 3 comments
Kteam - Howkteam Free Education
K9 2016-12-24 09:47:37
thử sửa 24 thành 25 xem e
0 bình chọn
Reply
Kteam - Howkteam Free Education
Trúc 2016-12-24 10:29:55
Dạ e sửa tối qua nhiều lần lắm rồi..e cũng lên mạng search tìm các phiên bản 25... rồi nhưng vẫn không được. Khởi chạy máy ảo lên thì app cứ bị đóng hoài giống lần đầu trong clip chạy..rồi e chạy lại cả chục lần vẫn bị y vậy cứ báo app bị closed...Em copy code từ trên đây về luôn rồi nhưng vẫn chạy bị vậy..Mong các a giúp đỡ.
0 bình chọn
Reply
Kteam - Howkteam Free Education
beehomiez 2016-12-26 18:35:10
thay chỗ 24 25 thành dấu + xem được k
0 bình chọn
Reply
Hủy bỏ   hoặc  
Hủy bỏ   hoặc  

Chiến dịch

Kteam - Howkteam Free Education