ViewPager 之初解

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 需要实现以下四个方法:

  • instantiateItem(ViewGroup, int)
  • destroyItem(ViewGroup, int, Object)
  • getCount()
  • isViewFromObject(View, Object)

先不要管这四个方法是干什么的,在后面的内容中会详细讲解。

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 的时候,重写了四个方法:

  • Object instantiateItem (ViewGroup container, int position)
  • void destroyItem (ViewGroup container, int position, Object object)
  • int getCount ()
  • boolean isViewFromObject (View view, Object object)

这四个方法是用来干什么的呢?

看一下官方文档:
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。

Copyright© 2020-2022 li-xyz 冀ICP备2022001112号-1