首页 > 代码库 > 处女男学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导航