編輯:關於android開發
一、概述
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:

ClickFragment則搞定了一個多次連續點擊“中彩蛋”的效果:
ClickFragment.java:

除了頁面Fragment之外,TabFragment和TitleFragment都會在 setCurrentTab 方法中更新自己的狀態。
不同的是,TabFragment會將點擊tab的事件通過 OnTabClickListenser 反饋給activity:
1 public class TabFragment extends Fragment {
2 private View mRootView;
3
4 private TextView mTxtTabApp;
5 private TextView mTxtTabHome;
6 private TextView mTxtTabUser;
7
8 private OnTabClickInternalListener mTabClickInternalListener = new OnTabClickInternalListener();
9 private OnTabClickListenser mOnTabClickListenser;
10
11 private int mDefaultTab = 0;
12 private int mCurrentTab = -1;
13
14 @Override
15 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
16 Log.d("Snser", "TabFragment onCreateView");
17 mRootView = inflater.inflate(R.layout.viewpager_fragment_tab, container, false);
18 initView(mRootView);
19 return mRootView;
20 }
21
22 private void initView(View root) {
23 mTxtTabApp = (TextView)root.findViewById(R.id.viewpager_fragment_tab_app);
24 mTxtTabApp.setTag(Integer.valueOf(0));
25 mTxtTabApp.setOnClickListener(mTabClickInternalListener);
26 mTxtTabHome = (TextView)root.findViewById(R.id.viewpager_fragment_tab_home);
27 mTxtTabHome.setTag(Integer.valueOf(1));
28 mTxtTabHome.setOnClickListener(mTabClickInternalListener);
29 mTxtTabUser = (TextView)root.findViewById(R.id.viewpager_fragment_tab_user);
30 mTxtTabUser.setTag(Integer.valueOf(2));
31 mTxtTabUser.setOnClickListener(mTabClickInternalListener);
32 setCurrentTab(mDefaultTab);
33 }
34
35 public interface OnTabClickListenser {
36 public void onTabClick(int tab);
37 }
38
39 private class OnTabClickInternalListener implements View.OnClickListener {
40 @Override
41 public void onClick(View v) {
42 if (v != null && v.getTag() instanceof Integer) {
43 final int tab = (Integer)v.getTag();
44 if (tab != mCurrentTab && mOnTabClickListenser != null) {
45 setCurrentTab(tab);
46 mOnTabClickListenser.onTabClick(tab);
47 }
48 }
49 }
50 }
51
52 public void setOnTabClickListenser(OnTabClickListenser listenser) {
53 mOnTabClickListenser = listenser;
54 }
55
56 public void setCurrentTab(int tab) {
57 if (mTxtTabApp != null && mTxtTabHome != null && mTxtTabUser != null) {
58 mCurrentTab = tab;
59 mTxtTabApp.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_normal));
60 mTxtTabHome.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_normal));
61 mTxtTabUser.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_normal));
62 switch (tab) {
63 case 0:
64 mTxtTabApp.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_current));
65 break;
66 case 1:
67 mTxtTabHome.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_current));
68 break;
69 case 2:
70 mTxtTabUser.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_current));
71 break;
72 default:
73 break;
74 }
75 } else {
76 //TabFragment在Activity的onResume之後才會onCreateView
77 //setCurrentTab的時候控件還沒初始化,存一下初始值在initView裡再初始化
78 mDefaultTab = tab;
79 }
80 }
81 }
1 public class TitleFragment extends Fragment {
2 private View mRootView;
3 private TextView mTxt;
4
5 @Override
6 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
7 Log.d("Snser", "TitleFragment onCreateView");
8 mRootView = inflater.inflate(R.layout.viewpager_fragment_title, container, false);
9 initView(mRootView);
10 return mRootView;
11 }
12
13 private void initView(View root) {
14 mTxt = (TextView)root.findViewById(R.id.viewpager_fragment_title_txt);
15 }
16
17 public void setCurrentTab(int tab) {
18 if (mTxt != null) {
19 switch (tab) {
20 case 0:
21 mTxt.setText("#ClickFragment");
22 break;
23 case 1:
24 mTxt.setText("#DateFragment");
25 break;
26 case 2:
27 mTxt.setText("#AnimFragment");
28 break;
29 default:
30 break;
31 }
32 }
33 }
34 }
3.3 Fragment的加載順序
回歸到3.1節中介紹到本文采用三種方式進行Fragment的載入,目的就是為了比較不同方式下Fragment的加載時機。
在 DEFAULT_PAGE = 1 時,各個Fragment的加載順序為:
// ViewPagerFragmentActivity onCreate // ViewPagerFragmentActivity onCreate setContentView // TitleFragment onCreateView (聲明在layout布局中的Fragment在setContentView之後就會被加載) // ViewPagerFragmentActivity onCreate initView // TabFragment onCreateView (動態replace的Fragment在replace之後、onResume之前被加載) // ViewPagerFragmentActivity onResume // DateFragment onCreateView (ViewPager最先加載默認頁) // ClickFragment onCreateView (ViewPager默認頁左邊的頁面會被預加載) // AnimFragment onCreateView (ViewPager默認頁右邊的頁面會被預加載)
在 DEFAULT_PAGE = 0 時,各個Fragment的加載順序為:
// ViewPagerFragmentActivity onCreate // ViewPagerFragmentActivity onCreate setContentView // TitleFragment onCreateView (聲明在layout布局中的Fragment在setContentView之後就會被加載) // ViewPagerFragmentActivity onCreate initView // TabFragment onCreateView (動態replace的Fragment在replace之後、onResume之前被加載) // ViewPagerFragmentActivity onResume // ClickFragment onCreateView (ViewPager最先加載默認頁) // DateFragment onCreateView(ViewPager默認頁右邊的頁面會被預加載) // AnimFragment onCreateView(ViewPager切換到DateFragment時才會預加載AnimFragment)
[轉載請保留本文地址:http://www.cnblogs.com/snser/p/5700754.html]
四、demo工程
先曬一下本文的動圖效果:

工程源碼如下(下載下面的圖片,擴展名改成zip即可):

[轉載請保留本文地址:http://www.cnblogs.com/snser/p/5700754.html]
集成websocket即時通訊 java聊天源碼 代碼下載 java後台框架源碼 websocket源碼 IM,websocket即時通訊
集成websocket即時通訊 java聊天源碼 代碼下載 java後台框架源碼 websocket源碼 IM,websocket即時通訊獲取【下載地址】 &
Android百度地圖API集成一《基礎地圖》,集成百度地圖api
Android百度地圖API集成一《基礎地圖》,集成百度地圖api 在使用百度地圖API集成百度地圖時,碰到了一些坑,特開此貼記錄。。。 直接開始代碼 1.新建
Android學習之旅:五子棋,android之旅五子棋
Android學習之旅:五子棋,android之旅五子棋 在學完了Android的基礎之後,我開始嘗試著寫一些小項目練練手,同時進一步鞏固自己的基礎知識,而我選的的第一
Kotlin中功能操作與集合(KAD 11),
Kotlin中功能操作與集合(KAD 11),作者:Antonio Leiva 時間:Feb 2, 2017 原文鏈接:https://antonioleiva.com/