Android TabLayout和ViewPager
在本教程中,我们将在我们已经在本教程中实现的TabLayout下实现ViewPager。
Android TabLayout ViewPager概述
ViewPagers用于在数据页面之间滑动。
通常与片段结合使用。
让我们从上一教程中修改布局,如下所示。
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.theitroad.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>
在MainActivity中添加ViewPager之前,我们先设置它的适配器。
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。
它调用三个片段,每个页面一个。
每个片段都具有一个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现在仅显示一行。
因此,ListView不应与我们的"材料设计"视图类型一起使用。
现在修复该应用程序。
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.theitroad.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应用程序的布局结构。
为了使其更相似,请进行以下更改:
- 导入并添加两个菜单图标可绘制对象
- 在
onCreateOptionsMenu()
中的MainActivity.java中对其进行充气 - 将colorPrimary和colorPrimaryDark分别更改为#00897B和#00796B
要增加菜单布局,请在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.theitroad.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>