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
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.
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:
Ồ, 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
Ví dụ về mã lệnh (code) của Android TabLayout ViewPager
Các lớp activity_main.xml
, MainActivity.java
và ViewPagerAdapter.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:
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:
- Nhập và thêm hai biểu tượng menu có thể vẽ được (drawables).
- Tạo chúng trong
MainActivity.java
ở phương thứconCreateOptionsMenu()
. - Thay đổi màu
colorPrimary
vàcolorPrimaryDark
thành#00897B
và#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:
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.