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] 

  • 相关阅读:
    Elementary Methods in Number Theory Exercise 1.2.25
    Elementary Methods in Number Theory Exercise 1.2.14
    图解欧几里德算法
    图解欧几里德算法
    Elementary Methods in Number Theory Exercise 1.2.14
    Android中的长度单位详解(dp、sp、px、in、pt、mm)
    分享下多年积累的对JAVA程序员成长之路的总结
    android异常之都是deamon惹的祸The connection to adb is down, and a severe error has occured.
    TomatoCartv1.1.8.2部署时报错
    JavaScript浏览器对象之二Document对象
  • 原文地址:https://www.cnblogs.com/snser/p/5700754.html
Copyright © 2011-2022 走看看