zoukankan      html  css  js  c++  java
  • ViewPager之Fragment页面切换

    一、概述

    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
    HH: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 }
    View Code

    ClickFragment则搞定了一个多次连续点击“中彩蛋”的效果:

    ClickFragment.java:

     1 public class ClickFragment extends PagerFragment {
     2     private View mViewRoot;
     3     private Button mBtnClick;
     4     
     5     @Override
     6     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     7         Log.d("Snser", "ClickFragment onCreateView");
     8         mViewRoot = inflater.inflate(R.layout.viewpager_fragment_click, container, false);
     9         initView(mViewRoot);
    10         return mViewRoot;
    11     }
    12     
    13     private void initView(View root) {
    14         mBtnClick = (Button)root.findViewById(R.id.viewpager_fragment_click_btn);
    15         mBtnClick.setOnClickListener(new MultiClickListener());
    16     }
    17     
    18     private static class MultiClickListener implements View.OnClickListener {
    19         private int mCount = 0;
    20         private long mLastClickTime = 0;
    21         
    22         private static final long MAX_CLICK_COUNT = 4;
    23         private static final long TIMEOUT_CLICK = 500;
    24         private static final int MSG_TIMEOUT_CLICK = 0x1001;
    25         
    26         private static Handler sHandler = new Handler() {
    27             @Override
    28             public void handleMessage(Message msg) {
    29                 if (msg.what == MSG_TIMEOUT_CLICK && msg.obj instanceof Button) {
    30                     Log.d("Snser", "MultiClickListener 连续点击超时");
    31                     ((Button)msg.obj).setText("点我");
    32                 }
    33             }
    34         };
    35         
    36         @Override
    37         public void onClick(View v) {
    38             if (v instanceof Button) {
    39                 final long time = System.currentTimeMillis();
    40                 if (mCount == 0 || time - mLastClickTime < TIMEOUT_CLICK) {
    41                     //这是连续点击
    42                     Log.d("Snser", "MultiClickListener 连续点击 mCount=" + (mCount + 1));
    43                     ++mCount;
    44                     mLastClickTime = time;
    45                     sHandler.removeMessages(MSG_TIMEOUT_CLICK);
    46                     sHandler.sendMessageDelayed(Message.obtain(sHandler, MSG_TIMEOUT_CLICK, v), TIMEOUT_CLICK);
    47                     if (mCount < MAX_CLICK_COUNT) {
    48                         ((Button)v).setText("点我   +" + mCount);
    49                     } else {
    50                         ((Button)v).setText("死机了!");
    51                     }
    52                 } else {
    53                     //这是新的点击
    54                     Log.d("Snser", "MultiClickListener 新的点击 mCount=0");
    55                     mCount = 0;
    56                     onClick(v);
    57                 }
    58             }
    59         }
    60     }
    61     
    62 }
    View Code

    除了页面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] 

  • 相关阅读:
    【Android】Android消息处理机制
    【Android】Sensor框架HAL层解读
    【Android】Sensor框架Framework层解读
    【流媒体】初识流媒体与流媒体技术
    【Git】Git与GitHub 入门
    【Delphi】基于状态机的串口通信
    【Delphi】SPComm注意事项
    【Android】事件输入系统-代码层次解读
    【Android】事件处理系统
    【Android】窗口机制分析与UI管理系统
  • 原文地址:https://www.cnblogs.com/snser/p/5700754.html
Copyright © 2011-2022 走看看