一、概述
ViewPager是android-support-v4中提供的类,它是一个容器类,常用于页面之间的切换。
继上篇文章《ViewPager之引导页》之后,本文主要介绍ViewPager更为通用的实践:ViewPager搭配Fragment实现页面切换。
这种实现方式相对于上篇文章而言,可以更好的支持不同页面各自的复杂逻辑,与此同时,也能够保障页面之间的耦合度尽可能的低。
按照惯例,先晒出效果图:
二、实现思路
首先分析一下不同区域的交互需求:
中间灰色区域除了要支持三套完全不同的逻辑之外,还要支持左右滑动切换。而顶部ActionBar和底部Tab切换都只需要更新状态,无需整体变换,也不需要整体滑动;
因此,中间灰色区域用ViewPager实现,它包含三个Fragment子页面。而顶部ActionBar和底部Tab切换各自是一个Fragment,直接隶属于Activity。
然后解决不同Fragment之间的依赖关系:
五个Fragment之间沟通的唯一纽带就是ViewPager页面的切换,因此,ViewPager页面切换时通知到不同的Fragment即可。
[转载请保留本文地址:http://www.cnblogs.com/snser/p/5700754.html]
三、开始干活
3.1 搭建整体框架
本文的五个Fragment采用三种方式载入:
ViewPager中的三个Fragment自然是通过其Adatper进行载入,顶部的TitleFragment(ActionBar)直接声明在layout布局中,而底部的TabFragment采用动态载入。
这样做的目的是方便后续分析不同载入方式的实际载入实际。
ok,整体的layout布局自然也就成了下面的样子:
viewpager_fragment.xml(activity的布局):
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:background="@color/background_default" 6 tools:context="${relativePackage}.${activityClass}" > 7 8 <fragment 9 android:id="@+id/viewpager_fragment_title" 10 android:name="cc.snser.cnblog5700754.TitleFragment" 11 android:layout_width="match_parent" 12 android:layout_height="wrap_content" 13 android:layout_alignParentTop="true" /> 14 15 <android.support.v4.view.ViewPager 16 android:id="@+id/viewpager_fragment_pager" 17 android:layout_width="match_parent" 18 android:layout_height="match_parent" 19 android:layout_above="@+id/viewpager_fragment_container" 20 android:layout_below="@+id/viewpager_fragment_title" /> 21 22 <FrameLayout 23 android:id="@id/viewpager_fragment_container" 24 android:layout_width="match_parent" 25 android:layout_height="wrap_content" 26 android:layout_alignParentBottom="true" > 27 </FrameLayout> 28 29 </RelativeLayout>
对应的,主界面的逻辑如下:
ViewPagerFragmentActivity.java:
1 public class ViewPagerFragmentActivity extends FragmentActivity { 2 private FragmentManager mManager = getSupportFragmentManager(); 3 4 private TitleFragment mTitleFragment; 5 private TabFragment mTabFragment; 6 private ViewPager mPager; 7 8 private ViewPagerAdapter mAdapter; 9 10 private static final int DEFAULT_PAGE = 1; //默认页面 11 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 Log.d("Snser", "ViewPagerFragmentActivity onCreate"); 15 super.onCreate(savedInstanceState); 16 Log.d("Snser", "ViewPagerFragmentActivity onCreate setContentView"); 17 setContentView(R.layout.viewpager_fragment); 18 Log.d("Snser", "ViewPagerFragmentActivity onCreate initView"); 19 initView(); 20 } 21 22 private void initView() { 23 mTitleFragment = (TitleFragment)mManager.findFragmentById(R.id.viewpager_fragment_title); 24 mTabFragment = new TabFragment(); 25 mTabFragment.setOnTabClickListenser(new ViewPageTabClickListenser()); 26 mManager.beginTransaction().replace(R.id.viewpager_fragment_container, mTabFragment).commit(); 27 mPager = (ViewPager)findViewById(R.id.viewpager_fragment_pager); 28 mPager.setAdapter(mAdapter = new ViewPagerAdapter(mManager)); 29 mPager.setOnPageChangeListener(new ViewPageChangeListener()); 30 setCurrentItem(DEFAULT_PAGE); 31 } 32 33 @Override 34 protected void onResume() { 35 Log.d("Snser", "ViewPagerFragmentActivity onResume"); 36 super.onResume(); 37 } 38 39 private class ViewPageChangeListener implements OnPageChangeListener { 40 @Override 41 public void onPageSelected(int position) { 42 setCurrentItem(position); 43 } 44 45 @Override 46 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 47 } 48 49 @Override 50 public void onPageScrollStateChanged(int state) { 51 } 52 } 53 54 private class ViewPagerAdapter extends FragmentPagerAdapter { 55 private ArrayList<PagerFragment> mFragments = new ArrayList<PagerFragment>(); 56 57 public ViewPagerAdapter(FragmentManager fm) { 58 super(fm); 59 mFragments.add(new ClickFragment()); 60 mFragments.add(new DateFragment()); 61 mFragments.add(new AnimFragment()); 62 } 63 64 @Override 65 public Fragment getItem(int position) { 66 return mFragments.get(position); 67 } 68 69 @Override 70 public int getCount() { 71 return mFragments.size(); 72 } 73 } 74 75 private class ViewPageTabClickListenser implements OnTabClickListenser { 76 @Override 77 public void onTabClick(int tab) { 78 setCurrentItem(tab); 79 } 80 } 81 82 private void setCurrentItem(int item) { 83 if (item == mPager.getCurrentItem()) { 84 //此时是源于initView或onPageSelected的调用 85 notifyPageChangeToFragments(item); 86 } else { 87 //此时是源于initView或onTabClick的调用,后续会自动触发一次onPageSelected 88 mPager.setCurrentItem(item); 89 } 90 } 91 92 private void notifyPageChangeToFragments(int item) { 93 for (int page = 0; page != mAdapter.getCount(); ++page) { 94 final Fragment fragment = mAdapter.getItem(page); 95 if (fragment instanceof PagerFragment) { 96 if (page == item) { 97 ((PagerFragment)fragment).onPageIn(); 98 } else { 99 ((PagerFragment)fragment).onPageOut(); 100 } 101 } 102 } 103 mTitleFragment.setCurrentTab(item); 104 mTabFragment.setCurrentTab(item); 105 } 106 }
可以看到,包含三种完全不同功能的Activity,其主界面代码居然只有区区106行,这甚至比上篇文章《ViewPager之引导页》还要少。
这必须要归功于Fragment的使用。
三个页面的具体逻辑分别由DateFragment、ClickFragment、AnimFragment进行维护,而ViewPager要做的,只是进行Fragment的切换和通知。
具体来说,ViewPagerAdapter负责根据不同的postion取出不同的Fragment。而三个页面Fragment都继承自PagerFragment:
PagerFragment.java:
1 public class PagerFragment extends Fragment{ 2 3 public void onPageIn() { 4 } 5 6 public void onPageOut() { 7 } 8 9 }
亦即在页面切换的时候(不管是来自滑动还是点击tab),activity都会通知三个fragment它们各自是否可见。
同时,在 notifyPageChangeToFragments 方法中,也会通知TitleFragment和TabFragment,它们该切换状态了。
3.2 Fragment的各自实现
拿AnimFragment举例,这个动画Fragment的功能是在其切入(onPageIn)的时候播放淡入的动画,而在其切出(onPageOut)的时候销毁动画。
1 public class AnimFragment extends PagerFragment { 2 private View mViewRoot; 3 private ImageView mImg; 4 5 private Animation mAnim; 6 7 @Override 8 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 9 Log.d("Snser", "AnimFragment onCreateView"); 10 mViewRoot = inflater.inflate(R.layout.viewpager_fragment_anim, container, false); 11 mAnim = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_fade_in); 12 mAnim.setInterpolator(new LinearInterpolator()); 13 initView(mViewRoot); 14 return mViewRoot; 15 } 16 17 private void initView(View root) { 18 mImg = (ImageView)root.findViewById(R.id.viewpager_fragment_anim_img); 19 startAnim(); 20 } 21 22 @Override 23 public void onDestroy() { 24 super.onDestroy(); 25 stopAnim(); 26 } 27 28 @Override 29 public void onPageIn() { 30 super.onPageIn(); 31 startAnim(); 32 } 33 34 @Override 35 public void onPageOut() { 36 super.onPageOut(); 37 stopAnim(); 38 } 39 40 private void startAnim() { 41 if (mImg != null && mAnim != null) { 42 mImg.startAnimation(mAnim); 43 } 44 } 45 46 private void stopAnim() { 47 if (mImg != null && mAnim != null) { 48 mImg.clearAnimation(); 49 } 50 } 51 52 }
DateFragment的作用是在切入的时候刷新当前日期和时间:
DateFragment.java:
1 public class DateFragment extends PagerFragment { 2 3 private View mViewRoot; 4 private TextView mTxtDate; 5 6 private SimpleDateFormat mFormat = new SimpleDateFormat("yyyy/MM/dd\nHH:mm:ss", Locale.CHINA); 7 8 @Override 9 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 10 Log.d("Snser", "DateFragment onCreateView"); 11 mViewRoot = inflater.inflate(R.layout.viewpager_fragment_date, container, false); 12 initView(mViewRoot); 13 return mViewRoot; 14 } 15 16 @Override 17 public void onPageIn() { 18 super.onPageIn(); 19 refreshDate(); 20 } 21 22 private void initView(View root) { 23 mTxtDate = (TextView)root.findViewById(R.id.viewpager_fragment_date_txt); 24 refreshDate(); 25 } 26 27 private void refreshDate() { 28 if (mTxtDate != null) { 29 mTxtDate.setText(mFormat.format(new Date())); 30 } 31 } 32 33 }