ViewPager 是 ViewGroup 的子类,也就是说,它是一个容器类,他可以让我们的数据页面左右滑动。
我们可以通过一个 PagerAdapter 来管理要显示的页面,是不是和 ListView 有点儿类似?
1.在布局中添加 ViewPager:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
</RelativeLayout>
在 Activity 当中就可以通过 findViewById 来获取到这个 ViewPager 了。
2.PagerAdapter
还记不记得 ListView 当中的 BaseAdapter,其实 PagerAdapter 和 BaseAdapter 的作用是一样的,都是数据源和 ViewPager 之间的桥梁。
在 BaseAdapter 当中,通过重写 getView 返回 每个Item 视图,而 ViewPager 是单个 View 的集合,所以稍微有些不同。
我们实现一个 PagerAdapter 需要实现以下四个方法:
先不要管这四个方法是干什么的,在后面的内容中会详细讲解。
public class CustomViewPagerAdapter extends PagerAdapter {
private List<View> mList;
public CustomViewPagerAdapter( List<View> list) {
this.mList = list;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mList.get(position));
return mList.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mList.get(position));
}
}
在 Activity 当中为 ViewPager 设置 Adapter
首先我们需要创建我们要展示的 View:
viewpager_one.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#e1e183">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="one"
android:textSize="50dp" />
</RelativeLayout>
viewpager_two.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#83e18c">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="two"
android:textSize="50dp" />
</RelativeLayout>
viewpager_three.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#83cae1">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="three"
android:textSize="50dp" />
</RelativeLayout>
然后在 Activity 当中创建 Adapter 并设置给 ViewPager:
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
private CustomViewPagerAdapter adapter;
private LayoutInflater inflater;
private List<View> viewList = new ArrayList<View>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inflater = LayoutInflater.from(this);
View view1 = inflater.inflate(R.layout.viewpager_one, null);
View view2 = inflater.inflate(R.layout.viewpager_two, null);
View view3 = inflater.inflate(R.layout.viewpager_three, null);
Button button = view1.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "点击了 View1 的 Button", Toast.LENGTH_SHORT).show();
}
});
Button button2 = view2.findViewById(R.id.button);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "点击了 View2 的 Button", Toast.LENGTH_SHORT).show();
}
});
viewList.add(view1);
viewList.add(view2);
viewList.add(view3);
viewPager = (ViewPager) findViewById(R.id.viewPager);
adapter = new CustomViewPagerAdapter(viewList);
viewPager.setAdapter(adapter);
}
}
这样最简单的 ViewPager 就搞定了:
上面在创建 PagerAdapter 的时候,重写了四个方法:
这四个方法是用来干什么的呢?
看一下官方文档:
int getCount ()
返回可用视图数。自然就是包含我们 View 的 List 的 size 了。
Object instantiateItem (ViewGroup container, int position)
该方法用来创建指定位置(position)的 View 到指定的容器(container)中。
返回值是代表新 View 的 Object 对象【记住这个对象】。
void destroyItem (ViewGroup container, int position, Object object)
该方法作用是从容器(container)中移除指定位置(position)的 View。
boolean isViewFromObject (View view,Object object)
该方法用于确认 instantiateItem 函数所返回来的 Object 与一个 View 是否是代表的同一个视图(即它俩是否是对应的,对应的表示同一个 View)
似乎还是一头雾水。
去源码里看一下 ViewPager 的 addNewItem 方法:
ItemInfo addNewItem(int position, int index) {
ItemInfo ii = new ItemInfo();
ii.position = position;
ii.object = mAdapter.instantiateItem(this, position);
ii.widthFactor = mAdapter.getPageWidth(position);
if (index < 0 || index >= mItems.size()) {
mItems.add(ii);
} else {
mItems.add(index, ii);
}
return ii;
}
方法很短,终点是 ItemInfo
对象:
static class ItemInfo {
Object object;
int position;
boolean scrolling;
float widthFactor;
float offset;
}
静态类,该对象中的 object 存储了 instantiateItem 方法返回的对象。
在看 infoForChild 方法,这个方法中调用 isViewFromObject 方法:
ItemInfo infoForChild(View child) {
for (int i = 0; i < mItems.size(); i++) {
ItemInfo ii = mItems.get(i);
if (mAdapter.isViewFromObject(child, ii.object)) {
return ii;
}
}
return null;
}
可以看到,在这里方法里,会将传入的 View 和 instantiateItem 方法的返回值作为参数调用 isViewFromObject 方法。
也就是说,ViewPager 中每个 View 的信息都存储在一个 ItemInfo 对象当中,所有 View 存储在一个 List<ItemInfo>
当中,当 View 要展示或者发生改变的时候,需要根据 View 的信息来做出响应的调整,这个时候遍历整个 List,通过 isViewFromObject 找到合适的 Item。