ViewPager 的简单使用(一)中简单的使用了 ViewPager ,但是 ViewPager 常常和 Fragment 以前使用,这时需要的适配器为 FragmentPagerAdapter 或者 FragmentStatePagerAdapter。
下面只是一个简单的示例,效果如下(AndroidStudio1.5):
ListPagerAdapter.java :
package com.crazy.viewpagertest;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class ListPagerAdapter extends FragmentPagerAdapter {
// 每个页面显示多少条目
private static final int ITEMS_PER_PAGE = 2;
private List<String> mItems;
public ListPagerAdapter(FragmentManager fm, List<String> items) {
super(fm);
mItems = items;
}
/**
* 这个位置首次需要一个 Fragment 时,该方法才会被调用
*/
@Override
public Fragment getItem(int position) {
int start = position * ITEMS_PER_PAGE;
return ArrayListFragment.newInstance(getPageList(position), start);
}
@Override
public int getCount() {
// 得到的分页总数
int pages = mItems.size() / ITEMS_PER_PAGE;
// 如果列表的大小不能整除页面的大小,就多添加一个页面来显示剩余的值
int excess = mItems.size() % ITEMS_PER_PAGE;
if (excess > 0) {
pages++ ;
}
return pages;
}
/**
* 这个方法会在 getItem() 之后针对新的 Fragment 被调用,而且在超出页数限制部分的
* Fragment 再加回来时,也会调用该方法;要确保这些元素会被更新到列表中
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
ArrayListFragment fragment =
(ArrayListFragment)super.instantiateItem(container, position);
fragment.updateListItems(getPageList(position));
return fragment;
}
/**
* 当 notifyDataSetChanged() 被调用时,该方法也会被框架调用。必须决定如何为新的
* 数据集更改每个 Fragment 。如果某个位置的 Fragment 不在需要,会返回 POSITION_NONE,
* 这样适配器就可以将其删除
*/
@Override
public int getItemPosition(Object object) {
ArrayListFragment fragment = (ArrayListFragment)object;
int position = fragment.getBaseIndex() / ITEMS_PER_PAGE;
if (position >= getCount()) {
// 不在需要这个页面
return POSITION_NONE;
} else {
// 刷新 Fragment 数据显示
fragment.updateListItems(getPageList(position));
return position;
}
}
/**
* 辅助方法,用于获取整个列表的某一部分,然后显示在给定的 Fragment 上。
*/
private List<String> getPageList(int position) {
int start = position * ITEMS_PER_PAGE;
int end = Math.min(start + ITEMS_PER_PAGE, mItems.size());
List<String> itemPage = mItems.subList(start, end);
return itemPage;
}
/**
* 内部自定义 Fragment,它会通过 ListView 中显示列表的一个片段,
* 并提供外部方法来更新列表
*/
public static class ArrayListFragment extends Fragment {
private ArrayList<String> mItems;
private ArrayAdapter<String> mAdapter;
private int mBaseIndex;
// 使用工厂模式创建 Fragment
static ArrayListFragment newInstance(List<String> page,
int baseIndex) {
ArrayListFragment fragment = new ArrayListFragment();
fragment.updateListItems(page);
fragment.setBaseIndex(baseIndex);
return fragment;
}
public ArrayListFragment(){
super();
mItems = new ArrayList<String>();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 为列表条目创建一个新的适配器
mAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, mItems);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
// 构造并返回一个 ListView,为它关联适配器
ListView listView = new ListView(getActivity());
listView.setAdapter(mAdapter);
return listView;
}
/**
* 在全局列表中保存一个索引,记录页面开始的地方
*/
public void setBaseIndex(int index) {
mBaseIndex = index;
}
/**
* 在全局列表中检索索引,可以找到页面开始的地方
*/
public int getBaseIndex(){
return mBaseIndex;
}
public void updateListItems(List<String> items) {
mItems.clear();
for (String piece : items) {
mItems.add(piece);
}
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
}
}
}
FragmentPagerAdapter 帮我们实现了 PagerAdapter 底层的很多功能。不必再实现 instantiateItem() ,destroyItem() 和 isViewFromObject() 方法,只需要重写 getItem() 来为每个页面位置提供相应的 Fragment 。这个例子让每个页面的条目显示数量为 2。在 getItem() 内创建 Fragment 时,会传入列表中的一部分数据,而这些数据是根据索引偏移和之前定义的常量来计算的。分页的数量由 getItem() 方法返回。
虽然 ViewPager 不会跟踪滚动出限定值之外的 Fragment ,但 FragmentManager 会继续跟踪。因此,当之前的 Fragment 回滚时,getItem() 不会被再次调用,因为 Fragment 已经存在了。但是正因为如此,如果一个数据集在这期间发生改变 Fragment 列表数据不会跟着更新。这就是重写 instantiateItem() 的原因。
MainActivity.java :
package com.crazy.viewpagertest;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import java.util.ArrayList;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private ArrayList<String> mListItems;
private ListPagerAdapter mAdapter;
private int count = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建初始数据集
mListItems = new ArrayList<>();
mListItems.add("北京");
mListItems.add("上海");
mListItems.add("广州");
mListItems.add("深圳");
mListItems.add("武汉");
mListItems.add("兰州");
mListItems.add("成都");
// 把数据关联到 ViewPager 上
ViewPager pager = (ViewPager)findViewById(R.id.ViewPager);
mAdapter = new ListPagerAdapter(getSupportFragmentManager(), mListItems);
pager.setAdapter(mAdapter);
}
public void onAddClick(View v) {
// 在列表的末尾添加新的条目
mListItems.add("Crazy boy: " + countData() +"," + new Date());
mAdapter.notifyDataSetChanged();
}
// 该方法是纯粹为了显示时方便观察效果,无其他意义
private int countData(){
return count++;
}
public void onRemoveClick(View v) {
// 从列表中删除一个条目
if (!mListItems.isEmpty()) {
mListItems.remove(0);
}
mAdapter.notifyDataSetChanged();
}
}
content_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.crazy.viewpagertest.MainActivity"
tools:showIn="@layout/activity_main">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加 item"
android:onClick="onAddClick" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="删除 item"
android:onClick="onRemoveClick" />
<android.support.v4.view.ViewPager
android:id="@+id/ViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
</LinearLayout>
ViewPager 中还有几个其他的方法:
1. setPageMargin() 和 setPageMarginDrawable() 允许在页面之间设置一些额外的间隔,并且使用一个 Drawable (可选)来填充间隔的内容。
2. setCurrentItem() 允许以编程的方式设置要显示的页面,并提供一个选项来禁用页面的切换时的滚动画面。
3. OnPageChangeListener 用于将滚动和变更动作通知给应用程序。
----> onPageSelected() 会在显示一个新页面时被调用。
----> 当发生滚动操作时会连续调用 onPageSelected() 。
----> onPageScrollStateChanged() 在 ViewPager 处于以下状态时会被调用:闲置时,用户主动滚动 ViewPager 时,自动滚动对齐到最近的页面时。