Trang chủHướng dẫnTổng quan về Android TabLayout và ViewPager
Android

Tổng quan về Android TabLayout và ViewPager

CyStack blog 6 phút để đọc
CyStack blog27/08/2025
Locker Avatar

Chris Pham

Technical Writer

Locker logo social
Reading Time: 6 minutes

Trong hướng dẫn này, chúng ta sẽ triển khai ViewPager bên dưới TabLayout mà chúng ta đã triển khai trong hướng dẫn trước.

Tổng quan về Android TabLayout và ViewPager

Tổng quan về Android TabLayout và ViewPager

ViewPager được sử dụng để vuốt qua các trang dữ liệu. Nó thường được sử dụng cùng với các Fragment. Hãy sửa đổi layout từ hướng dẫn trước như sau: activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="<https://schemas.android.com/apk/res/android>"
    xmlns:app="<https://schemas.android.com/apk/res-auto>"
    xmlns:tools="<https://schemas.android.com/tools>"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.journaldev.tablayoutviewpager.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        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.design.widget.TabLayout
            android:id="@+id/tabs"
            style="@style/MyStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabGravity="fill"
            app:tabMode="fixed" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

Trước khi chúng ta thêm ViewPager vào MainActivity, hãy thiết lập adapter của nó.

public class ViewPagerAdapter extends FragmentPagerAdapter {

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

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;
        if (position == 0)
        {
            fragment = new FragmentA();
        }
        else if (position == 1)
        {
            fragment = new FragmentB();
        }
        else if (position == 2)
        {
            fragment = new FragmentC();
        }
        return fragment;
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        String title = null;
        if (position == 0)
        {
            title = "Tab-1";
        }
        else if (position == 1)
        {
            title = "Tab-2";
        }
        else if (position == 2)
        {
            title = "Tab-3";
        }
        return title;
    }
}

ViewPagerAdapter ở trên mở rộng FragmentPagerAdapter. Nó gọi ba Fragment, một cho mỗi trang của nó. Mỗi fragment chứa một ListView như dưới đây: fragment_list.xml

<?xml version="1.0" encoding="utf-8"?>

<ListView xmlns:android="<https://schemas.android.com/apk/res/android>"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/list"/>

Các tệp FragmentA(/B/C).java được đưa ra dưới đây:

public class FragmentA extends Fragment {

    ListView list;

    public FragmentA() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment, container, false);

        list = (ListView) view.findViewById(R.id.list);
        ArrayList stringList= new ArrayList();

        stringList.add("Item 1A");
        stringList.add("Item 1B");
        stringList.add("Item 1C");
        stringList.add("Item 1D");
        stringList.add("Item 1E");
        stringList.add("Item 1F");
        stringList.add("Item 1G");
        stringList.add("Item 1H");
        stringList.add("Item 1I");
        stringList.add("Item 1J");
        stringList.add("Item 1K");
        stringList.add("Item 1L");
        stringList.add("Item 1M");
        stringList.add("Item 1N");
        stringList.add("Item 1O");
        stringList.add("Item 1P");
        stringList.add("Item 1Q");
        stringList.add("Item 1R");
        stringList.add("Item 1S");
        stringList.add("Item 1T");
        stringList.add("Item 1U");
        stringList.add("Item 1V");
        stringList.add("Item 1W");
        stringList.add("Item 1X");
        stringList.add("Item 1Y");
        stringList.add("Item 1Z");

        CustomAdapter adapter = new CustomAdapter(stringList,getActivity());
        list.setAdapter(adapter);

        return view;
    }
}

Lớp CustomAdapter.java cho ListView ở trên là:

public class CustomAdapter extends ArrayAdapter {

    private ArrayList dataSet;
    Context mContext;

    // View lookup cache
    private static class ViewHolder {
        TextView txtName;

    }

    public CustomAdapter(ArrayList data, Context context) {
        super(context, R.layout.row_item, data);
        this.dataSet = data;
        this.mContext = context;

    }

    @Nullable
    @Override
    public String getItem(int position) {
        return dataSet.get(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder viewHolder; // view lookup cache stored in tag

        if (convertView == null) {

            viewHolder = new ViewHolder();
            LayoutInflater inflater = LayoutInflater.from(getContext());
            convertView = inflater.inflate(R.layout.row_item, parent, false);
            viewHolder.txtName = (TextView) convertView.findViewById(R.id.name);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        viewHolder.txtName.setText(getItem(position));
        // Return the completed view to render on screen
        return convertView;
    }
}

Lớp MainActivity.java được đưa ra dưới đây:

public class MainActivity extends AppCompatActivity {

    TabLayout tabLayout;
    ViewPager viewPager;
    ViewPagerAdapter viewPagerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        viewPager = (ViewPager) findViewById(R.id.viewPager);
        viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(viewPagerAdapter);
        tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(viewPager);
    }

}

Trong đoạn mã trên, setupWithViewPager() được sử dụng để kết nối TabLayout với ViewPager. Phương thức getPageTitle() trong FragmentPagerAdapter được dùng để đặt tiêu đề cho mỗi Tab. Bây giờ, chúng ta hãy xem kết quả đầu ra khi đoạn mã trên được chạy.

android tab layout view pager issue

Câu hỏi: Vì sao ToolBar không cuộn theo scrollFlags đã đặt? Đó là do ListView. CoordinatorLayout không hỗ trợ ListView (nó không phải là một phần của Material Design) và các cử chỉ cuộn của nó. Do đó, nên sử dụng RecyclerView thay thế. Lưu ý: Các Fragment thuộc về một activity của CoordinatorLayout cần sử dụng NestedScrollView hoặc RecyclerView làm thành phần cha để các cử chỉ cuộn hoạt động chính xác. Trước khi chúng ta thay thế việc triển khai ListView trong ứng dụng, hãy bọc bố cục của Fragment hiện tại bằng một NestedScrollView như hiển thị bên dưới: fragment_list.xml

<?xml version="1.0" encoding="utf-8"?>

<android.support.v4.widget.NestedScrollView xmlns:android="<https://schemas.android.com/apk/res/android>"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ListView xmlns:android="<https://schemas.android.com/apk/res/android>"
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</android.support.v4.widget.NestedScrollView>

Hãy xem ứng dụng hoạt động như thế nào:

android tab layout view pager issue

Ồ, lỗi cuộn đã được khắc phục nhưng ListView chỉ hiển thị một hàng. Do đó, ListView không nên được sử dụng với các loại chế độ xem trong Material Design của chúng ta. Bây giờ, hãy cùng sửa lỗi ứng dụng.

Cấu trúc dự án Android TabLayout ViewPager

android tab layout view pager project

Ví dụ về mã lệnh (code) của Android TabLayout ViewPager

Các lớp activity_main.xml, MainActivity.javaViewPagerAdapter.java không thay đổi. Bây giờ chúng ta hãy xem xét các Fragment. Bố cục của các Fragment được đưa ra dưới đây: fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="<https://schemas.android.com/apk/res/android>" />

Các tệp FragmentA(/B/C).java được đưa ra dưới đây:

package com.journaldev.tablayoutviewpager;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentA extends Fragment {

    RecyclerView recyclerView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(
                R.layout.fragment, container, false);
        return rootView;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        String[] items = getResources().getStringArray(R.array.tab_A);
        RecyclerViewAdapter adapter = new RecyclerViewAdapter(items);
        recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);

    }
}

Chúng ta đã chuyển dữ liệu cần hiển thị vào tệp strings.xml. Dữ liệu được định nghĩa ở đó như sau:

<resources>
    <string name="app_name">TabLayoutViewPager</string>
    <string name="action_settings">Settings</string>

    <string-array name="tab_A">
        <item>Item 1A</item>
        <item>Item 1B</item>
    </string-array>

    <string-array name="tab_B">
        <item>Item 2A</item>
    </string-array>
</resources>

Lưu ý: Chúng ta đã tối ưu hóa logic mã lệnh của fragment để nó điền dữ liệu vào adapter và hiển thị nó ngay khi view được tạo. RecyclerViewAdapter.java có một mảng chuỗi làm đối số. Mã lệnh cho nó được đưa ra dưới đây.

public class RecyclerViewAdapter extends RecyclerView.Adapter {

    String[] items;

    public RecyclerViewAdapter(String[] items) {
        this.items = items;
    }

    @Override
    public TextItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view_list_item, parent, false);
        return new TextItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(TextItemViewHolder holder, int position) {
        holder.bind(items[position]);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemCount() {
        return items.length;
    }
}

Trong đoạn mã trên, chúng ta đã thêm một lớp RecyclerViewHolder tùy chỉnh có bố cục tương tự như các mục trong danh sách. Lớp TextItemViewHolder.java được cung cấp dưới đây.

public class TextItemViewHolder extends RecyclerView.ViewHolder {
    private TextView textView;

    public TextItemViewHolder(View itemView) {
        super(itemView);
        textView = (TextView) itemView.findViewById(R.id.list_item);
    }

    public void bind(String text) {
        textView.setText(text);
    }

}

Bố cục cho ViewHolder tùy chỉnh ở trên là: recycler_view_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="<https://schemas.android.com/apk/res/android>"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/list_item"
        android:textSize="18sp"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingRight="8dp"
        android:paddingLeft="8dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <View
        android:id="@+id/separator"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#858585" />
</LinearLayout>

Kết quả của ứng dụng khi hoạt động được đưa ra dưới đây:

android tab layout view pager output

Cấu trúc bố cục giống với ứng dụng WhatsApp. Để làm cho nó tương tự hơn, hãy thực hiện các thay đổi sau:

  1. Nhập và thêm hai biểu tượng menu có thể vẽ được (drawables).
  2. Tạo chúng trong MainActivity.java ở phương thức onCreateOptionsMenu().
  3. Thay đổi màu colorPrimarycolorPrimaryDark thành #00897B#00796B tương ứng.

Để tạo bố cục menu, hãy thêm phương thức sau vào MainActivity.java.

@Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.menu_main, menu);

        return super.onCreateOptionsMenu(menu);
    }

Tệp menu_main.xml trông như thế này:

<menu xmlns:android="<https://schemas.android.com/apk/res/android>"
    xmlns:app="<https://schemas.android.com/apk/res-auto>"
    xmlns:tools="<https://schemas.android.com/tools>"
    tools:context="com.journaldev.tablayoutviewpager.MainActivity">
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_search"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        android:icon="@drawable/search"
        app:showAsAction="ifRoom" />

    <item
        android:id="@+id/action_add"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        android:icon="@drawable/add"
        app:showAsAction="ifRoom" />
</menu>

Sau khi thực hiện những thay đổi trên, bạn sẽ có kết quả tương tự như sau:

android tab layout view pager similar whatsapp

Hướng dẫn này xin được kết thúc tại đây. Bạn có thể tải xuống dự án Android TabLayout ViewPager từ liên kết bên dưới.

Tải xuống dự án Android TabLayout ViewPager

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất