安卓TabLayout和ViewPager
在本教程中,我们将在已经实现的TabLayout下实现一个ViewPager。
安卓TabLayout ViewPager概述
使用ViewPagers可以通过滑动来浏览数据页面。它通常与碎片(fragments)一起使用。让我们将之前教程中的布局进行如下修改,即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.Olivia.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>
在将ViewPager添加到MainActivity之前,让我们先设置它的适配器。
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扩展了FragmentPagerAdapter。它调用了三个Fragments,每个Fragment对应一个页面。每个Fragment都包含一个ListView,如下所示的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"/>
以下是给出的FragmentA(/B/C).java文件。
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;
}
}
以上ListView的CustomAdapter.java类为:
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;
}
}
以下是MainActivity.java类
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);
}
}
在上面的代码中,使用setupWithViewPager()方法将TabLayout与ViewPager连接起来。在FragmentPagerAdapter中使用getPageTitle()方法来设置每个选项卡的标题。让我们看一下当上面的代码运行时的输出。问题:为什么ToolBar没有根据设置的scrollFlags滚动?这是由于ListView引起的。CoordinatorLayout不支持ListView(它不是Material Design的一部分)和它的滚动手势。因此建议使用RecyclerView代替。注意:属于CoordinatorLayout活动的片段需要使用NestedScrollView或RecyclerView作为父布局,以使滚动手势能正常工作。在替换应用程序中的ListView实现之前,让我们将当前片段的布局包装在NestedScrollView中,如下所示。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>
让我们看看应用现在的行为:哎呀,滚动问题已经解决了,但是ListView现在只显示一行。所以,我们不应该在我们的Material Design视图类型中使用ListView。现在让我们修复这个应用程序。
Android TabLayout ViewPager 项目结构
Android TabLayout ViewPager示例代码
如果我们不改变activity_main.xml、MainActivity.java和ViewPagerAdapter.java类,那我们现在来看看片段。片段的布局如下所示,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" />
以下是FragmentA(/B/C).java的代码片段。
package com.Olivia.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);
}
}
我们把要显示的数据转移到了strings.xml文件中,并在那里进行了定义。
<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>
注意:我们已经优化了我们的碎片代码逻辑,使其在视图创建后填充适配器并显示出来。RecyclerViewAdapter.java的参数是一个字符串数组。以下是代码示例。
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;
}
}
在上面的代码中,我们添加了一个自定义的RecyclerViewHolder类,它的布局类似于列表项。下面是TextItemViewHolder.java类。
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);
}
}
以上自定义ViewHolder的布局是: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>
应用程序运行结果如下所示。布局结构类似于WhatsApp应用程序。为了使其更相似,请进行以下更改:
- Import and add the two menu icon drawables
- Inflate them in the MainActivity.java in the onCreateOptionsMenu()
- Change the colorPrimary and colorPrimaryDark to #00897B and #00796B respectively
为了扩大菜单布局,请在MainActivity.java中添加以下方法。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return super.onCreateOptionsMenu(menu);
}
menu_main.xml的样子是这样的:
<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.Olivia.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>
通过进行上述更改,最终你会得到像这样的结果。这标志着本教程的结束。你可以从下方链接下载Android TabLayoutViewPager项目。
下载Android TabLayout ViewPager项目。