Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีใช้ ViewPager2 กับ TabLayout บน Android (Modern Approach)

ภาพรวม

ViewPager2 เป็นเวอร์ชันปรับปรุงจาก ViewPager (เดิม) รองรับ RecyclerView.Adapter, Vertical scrolling, RTL, และทำงานร่วมกับ TabLayout ได้อย่างสมบูรณ์ บทความนี้สาธิตวิธีทันสมัย (Modern) แทน ViewPager เดิมที่ Deprecated แล้ว

Dependency (build.gradle)

dependencies {
    implementation 'androidx.viewpager2:viewpager2:1.0.0'
    implementation 'com.google.android.material:material:1.11.0'
    implementation 'androidx.fragment:fragment:1.6.2'
}

ขั้นตอนการสร้าง

1. Layout (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar">

        <androidx.appcompat.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/ThemeOverlay.MaterialComponents.Light" />

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="fixed"
            app:tabGravity="fill" />

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

2. Fragment Layout (res/layout/fragment_page.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tvPageTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

3. PageFragment.java

package com.example.myapplication;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class PageFragment extends Fragment {

    private static final String ARG_TITLE = "page_title";

    public static PageFragment newInstance(String title) {
        PageFragment fragment = new PageFragment();
        Bundle args = new Bundle();
        args.putString(ARG_TITLE, title);
        fragment.setArguments(args);
        return fragment;
    }

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

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        String title = getArguments() != null ? getArguments().getString(ARG_TITLE) : "Page";
        TextView tv = view.findViewById(R.id.tvPageTitle);
        tv.setText(title);
    }
}

4. ViewPagerAdapter.java

package com.example.myapplication;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;

public class ViewPagerAdapter extends FragmentStateAdapter {

    private final String[] titles;

    public ViewPagerAdapter(@NonNull FragmentActivity fa, String[] titles) {
        super(fa);
        this.titles = titles;
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return PageFragment.newInstance(titles[position]);
    }

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

5. MainActivity.java

package com.example.myapplication;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;

public class MainActivity extends AppCompatActivity {

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

        // Toolbar
        androidx.appcompat.widget.Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // ViewPager2 + Adapter
        ViewPager2 viewPager = findViewById(R.id.viewPager);
        String[] titles = {"หน้าแรก", "ค้นหา", "โปรไฟล์", "ตั้งค่า"};
        viewPager.setAdapter(new ViewPagerAdapter(this, titles));

        // TabLayout + Mediator (เชื่อม TabLayout กับ ViewPager2 อัตโนมัติ)
        TabLayout tabLayout = findViewById(R.id.tabLayout);
        new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
            tab.setText(titles[position]);
        }).attach();
    }
}

คุณสมบัติเด่นของ ViewPager2

คุณสมบัติViewPager (เดิม)ViewPager2
Adapter BasePagerAdapterRecyclerView.Adapter (FragmentStateAdapter)
Vertical Scrollingไม่รองรับรองรับ (android:orientation="vertical")
RTL Supportจำกัดรองรับเต็มรูปแบบ
notifyDataSetChangedReload ทั้งหมดDiffUtil / notifyItemChanged (Granular)
Fragment Lifecycleซับซ้อน (setUserVisibleHint)ชัดเจน (Lifecycle แบบมาตรฐาน)
Fake Dragมีมี (fakeDragBy)

การทำงานกับ TabLayoutMediator

TabLayoutMediator ทำหน้าที่ซิงค์ TabLayout กับ ViewPager2 อัตโนมัติ:

  • สร้าง Tab ให้ตรงกับจำนวนหน้า (getItemCount)
  • อัปเดต Tab เมื่อ Swipe หรือ กด Tab
  • รองรับ Badge, Icon, Custom View ได้
// ตัวอย่างเพิ่ม Badge
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
    tab.setText(titles[position]);
    if (position == 1) {
        tab.getOrCreateBadge().setNumber(5); // Badge ตัวเลข
    }
}).attach();

สรุป

ViewPager2 + TabLayout + TabLayoutMediator เป็น **มาตรฐานปัจจุบัน** สำหรับ Swipeable Tabs ใน Android ใช้ FragmentStateAdapter (ยึดตาม RecyclerView) ประสิทธิภาพดี รองรับ Vertical/RTL และ Lifecycle ชัดเจน ควรย้ายจาก ViewPager เดิมให้เร็วที่สุด