首页 > 代码库 > 处女男学Android(十一)---Gallery、ViewPager和ViewPager+Fragment实现的Tab导航

处女男学Android(十一)---Gallery、ViewPager和ViewPager+Fragment实现的Tab导航



一、前言



有阵子没更新博客了,主要是最近公司接了个P2P的金融借贷项目没人做,被拉去写服务端,所以迟迟没时间继续学习大安卓,想了想自己的安卓水平和公司的专业安卓璟博比起来依旧差距挺大,于是乎我要加把劲赶上才行,所以继续翻开李刚疯狂讲义系列,看到Gallery这个控件了,大致功能是横向滚动查看列表项,再仔细看了一下居然过时了,官方推荐用ViewPager来替代,还没学就过时了,有点不爽,干脆新的旧的一起学习一下,也好进行一下比较吧。废话不多说,首先是已经过时的Gallery。



二、画廊视图Gallery的简介与应用



先扫一眼源码,不难发现,Gallery继承了AbsSpinner

技术分享

它和Spinner有共同的父类(据说Spinner也不建议使用了,推荐用TextView+PopupWindow去替代),Spinner是一个垂直的下拉列表,而Gallery则是一个水平的横向列表框,同时Gallery允许用户通过拖动去查看上一个或下一个列表项。既然拥有共同父类,那么使用方法上自然也非常相似,同Spinner一样,Gallery也需要一个adapter为其提供列表项。下面直接通过代码来演示一下Gallery的用法。

activity_gallery.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/win_bai"
    android:orientation="vertical" >

    <Gallery
        android:id="@+id/gly_ad_imgs"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:unselectedAlpha="0.6" /> <!-- 没有选中的图片的透明度 -->

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2.0"
        android:orientation="horizontal" >

        <!-- Left -->

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="2dp"
            android:layout_weight="1.0"
            android:gravity="center_horizontal"
            android:orientation="vertical" >

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="3.0"
                android:background="@drawable/ibtn_csyx_selector" />

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="2.0"
                android:background="@drawable/ibtn_xqn_selector" />

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="2.0"
                android:background="@drawable/ibtn_ssp_selector" />
        </LinearLayout>

        <!-- Right -->

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1.0"
            android:gravity="center_horizontal"
            android:orientation="vertical" >

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="4.0"
                android:background="@drawable/ibtn_zbts_selector" />

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="5.0"
                android:background="@drawable/ibtn_lxyj_selector" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>
GalleryActivity:

package com.wl.viewtestdemo.view;

import com.wl.viewtestdemo.util.OtherTools;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;

public class GalleryActivity extends Activity {

	private Gallery adsGallery;
	private int[] imgs = { R.drawable.vp_test_1, R.drawable.vp_test_2,
			R.drawable.vp_test_3, R.drawable.vp_test_4, R.drawable.vp_test_5 };

	private MyAdapter4Gallery adapter;
	private LayoutInflater mInflater;
	private Context context;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_gallery);

		context = this;
		mInflater = LayoutInflater.from(context);
		adsGallery = (Gallery) findViewById(R.id.gly_ad_imgs);
		adapter = new MyAdapter4Gallery();
		adsGallery.setAdapter(adapter);
		// adsGallery.setAnimationDuration(500); 设置图片切换时的动画持续时间
		adsGallery.setSpacing(10); //设置图片之间的间距
		adsGallery.setOnItemSelectedListener(new OnItemSelectedListener() {
			@Override
			public void onItemSelected(AdapterView<?> parent, View view,
					int position, long id) {
				// TODO Auto-generated method stub
				OtherTools.showInfo(context, "这是第" + (position + 1) + "张图片");
			}

			@Override
			public void onNothingSelected(AdapterView<?> parent) {
				// TODO Auto-generated method stub

			}
		});
	}

	class MyAdapter4Gallery extends BaseAdapter {

		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return imgs.length;
		}

		@Override
		public Integer getItem(int position) {
			// TODO Auto-generated method stub
			return imgs[position];
		}

		@Override
		public long getItemId(int position) {
			// TODO Auto-generated method stub
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			// TODO Auto-generated method stub
			View v = mInflater.inflate(R.layout.item_gallery, null);
			ImageView mainImg = (ImageView) v
					.findViewById(R.id.iv_image_item_ad);
			mainImg.setImageResource(imgs[position]);
			return v;
		}

	}
}

很简单吧,全局变量imgs存了5张示例图片,adapter的item只是一个ImageView所以就不贴布局代码了,最后看一下运行效果:

技术分享

简单的说一下我初次使用时感觉不好的地方:

1.Gallery在水平滚动时可以滑过多张图片,一般不符合实际需求,所以需要对图片的滑动和位置做一些特殊处理。

2.Gallery在切换图片时有一个惯性,会造成不流畅的感觉。

3.可自定义的图片间距和切换时的动画时间是否会造成用户体验隐患。


这三点肯定不是使Gallery过时的原因,我只是从初识的角度认为不够方便或者不够好用的地方吧,大致查了一下相关资料,据说Google是因为Gallery有不易解决的缓存缺陷才抛弃它的,不过这些都不重要了,我们有更好的选择去替代它就可以了,下面就具体的看一下ViewPager。



三、ViewPager的简介与应用



首先扫一眼官方文档的Class Overview中的一段话:

技术分享

简单翻译一下,布局管理者允许用户通过左滑到达右边的数据页,提供一个PagerAdapter 的实现类去生成显示View的页面。这里已经说的很清楚了,ViewPager也需要一个适配器来为其提供列表项,这个适配器就是PagerAdapter,所以现在只需知道如何实现它即可,继续看PagerAdapter文档:

技术分享

很明显,实现一个PagerAdapter,需要重写以上的4个方法,下面就分别说明一下这4个方法的写法和用途。


1、getCount()

和所有的adapter都一样,提供列表项的容量大小,一般就是List的size或者数组的length。


2、isViewFromObject(View,Object)

官方文档的原话是这样说得:The method isViewFromObject(View, Object) identifies whether a page View is associated with a given key object.

翻译过来就是“isViewFromObject方法识别了当前页面是否与给定的key object相关联”。

其实这里又引出了一个key的概念,这里我们暂且只需要知道每个滑动的页面都与一个唯一的key一一对应即可。至于返回值,官方文档也说了如何返回:

技术分享

没错,return view==object即可。


3、instantiateItem(ViewGroup,int)

继续看文档:

技术分享

没错,为特定的位置创建页面视图,和我们自定义Adapter的getView()方法的作用类似。我们关注的无非就是在方法中需要做的事情以及方法结束后的返回值,那么继续看文档:

技术分享

已经用红色线条标出来了,我们在方法中需要做的即是将指定position的视图添加到container中,至于返回值,可以是新增的view,亦或是可以代表当前页面的任意值,比如position变量。


4、destroyItem(ViewGroup,int,Object)

老规矩看文档:

技术分享

很简单,从特定的位置移除页面视图。

现在应该对ViewPager的工作过程和原理有一个大致的了解了,个人感觉ViewPager整体是通过一个类似Queue的结构去维护一组View的,滑动的过程中不断的调用instantiateItem和destroyItem,不知道这样的理解是否准确,如果不对还希望大神批评指正,作为初学者我目前还是以应用为主的同时,尽自己的最大努力去理解原理。好了,最后就通过ViewPager来实现一下上面的那个Gallery的图片滑动例子,下面贴出代码。

activity_view_pager.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/win_bai"
    android:orientation="vertical" >

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_ad_imgs"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:background="#000"
        android:padding="2dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2.0"
        android:orientation="horizontal" >

        <!-- Left -->

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="2dp"
            android:layout_weight="1.0"
            android:gravity="center_horizontal"
            android:orientation="vertical" >

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="3.0"
                android:background="@drawable/ibtn_csyx_selector" />

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="2.0"
                android:background="@drawable/ibtn_xqn_selector" />

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="2.0"
                android:background="@drawable/ibtn_ssp_selector" />
        </LinearLayout>

        <!-- Right -->

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1.0"
            android:gravity="center_horizontal"
            android:orientation="vertical" >

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="4.0"
                android:background="@drawable/ibtn_zbts_selector" />

            <ImageButton
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="2dp"
                android:layout_weight="5.0"
                android:background="@drawable/ibtn_lxyj_selector" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>

ViewPagerActivity:

package com.wl.viewtestdemo.view;

import java.util.ArrayList;
import java.util.List;

import com.wl.viewtestdemo.util.OtherTools;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class ViewPagerActivity extends Activity implements OnPageChangeListener {

	private ViewPager mViewPager;
	private List<View> adImgs;
	private MyPagerAdapter adapter;

	private Context context;

	private int[] imgs = { R.drawable.vp_test_1, R.drawable.vp_test_2,
			R.drawable.vp_test_3, R.drawable.vp_test_4, R.drawable.vp_test_5 };

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_view_pager);

		context = this;
		mViewPager = (ViewPager) findViewById(R.id.vp_ad_imgs);
		initAdImgViews();
	}

	private void initAdImgViews() {
		// TODO Auto-generated method stub
		adImgs = new ArrayList<View>();
		for (int i = 0; i < 5; i++) {
			View view = LayoutInflater.from(context).inflate(
					R.layout.item_gallery, null);
			adImgs.add(view);
		}
		adapter = new MyPagerAdapter();
		mViewPager.setAdapter(adapter);
		mViewPager.setOnPageChangeListener(this);
		mViewPager.setCurrentItem(1);
	}

	class MyPagerAdapter extends PagerAdapter {

		@Override
		public void destroyItem(ViewGroup container, int position, Object object) {
			// TODO Auto-generated method stub
			View view = adImgs.get(position);
			container.removeView(view);
			((ImageView) view.findViewById(R.id.iv_image_item_ad))
					.setImageBitmap(null);
		}

		@Override
		public Object instantiateItem(ViewGroup container, int position) {
			// TODO Auto-generated method stub

			View view = adImgs.get(position);
			ImageView adImg = (ImageView) view
					.findViewById(R.id.iv_image_item_ad);
			adImg.setImageResource(imgs[position]);
			container.addView(adImgs.get(position));
			return adImgs.get(position);
		}

		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return adImgs.size();
		}

		@Override
		public boolean isViewFromObject(View arg0, Object arg1) {
			// TODO Auto-generated method stub
			return arg0 == arg1;
		}

	}

	@Override
	public void onPageScrollStateChanged(int arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onPageScrolled(int arg0, float arg1, int arg2) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onPageSelected(int i) {
		// TODO Auto-generated method stub
		OtherTools.showInfo(context, "这是第" + (i + 1) + "张图片");
	}

}

最后看一下运行效果:

技术分享

OK了,在模拟器上可能效果不明显,在真机上运行对比的话应该可以明显的感觉到已经没有Gallery出现的那几点问题了,ViewPager就暂且记录到这里,本来打算结束本篇Blog,但在结束之前还想做一个东西,就是ViewPager+Fragment实现的带滑动的Tab导航,刚好上一篇Blog也在记录Fragment,这样就可以将知识串起来了。



四、ViewPager+Fragment+RadioGroup实现Tab导航



在初学Fragment的时候我就写过一篇Fragment实现Tab导航的blog(处女男学Android(八)---Fragment初体验之实现Tab导航),在这篇blog中主要是通过切换radiobutton来replace指定的Fragment从而实现Tab导航,那么现在学了ViewPager之后就在此基础上加上滑动效果好了,首先看一下官方文档中的ViewPager的Class Overview中的第三段话:

技术分享

简单翻译一下,ViewPager通常与Fragment结合使用,这是一种便捷的方式来提供和管理每个页面的生命周期。已经有一些标准的适配器实现了Fragment和ViewPager的结合使用,并涵盖了最常见的用例。 FragmentPagerAdapter FragmentStatePagerAdapter这两个类就展示了如何通过ViewPager+Fragment去构建一个完整的用户UI。


翻译的很渣,但是我们只需要明白一点,就是结合Fragment的话不能再使用之前的PagerAdapter,而应当使


FragmentPagerAdapter或者FragmentStatePagerAdapter即可。


那么接下来继续看看文档中关于FragmentPagerAdapter的介绍:

技术分享

前面就不翻译了(英语捉急),有兴趣的同学自己看看,关键是我用红线标出的那行,说明了子类需要实现


getItem(int)和getCount()这两个方法就可以工作了。不难理解,同之前都是类似的,count表示Fragment的个数,


而Item则表示指定页的Fragment对象。明确了这些之后,那么最后看一下完整的代码应该差不多就都明白了吧。


activity_fm_vp.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_main_day"
    android:orientation="vertical" >

    <RadioGroup
        android:id="@+id/rg_select_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal" >

        <RadioButton
            android:id="@+id/rb_current_recommend"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:background="@drawable/iv_selectbar_selector"
            android:button="@null"
            android:gravity="center"
            android:text="当前推荐"
            android:textColor="#fff" />

        <RadioButton
            android:id="@+id/rb_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:background="@drawable/iv_selectbar_selector"
            android:button="@null"
            android:gravity="center"
            android:text="景点"
            android:textColor="#fff" />

        <RadioButton
            android:id="@+id/rb_food"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:background="@drawable/iv_selectbar_selector"
            android:button="@null"
            android:gravity="center"
            android:text="美食"
            android:textColor="#fff" />

        <RadioButton
            android:id="@+id/rb_culture"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:background="@drawable/iv_selectbar_selector"
            android:button="@null"
            android:gravity="center"
            android:text="文化"
            android:textColor="#fff" />

        <RadioButton
            android:id="@+id/rb_entertainment"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:background="@drawable/iv_selectbar_selector"
            android:button="@null"
            android:gravity="center"
            android:text="娱乐"
            android:textColor="#fff" />
    </RadioGroup>

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/qian_bai"
        android:padding="2dp" />

</LinearLayout>

FmAndVpActivity:

package com.wl.viewtestdemo.view;

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;

public class FmAndVpActivity extends FragmentActivity implements
		OnCheckedChangeListener {

	private ViewPager mViewPager;
	private FragmentPagerAdapter mAdapter;
	private List<Fragment> mFragments = new ArrayList<Fragment>();
	private RadioGroup rg;
	private RadioButton rbCurrentRecommend;
	private RadioButton rbView;
	private RadioButton rbFood;
	private RadioButton rbCulture;
	private RadioButton rbEntertainment;

	@Override
	protected void onCreate(Bundle arg0) {
		// TODO Auto-generated method stub
		super.onCreate(arg0);
		setContentView(R.layout.activity_fm_vp);

		mViewPager = (ViewPager) findViewById(R.id.vp_pager);
		initView();
		mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {

			@Override
			public int getCount() {
				// TODO Auto-generated method stub
				return mFragments.size();
			}

			@Override
			public Fragment getItem(int i) {
				// TODO Auto-generated method stub
				return mFragments.get(i);
			}
		};

		mViewPager.setAdapter(mAdapter);
		mViewPager.setOnPageChangeListener(new OnPageChangeListener() {

			@Override
			public void onPageSelected(int position) {
				// TODO Auto-generated method stub
				switch (position) {
				case 0:
					rbCurrentRecommend.setChecked(true);
					break;
				case 1:
					rbView.setChecked(true);
					break;
				case 2:
					rbFood.setChecked(true);
					break;
				case 3:
					rbCulture.setChecked(true);
					break;
				case 4:
					rbEntertainment.setChecked(true);
					break;
				}

			}

			@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

			}
		});
	}

	private void initView() {
		rg = (RadioGroup) findViewById(R.id.rg_select_main);
		rg.setOnCheckedChangeListener(this);
		rbCurrentRecommend = (RadioButton) findViewById(R.id.rb_current_recommend);
		rbCurrentRecommend.setChecked(true);
		rbView = (RadioButton) findViewById(R.id.rb_view);
		rbFood = (RadioButton) findViewById(R.id.rb_food);
		rbCulture = (RadioButton) findViewById(R.id.rb_culture);
		rbEntertainment = (RadioButton) findViewById(R.id.rb_entertainment);

		FragmentOne fOne = new FragmentOne();
		FragmentTwo fTwo = new FragmentTwo();
		FragmentThree fThree = new FragmentThree();
		FragmentFour fFour = new FragmentFour();
		FragmentFive fFive = new FragmentFive();
		mFragments.add(fOne);
		mFragments.add(fTwo);
		mFragments.add(fThree);
		mFragments.add(fFour);
		mFragments.add(fFive);
	}

	@Override
	public void onCheckedChanged(RadioGroup group, int checkedId) {
		// TODO Auto-generated method stub
		switch (checkedId) {
		case R.id.rb_current_recommend:
			mViewPager.setCurrentItem(0);
			break;
		case R.id.rb_view:
			mViewPager.setCurrentItem(1);
			break;
		case R.id.rb_food:
			mViewPager.setCurrentItem(2);
			break;
		case R.id.rb_culture:
			mViewPager.setCurrentItem(3);
			break;
		case R.id.rb_entertainment:
			mViewPager.setCurrentItem(4);
			break;
		}

	}

}

最后看一下运行效果:

技术分享



五、总结



到这里本篇blog就算是记录完毕了,下午调了好久Genymotion模拟器终于算是弄好了,以后再也不用担心gif图片大于2M了,哈哈。上篇blog结尾说了不知道后序打算继续学什么,其实我仔细想了一下,觉得还是看到哪、对哪些感兴趣、认为哪些重要就学哪些好了,没必要去刻意按照书本目录或者什么固定顺序去学习安卓,我在初学安卓的时候就问过鸿洋,他告诉我怎么学都可以,只要学了,就会有收获。现在想想也觉得就是这么个道理,只要坚持学习,就那么点东西,征服她只是时间问题而已!继续努力吧,加油小灯!


处女男学Android(十一)---Gallery、ViewPager和ViewPager+Fragment实现的Tab导航