转自文章http://www.apkbus.com/android-123455-1-1.html
x
android.support.v4.ViewPager类在 API 4+ Support 支持包中开始为我们提供,它可以让我们有能力左右滑动以'页'的形式展示数据。我们可以通过继承 PagerAdapter 来生成页面形式的视图。介绍具体的使用方式之前先来看下效果 首先需要在layout文件中配置ViewPager View [html] view plaincopy
- <android.support.v4.view.ViewPager
- android:id="@+id/pager"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="50dp" />
其实ViewPager本质上是一个View,继承体系如下,可以发现它的上面就是ViewGroup ,它是有能力add View的
上面提到过使用 PagerAdapter 来生成一个页面视图,也就是一页, PagerAdapter 决定了ViewPager一共有多少页,负责每页的初始化,每页的销毁等工作 [java] view plaincopy
- class MyPagerAdapter extends PagerAdapter{
-
- @Override
- public int getCount() {
- return mViewList .size();
- }
-
- @Override
- public Object instantiateItem(View container, int position) {
- Log. i("INFO", "instantiate item:"+position);
- ((ViewPager) container).addView( mViewList.get(position),0);
- return mViewList .get(position);
- }
-
- @Override
- public void destroyItem(View container, int position, Object object) {
- ((ViewPager) container).removeView( mViewList.get(position));
- }
-
- @Override
- public boolean isViewFromObject(View arg0, Object arg1) {
- return arg0 == arg1;
- }
可以看到ViewPager其实是一个组件容器,可以为它的每页添加一个要显示的View,用于展现数据 [java] view plaincopy
- mLayoutInflater = getLayoutInflater();
-
- //可以按照需求进行动态创建Layout,这里暂用静态的xml layout
- mViewList.add(mLayoutInflater.inflate(R.layout.per_pager1, null));
- mViewList.add(mLayoutInflater.inflate(R.layout.per_pager2, null));
- mViewList.add(mLayoutInflater.inflate(R.layout.per_pager3, null));
-
- ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
- mPagerAdapter = new MyPagerAdapter();
- viewPager.setAdapter(mPagerAdapter);
上面的每一个经过inflate的Layout就代表的是每一页的布局,就像我们平常使用的布局文件一样... 我们经常看到一般在ViewPager下会有一些圆点来指示当前我们浏览到第几页了,要实现这种效果,我们要根据页面的数量来动态生成圆点的数量,并添加到一个LinearLayout中,便于管理 [java] view plaincopy
- Bitmap bitmap = BitmapFactory. decodeResource(getResources(), R.drawable.icon_dot_normal );
- for (int i = 0; i < mViewList.size(); i++) {
- Button bt = new Button(this );
- bt.setLayoutParams( new ViewGroup.LayoutParams(bitmap.getWidth(),bitmap.getHeight()));
- bt.setBackgroundResource(R.drawable. icon_dot_normal );
- mNumLayout .addView(bt);
- }
那我们怎么才能知道当前滑动到第几页了呢 ? 其实我们可以为ViewPager设置一个OnPageChangeListener 页面改变监听器来监听页面的改变,从而得到当前滑动到了第几页 [java] view plaincopy
- viewPager. setOnPageChangeListener( new OnPageChangeListener() {
-
- @Override
- public void onPageSelected( int position) {
-
- if (mPreSelectedBt != null){
- mPreSelectedBt .setBackgroundResource(R.drawable. icon_dot_normal);
- }
-
- Button currentBt = (Button)mNumLayout .getChildAt(position);
- currentBt.setBackgroundResource(R.drawable. home_page_dot_select );
- mPreSelectedBt = currentBt;
-
- //Log.i("INFO", "current item:"+position);
- }
-
- @Override
- public void onPageScrolled( int arg0, float arg1, int arg2) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onPageScrollStateChanged( int arg0) {
- // TODO Auto-generated method stub
-
- }
- });
mPreSelectedBt 只是为了记录当前显示的圆点,为下一次圆点焦点切换做准备,下面看一个完整的实现 [java] view plaincopy
- public class MainActivity extends Activity {
-
- ArrayList<View> mViewList = new ArrayList<View>();
- LayoutInflater mLayoutInflater;
- LinearLayout mNumLayout;
-
- Button mPreSelectedBt;
-
- MyPagerAdapter mPagerAdapter;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- mLayoutInflater = getLayoutInflater();
-
- //可以按照需求进行动态创建Layout,这里暂用静态的xml layout
- mViewList.add(mLayoutInflater.inflate(R.layout.per_pager1, null));
- mViewList.add(mLayoutInflater.inflate(R.layout.per_pager2, null));
- mViewList.add(mLayoutInflater.inflate(R.layout.per_pager3, null));
-
- ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
- mPagerAdapter = new MyPagerAdapter();
- viewPager.setAdapter(mPagerAdapter);
-
- mNumLayout = (LinearLayout) findViewById(R.id.ll_pager_num);
-
- Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_dot_normal);
- for (int i = 0; i < mViewList.size(); i++) {
- Button bt = new Button(this);
- bt.setLayoutParams(new ViewGroup.LayoutParams(bitmap.getWidth(),bitmap.getHeight()));
- bt.setBackgroundResource(R.drawable.icon_dot_normal);
- mNumLayout.addView(bt);
- }
-
- viewPager.setOnPageChangeListener(new OnPageChangeListener() {
-
- @Override
- public void onPageSelected(int position) {
-
- if(mPreSelectedBt != null){
- mPreSelectedBt.setBackgroundResource(R.drawable.icon_dot_normal);
- }
-
- Button currentBt = (Button)mNumLayout.getChildAt(position);
- currentBt.setBackgroundResource(R.drawable.home_page_dot_select);
- mPreSelectedBt = currentBt;
-
- //Log.i("INFO", "current item:"+position);
- }
-
- @Override
- public void onPageScrolled(int arg0, float arg1, int arg2) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onPageScrollStateChanged(int arg0) {
- // TODO Auto-generated method stub
-
- }
- });
-
-
- }
-
- class MyPagerAdapter extends PagerAdapter{
-
- @Override
- public int getCount() {
- return mViewList.size();
- }
-
- @Override
- public Object instantiateItem(View container, int position) {
- Log.i("INFO", "instantiate item:"+position);
- ((ViewPager) container).addView(mViewList.get(position),0);
- return mViewList.get(position);
- }
-
- @Override
- public void destroyItem(View container, int position, Object object) {
- Log.i("INFO", "destroy item:"+position);
- ((ViewPager) container).removeView(mViewList.get(position));
- }
-
- @Override
- public boolean isViewFromObject(View arg0, Object arg1) {
- return arg0 == arg1;
- }
- }
- }
如果你仔细观察打印日志会发现,PagerAdapter的实现方式有点特殊,永远都会存在两个经过初始化的'页'(page>2时),显示当前页时会初始化下一页,会destroy上上页,一轮滑动完成的打印信息如下 [html] view plaincopy
- 04-02 22:39:59.880: I/INFO(27187): instantiate item:0
- 04-02 22:39:59.880: I/INFO(27187): instantiate item:1
- 04-02 22:40:03.890: I/INFO(27187): instantiate item:2
- 04-02 22:40:39.020: I/INFO(27187): destroy item:0
当要显示的页数发生改变时,我们可以通过调用PagerAdapter的notifyDataSetChanged() 来通知数据的改变(必须在UI主线程中通知更新),同时圆点也需要进行更新 [java] view plaincopy
- mViewList.add(mLayoutInflater.inflate(R.layout.per_pager1, null));
- Button bt = new Button(this);
- bt.setLayoutParams(new ViewGroup.LayoutParams(bitmap.getWidth(),bitmap.getHeight()));
- bt.setBackgroundResource(R.drawable.icon_dot_normal);
- mNumLayout.addView(bt);
- mPagerAdapter.notifyDataSetChanged();
以上基本上就是ViewPager基本的使用方式,还是比较简单的,但ViewPager最常使用的方式是结合 Fragment 来一起使用,这种方式可以很方便的来管理每个页面的生命周期,Android也为我们提供了好些种固定的实现好了的Adapters来给ViewPager使用,它们包括FragmentPagerAdapter, FragmentStatePagerAdapter,FragmentPagerAdapter, 和 FragmentStatePagerAdapter ,它们中的每一种都可以编写少量简单的代码就为我们建立一个完整的用户界面,下一节将进行介绍 使用Fragment 来表示一页,显得更加简单和直观,Fragment 本身提供的一些特性可以让我们方便的对每一页进行管理,使用FragmentManager可以根据ID或TAG来查找Fragment , 动态添加、删除、替换,Fragment 可以管理自己的生命周期,像Activity一样提供了一些生命周期回调方法。 让Fragment 成为ViewPager的一页时,FragmentManager会一直保存管理创建好了的Fragment,即使当前不是显示的这一页,Fragment对象也不会被销毁,在后台默默等待重新显示。但如果Fragment不再可见时,它的视图层次会被销毁掉,下次显示时视图会重新创建。 出于使用FragmentPagerAdapter 时,Fragment对象会一直存留在内存中,所以当有大量的显示页时,就不适合用FragmentPagerAdapter 了,FragmentPagerAdapter 适用于只有少数的page情况,像选项卡。这个时候你可以考虑使用FragmentStatePagerAdapter ,当使用FragmentStatePagerAdapter 时,如果Fragment不显示,那么Fragment对象会被销毁,但在回调onDestroy()方法之前会回调onSaveInstanceState(Bundle outState)方法来保存Fragment的状态,下次Fragment显示时通过onCreate(Bundle savedInstanceState)把存储的状态值取出来,FragmentStatePagerAdapter 比较适合页面比较多的情况,像一个页面的ListView 最后一点要注意,当使用FragmentPagerAdapter 时一定要为它的宿主ViewPager设置一个有效的ID ! 下面是一个使用FragmentPagerAdapter 的示例,需要注意的是destroyItem()方法并不是去Destroy Fragment对象 而是Destroy的是Fragment的视图 , 这一点需要理解注意了 [java] view plaincopy
- class MyFragmentPagerAdapter extends FragmentPagerAdapter{
-
- public MyFragmentPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
- @Override
- public Fragment getItem(int position) {
- return MyPageFragment.create(position);
- }
-
- @Override
- public int getCount() {
- return mPagerNum; // 代表页数
- }
-
- @Override
- public void destroyItem(ViewGroup container, int position, Object object) {
- // 这里Destroy的是Fragment的视图层次,并不是Destroy Fragment对象
- super.destroyItem(container, position, object);
- Log.i("INFO", "Destroy Item...");
- }
- }
下面是一个Fragment的示例程序,记得前面说过,使用FragmentPagerAdapter 适配器时,创建好了的Fragment会一直在内存中,不会被销毁,但它的视图层次是会被销毁的,所以onCreate()方法只会被调用一次,而 onCreateView() 方法,每次Fragment从不可见到可见时会被调用,可以看到Fragment有一些生命周期回调方法 onPause()、onDestroy()等等 [java] view plaincopy
- public class MyPageFragment extends Fragment {
-
- public static final String ARG_PAGE = "page_num";
-
- // 当前页
- private int currentPageNum;
-
- public MyPageFragment() {
- }
-
- public static MyPageFragment create(int pagerNum) {
-
- MyPageFragment myPageFrament = new MyPageFragment();
- Bundle arg = new Bundle();
- arg.putInt(ARG_PAGE, pagerNum);
- myPageFrament.setArguments(arg);
-
- return myPageFrament;
-
- }
-
-
- /* (non-Javadoc)
- * @See android.support.v4.app.Fragment#onCreate(android.os.Bundle)
- *
- * Fragment创建的时候调用
- *
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log.i("INFO", "onCreate..");
- currentPageNum = getArguments().getInt(ARG_PAGE);
- }
-
- /* (non-Javadoc)
- * @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
- *
- * Fragment从不可见到可见时调用
- *
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
-
- Log.i("INFO", "onCreateView..");
-
- ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.per_pager1,
- container, false);
-
- switch (currentPageNum) {
- case 0:
- rootView.setBackgroundResource(R.drawable.page1_bg);
- break;
- case 1:
- rootView.setBackgroundResource(R.drawable.page2_bg);
- break;
- case 2:
- rootView.setBackgroundResource(R.drawable.page3_bg);
- break;
- default:
- break;
- }
-
- return rootView;
-
- }
-
- @Override
- public void onPause() {
- super.onPause();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.i("INFO", "MyFragment Destroy...");
- }
-
- }
|