ViewPager 解析(一)

ViewPager 用途比较广泛,本篇文章将会谈谈 ViewPager 和 PagerAdapter 的相关使用。

最简单的 ViewPager

下面展示一个非常简单的 ViewPager 效果的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class MainActivity extends AppCompatActivity {

private View view1;
private View view2;
private View view3;
private ViewPager mViewPager;
private List<View> mViewList;

private PagerAdapter mAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mViewPager = (ViewPager) findViewById(R.id.viewpager);
LayoutInflater inflater = getLayoutInflater();

view1 = inflater.inflate(R.layout.layout1, null);
view2 = inflater.inflate(R.layout.layout2, null);
view3 = inflater.inflate(R.layout.layout3, null);

mViewList = new ArrayList<>();

mViewList.add(view1);
mViewList.add(view2);
mViewList.add(view3);

mAdapter = new PagerAdapter() {
@Override
public int getCount() {
return mViewList.size();
}

@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mViewList.get(position));
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mViewList.get(position));
return mViewList.get(position);
}
};

mViewPager.setAdapter(mAdapter);

}
}

其中,layout1.xml 的文件布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_light"
android:orientation="vertical">

<TextView
android:layout_centerInParent="true"
android:text="Page 1"
android:textSize="58sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

</RelativeLayout>

layout2.xml 的文件布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light"
android:orientation="vertical">

<TextView
android:layout_centerInParent="true"
android:text="Page 2"
android:textSize="58sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

</RelativeLayout>

layout3.xml 的布局文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:orientation="vertical">

<TextView
android:layout_centerInParent="true"
android:text="Page 3"
android:textSize="58sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>

OK,一个最简单的 ViewPager 效果就完成了。

LayoutInflater技术广泛应用于需要动态添加View的时候,比如在ScrollView和ListView中,经常都可以看到LayoutInflater的身影。

LayoutInflater 不是今天的重点,今天讨论的重点是 PagerAdapter。

PagerAdapter 解析

PagerAdapter 和 ListView、GridView 里面使用到的 Adapter 不同。PagerAdapter 并没有使用回收机制,而是通过回调的方式来表明每次更新界面的操作流程。当然,如果你需要操作复杂的界面, PagerAdapter 也能实现视图回收。

ViewPager 完整的界面更新流程如下:当 PagerAdapter调用了 startUpdate(ViewGourp) 方法时,表明 ViewPager 中的内容将要发生改变。接着就会调用 instantiateItem(ViewGroup, int) 以及/或者 destroyItem(ViewGourp, int, Object) 方法,最后会以调用 finishUpdate(ViewGroup) 作为结束。

上面说到的几个方法里面,总共有三个参数对象,这几个方法里面的参数含义是一样的。ViewGroup 的对象 container 表示 ViewPager 的视图容器。int 类型的参数 position 表示页面视图的顺序位置。而 Object 对象是一个键,用来表示视图对象的引用。

那么什么是呢?用来干嘛?

首先,ViewPager 并不会直接操作视图,而是将每一个页面视图与一个键(key Object)关联起来,通过操作键来获取到引用视图进行操作。这个键是用来跟踪以及唯一标识一个独立的页面视图。当调用了 instantiateItem(ViewGroup, int) 方法创建一个页面视图并将其添加进视图容器 container 之后,需要返回一个对象(Object)作为键,作为该页面视图的引用,一个非常简单的 PagerAdapter 可以选择使用页面视图本身(一个 View)作为一个键,就像开头的那个例子一样,当然你也可以自定义一个键。同时,isViewFromObject(View, Object) 这个方法用于判断当前页面视图 View 是否是与该键(Object)关联的 View,因此该方法的实现方式一般都固定是 return view == object;

以上就是 PagerAdapter 相关的核心内容,结合最开始的例子,希望大家都能够看明白。下面将 PagerAdapter 需要重写的4个方法进行简单说明:

  • destroyItem(ViewGroup container, int position, Object object)

移除指定位置的页面。该 Adapter 负责在 finishUpdate() 方法返回时从容器删除视图。

这个很简单,直接像下面的方法实现一样就行:

1
2
3
4
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mViewList.get(position));
}
  • getCount ()

返回可用视图的数量。

同样很简单,实现方法如下:

1
2
3
4
@Override
public int getCount() {
return mViewList.size();
}
  • instantiateItem (ViewGroup container, int position)

创建指定位置的页面,该 Adapter 负责将视图添加进当前的容器(container),并返回一个键(key),用来关联当前的页面视图 View。

实现的方法一般是:

1
2
3
4
5
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mViewList.get(position));
return mViewList.get(position);
}
  • isViewFromObject(View view, Object object)

判断instantiateItem(ViewGroup, int) 方法所返回来的 Key 与一个页面视图是否是代表的同一个视图(即它俩是否是对应的,对应的表示同一个View)。

一般实现方法如下:

1
2
3
4
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}

自定义 Key

前面说到,一个非常简单的 PagerAdapter 可以选择使用页面视图本身(一个 View)作为一个键。那么很多人会想到(包括我):为什么通常使用视图本身(View 对象)作为键,而不是视图位置(int 类型参数 position)呢?

下面我们就来实现自定义 Key:使用 int 类型的参数作为键。其他的不变,我们只看一下 PagerAdapter 的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 mAdapter = new PagerAdapter() {

@Override
public int getCount() {
return mViewList.size();
}

@Override
public boolean isViewFromObject(View view, Object object) {
return view == mViewList.get((Integer) object);
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mViewList.get(position));
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mViewList.get(position));
return position;
}
};

其实,修改的也只有 isViewFromObject()instantiateItem() 两个方法。在 instantiateItem() 方法中我们返回了当前视图位置 position 作为键,而在 isViewFromObject() 方法中,为了获取该键所引用的 view,需要将 object 对象进行强制转换获取 positon,然后从视图容器集合中取出 view。私以为这样做比较麻烦,还不如原来将 view 作为键来的方便。

如果觉得文章对你有帮助,请我喝杯可乐吧