Android之ActionBar、Tabs、Fragment、ViewPager实现标签页切换并缓存页面

感觉 Android 到处都是坑,每个地方都要把人折腾半天。

今天来简单说说 Android之ActionBar、Tabs、Fragment、ViewPager 实现标签页切换并缓存页面

关于他们的介绍就不多说了,网上到处都是,只说关键的部分:

我在开发的时候遇到几个疑难问题,花费大量时间处理,总结如下:

1. 关于 Fragment 内部逻辑处理该写在哪个事件回调部分?

2. ViewPager 页面切换动画卡顿,让我头疼了很久。

3. ViewPager 中如何保存 Fragment 当前视图的状态,让 Tabs 页面切换后不会重新加载,这地方很坑爹

4. ActionBar 中的 tab 很多时如何滚动显示


解答:

一、Fragment 的事件回调:

  1. package com.ai9475.meitian.ui.fragment;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup;  
  8. import android.widget.ListView;  
  9.   
  10. import com.ai9475.meitian.R;  
  11. import com.ai9475.meitian.view.DiaryList;  
  12. import com.ai9475.util.ZLog;  
  13.   
  14. /** 
  15.  * 
  16.  * Created by ZHOUZ on 14-1-21. 
  17.  */  
  18. public class DiaryListFragment extends BaseFragment  
  19. {  
  20.     private static final String TAG = "DiaryListFragment";  
  21.   
  22.     @Override  
  23.     public void onAttach(Activity activity)  
  24.     {  
  25.         ZLog.i(TAG, "onAttach");  
  26.         super.onAttach(activity);  
  27.     }  
  28.   
  29.     @Override  
  30.     public void onCreate(Bundle savedInstanceState)  
  31.     {  
  32.         ZLog.i(TAG, "onCreate");  
  33.         super.onCreate(savedInstanceState);  
  34.     }  
  35.   
  36.     @Override  
  37.     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)  
  38.     {  
  39.         ZLog.i(TAG, "onCreateView");  
  40.         return inflater.inflate(R.layout.fragment_diary_list, container, false);  
  41.     }  
  42.   
  43.     @Override  
  44.     public void onActivityCreated(Bundle savedInstanceState)  
  45.     {  
  46.         ZLog.i(TAG, "onActivityCreated");  
  47.         super.onActivityCreated(savedInstanceState);  
  48.         ZLog.i(TAG, "DiaryList0");  
  49.         DiaryList diaryList = new DiaryList(  
  50.                 getActivity().getApplicationContext(),  
  51.                 (ListView) getView().findViewById(R.id.diaryListCt)  
  52.   
  53.         );  
  54.         ZLog.i(TAG, "DiaryList load0");  
  55.         diaryList.load("http://m.ai9475.com/?con=meitian_app");  
  56.         this.setRetainInstance(true);  
  57.     }  
  58.   
  59.     @Override  
  60.     public void onStart()  
  61.     {  
  62.         ZLog.i(TAG, "onStart");  
  63.         super.onStart();  
  64.     }  
  65.   
  66.     @Override  
  67.     public void onResume()  
  68.     {  
  69.         ZLog.i(TAG, "onResume");  
  70.         super.onResume();  
  71.     }  
  72.   
  73.     @Override  
  74.     public void onPause()  
  75.     {  
  76.         ZLog.i(TAG, "onPause");  
  77.         super.onPause();  
  78.     }  
  79.   
  80.     @Override  
  81.     public void onStop()  
  82.     {  
  83.         ZLog.i(TAG, "onStop");  
  84.         super.onStop();  
  85.     }  
  86.   
  87.     @Override  
  88.     public void onDestroyView()  
  89.     {  
  90.         ZLog.i(TAG, "onDestroyView");  
  91.         super.onDestroyView();  
  92.     }  
  93.   
  94.     @Override  
  95.     public void onDestroy()  
  96.     {  
  97.         ZLog.i(TAG, "onDestroy");  
  98.         super.onDestroy();  
  99.     }  
  100.   
  101.     @Override  
  102.     public void onDetach()  
  103.     {  
  104.         ZLog.i(TAG, "onDetach");  
  105.         super.onDetach();  
  106.     }  
  107. }  


上面的类中的 on 事件就是Fragment主要处理的时间回调,注意复写父类方法时要回调执行父类同名方法,否则会出错

主要复写 onCreateView 方法,返回该 Fragment 所对应的视图对象,这里可以在返回视图对象前进行一些简单的配置,但千万不要写太耗时的处理阻塞UI主线程。

另外 onActivityCreated 方法,是当 activity 的 onCreate 事件结束时的回调,此时当前的Fragment对应的view已经并入到整个布局中,此时可以使用 getView() 方法获取视图对象。

其他几个事件没什么太多可说,有些我也还不是太清楚,还有些动画调用的事件。


二、切换页面卡顿问题

这个问题的产生主要可能是两方面,

1. 没有使用 ViewPager 的缓存,每次切换都重新加载。

2. 加载 Fragment 内部有耗时耗资源的逻辑处理。

这里主要说下第二种情况,我一开始没处理掉 缓存问题时有一个解决办法,

  1. <android.support.v4.view.ViewPager  
  2.     android:id="@+id/tabsViewPager"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     >  
  6. </android.support.v4.view.ViewPager>  

 

  1. mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);  
  2. mViewPager.setOnPageChangeListener(  
  3.                 new ViewPager.SimpleOnPageChangeListener() {  
  4.                     private static final String TAG = "ViewPager.SimpleOnPageChangeListener";  
  5.                     private ArrayList hasLoadedPages = new ArrayList<Integer>();  
  6.                     @Override  
  7.                     public void onPageSelected(int position) {  
  8.                         ZLog.i(TAG, "onPageSelected position:"+ position);  
  9.                         getSupportActionBar().setSelectedNavigationItem(position);  
  10.                     }  
  11.   
  12.                     @Override  
  13.                     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)  
  14.                     {  
  15.                         ZLog.i(TAG, "onPageScrolled position: "+ position +", positionOffset:"+ positionOffset +", positionOffsetPixels:"+ positionOffsetPixels);  
  16.                     }  
  17.   
  18.                     @Override  
  19.                     public void onPageScrollStateChanged(int state) {  
  20.                         ZLog.i(TAG, "onPageScrollStateChanged");  
  21.                         int position = mViewPager.getCurrentItem();  
  22.   
  23.                         switch (state) {  
  24.                             // 正在拖动  
  25.                             case ViewPager.SCROLL_STATE_DRAGGING :  
  26.                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:"+ position);  
  27.                                 break;  
  28.                             // 拖动释放后正在沉降的过程  
  29.                             case ViewPager.SCROLL_STATE_SETTLING :  
  30.                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:"+ position);  
  31.                                 break;  
  32.                             // 切换动画全部完成结束  
  33.                             case ViewPager.SCROLL_STATE_IDLE :  
  34.                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:"+ position);  
  35.                 // 已加载过则不再加载  
  36.                                 if (hasLoadedPages.contains(position)) break;  
  37.                                 Fragment fragment = mPager.getFragments().get(position);  
  38.                                 runCallback(position, fragment);  
  39.                                 hasLoadedPages.add(position);  
  40.                                 break;  
  41.                         }  
  42.                     }  
  43.   
  44.                     public void runCallback(int position, Fragment fragment) {  
  45.                         ZLog.i(TAG, "runCallback");  
  46.                         DiaryList diaryList;  
  47.                         switch (position) {  
  48.                             case 0 :  
  49.                                 ZLog.i(TAG, "DiaryList0");  
  50.                                 diaryList = new DiaryList(  
  51.                                         getApplicationContext(),  
  52.                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)  
  53.   
  54.                                 );  
  55.                                 ZLog.i(TAG, "DiaryList load0");  
  56.                                 diaryList.load("http://m.ai9475.com/?con=meitian_app");  
  57.                                 break;  
  58.                             case 1:  
  59.                                 ZLog.i(TAG, "DiaryList1");  
  60.                                 diaryList = new DiaryList(  
  61.                                         getApplicationContext(),  
  62.                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)  
  63.   
  64.                                 );  
  65.                                 ZLog.i(TAG, "DiaryList load1");  
  66.                                 diaryList.load("http://m.ai9475.com/?con=meitian_app&act=hot");  
  67.                                 break;  
  68.                             case 2:  
  69.                                 ZLog.i(TAG, "DiaryList2");  
  70.                                 diaryList = new DiaryList(  
  71.                                         getApplicationContext(),  
  72.                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)  
  73.   
  74.                                 );  
  75.                                 ZLog.i(TAG, "DiaryList load2");  
  76.                                 diaryList.load("http://m.ai9475.com/?con=meitian_app");  
  77.                                 break;  
  78.                         }  
  79.                     }  
  80.                 }  


这里主要用到 public void onPageScrollStateChanged(int state) 页面滚动切换状态变化的事件监听

当滚动动画完全结束 case ViewPager.SCROLL_STATE_IDLE  时再执行 Fragment 的逻辑处理,这样动画就会流畅了。

但经过后来的测试发现有个很简单的解决方案,就是下面要说到的 ViewPager 的缓存功能,其实很简单。


三、缓存 Tabs 页面切换不重新加载数据

我在这地方折腾的最久,而且很多时候无从下手的感觉,网上搜索了很多文章都有说道保存 Fragment 数据和状态,但是没有整整提到如何来保存他的当前view状态,不知道如何保存,当然实际上我还是没搞懂如何仅仅缓存 Fragment 的状态,但对于 ViewPager 缓存 Tab 对应的 Fragment 还是找到了办法,之前花了很大功夫来自己实现,后来偶然发现他居然有个自带的方法

[plain] view plain copy 在CODE上查看代码片派生到我的代码片
  1. // 设置缓存多少个 Tab对应的 fragment  
  2. mViewPager.setOffscreenPageLimit(6);  


我测试时用了 6个 listView 加载图片列表数据,切换动画也没有任何卡顿现象,非常流畅,就这么简单一句就搞定了。

配置该项后,ViewPager在切换时将不会清理不可见的 Fragment,不会触发 Fragment 的任何事件,因此也就不会导致其重新加载。


四、ActionBar 中的 Tabs 

这个其实不用操作,在tabs数量超过一屏后,例如我现在设置6个宽度超过了屏幕则会变成可以横向滚动的状态,而不需要自己实现,之前不知道在这方面查了很多资料都无果,只知道可以用 tabhost 可以实现,但在ActionBar 里面却又用不了,自己试了下方多个tab才发现原来会自动实现,无语。




还是贴上 MainActivity.class 完整代码:

    1. package com.ai9475.meitian.ui;  
    2.   
    3. import android.os.Bundle;  
    4. import android.support.v4.app.Fragment;  
    5. import android.support.v4.app.FragmentManager;  
    6. import android.support.v4.app.FragmentPagerAdapter;  
    7. import android.support.v4.app.FragmentStatePagerAdapter;  
    8. import android.support.v4.app.FragmentTransaction;  
    9. import android.support.v4.view.ViewPager;  
    10. import android.support.v7.app.ActionBar;  
    11. import android.view.Menu;  
    12. import android.widget.ListView;  
    13.   
    14. import com.ai9475.meitian.AppManager;  
    15. import com.ai9475.meitian.R;  
    16. import com.ai9475.meitian.ui.fragment.DiaryListFragment;  
    17. import com.ai9475.meitian.ui.fragment.Test2Fragment;  
    18. import com.ai9475.meitian.ui.fragment.Test3Fragment;  
    19. import com.ai9475.meitian.view.DiaryList;  
    20. import com.ai9475.util.ZLog;  
    21.   
    22. import java.util.ArrayList;  
    23.   
    24. public class MainActivity extends BaseActivity  
    25. {  
    26.     private static final String TAG = "MainActivity";  
    27.     private MyTabsPagerAdapter mPager;  
    28.     private ViewPager mViewPager;  
    29.   
    30.     @Override  
    31.     protected void onCreate(Bundle savedInstanceState)  
    32.     {  
    33.         ZLog.i(TAG, "start");  
    34.         // 执行父级初始化方法  
    35.         super.onCreate(savedInstanceState);  
    36.         //requestWindowFeature(Window.FEATURE_NO_TITLE);  
    37.         ZLog.i(TAG, "setContentView");  
    38.         setContentView(R.layout.activity_main);  
    39.   
    40.         // 滑动页面视图配置  
    41.         ZLog.i(TAG, "MyTabsPagerAdapter start");  
    42.         mPager = new MyTabsPagerAdapter(getSupportFragmentManager());  
    43.         mPager.getFragments().add(new DiaryListFragment());  
    44.         mPager.getFragments().add(new Test2Fragment());  
    45.         mPager.getFragments().add(new Test3Fragment());  
    46.         mPager.getFragments().add(new Test3Fragment());  
    47.         mPager.getFragments().add(new Test3Fragment());  
    48.         mPager.getFragments().add(new Test3Fragment());  
    49.         // 滑动分页容器  
    50.         mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);  
    51.         // 设置缓存多少个 fragment  
    52.         mViewPager.setOffscreenPageLimit(6);  
    53.         mViewPager.setAdapter(mPager);  
    54.         // 页面滑动事件  
    55.         mViewPager.setOnPageChangeListener(  
    56.                 new ViewPager.SimpleOnPageChangeListener() {  
    57.                     private static final String TAG = "ViewPager.SimpleOnPageChangeListener";  
    58.                     private ArrayList hasLoadedPages = new ArrayList<Integer>();  
    59.                     @Override  
    60.                     public void onPageSelected(int position) {  
    61.                         ZLog.i(TAG, "onPageSelected position:"+ position);  
    62.                         getSupportActionBar().setSelectedNavigationItem(position);  
    63.                     }  
    64.   
    65.                     @Override  
    66.                     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)  
    67.                     {  
    68.                         ZLog.i(TAG, "onPageScrolled position: "+ position +", positionOffset:"+ positionOffset +", positionOffsetPixels:"+ positionOffsetPixels);  
    69.                     }  
    70.   
    71.                     @Override  
    72.                     public void onPageScrollStateChanged(int state) {  
    73.                         ZLog.i(TAG, "onPageScrollStateChanged");  
    74.                         int position = mViewPager.getCurrentItem();  
    75.   
    76.                         switch (state) {  
    77.                             // 正在拖动  
    78.                             case ViewPager.SCROLL_STATE_DRAGGING :  
    79.                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:"+ position);  
    80.                                 break;  
    81.                             // 拖动释放后正在沉降的过程  
    82.                             case ViewPager.SCROLL_STATE_SETTLING :  
    83.                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:"+ position);  
    84.                                 break;  
    85.                             // 切换动画全部完成结束  
    86.                             case ViewPager.SCROLL_STATE_IDLE :  
    87.                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:"+ position);  
    88.                                 /*if (hasLoadedPages.contains(position)) break; 
    89.                                 Fragment fragment = mPager.getFragments().get(position); 
    90.                                 runCallback(position, fragment); 
    91.                                 hasLoadedPages.add(position);*/  
    92.                                 break;  
    93.                         }  
    94.                     }  
    95.   
    96.                     public void runCallback(int position, Fragment fragment) {  
    97.                         ZLog.i(TAG, "runCallback");  
    98.                         DiaryList diaryList;  
    99.                         switch (position) {  
    100.                             case 0 :  
    101.                                 ZLog.i(TAG, "DiaryList0");  
    102.                                 diaryList = new DiaryList(  
    103.                                         getApplicationContext(),  
    104.                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)  
    105.   
    106.                                 );  
    107.                                 ZLog.i(TAG, "DiaryList load0");  
    108.                                 diaryList.load("http://m.ai9475.com/?con=meitian_app");  
    109.                                 break;  
    110.                             case 1:  
    111.                                 ZLog.i(TAG, "DiaryList1");  
    112.                                 diaryList = new DiaryList(  
    113.                                         getApplicationContext(),  
    114.                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)  
    115.   
    116.                                 );  
    117.                                 ZLog.i(TAG, "DiaryList load1");  
    118.                                 diaryList.load("http://m.ai9475.com/?con=meitian_app&act=hot");  
    119.                                 break;  
    120.                             case 2:  
    121.                                 ZLog.i(TAG, "DiaryList2");  
    122.                                 diaryList = new DiaryList(  
    123.                                         getApplicationContext(),  
    124.                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)  
    125.   
    126.                                 );  
    127.                                 ZLog.i(TAG, "DiaryList load2");  
    128.                                 diaryList.load("http://m.ai9475.com/?con=meitian_app");  
    129.                                 break;  
    130.                         }  
    131.                     }  
    132.                 }  
    133.         );  
    134.   
    135.         ActionBar actionBar = getSupportActionBar();  
    136.         actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);  
    137.         // Tab 页面切换  
    138.         MyTabListener listener = new MyTabListener();  
    139.         // 默认的首页 tab  
    140.         ActionBar.Tab indexTab = actionBar.newTab()  
    141.                 .setText(getString(R.string.tab_index))  
    142.                 .setTabListener(listener);  
    143.         actionBar.addTab(indexTab);  
    144.         actionBar.addTab(actionBar.newTab()  
    145.                 .setText(getString(R.string.tab_hot))  
    146.                 .setTabListener(listener)  
    147.         );  
    148.         actionBar.addTab(actionBar.newTab()  
    149.                 .setText(getString(R.string.tab_tag))  
    150.                 .setTabListener(listener)  
    151.         );  
    152.         actionBar.addTab(actionBar.newTab()  
    153.                 .setText(getString(R.string.tab_tag))  
    154.                 .setTabListener(listener)  
    155.         );  
    156.         actionBar.addTab(actionBar.newTab()  
    157.                 .setText(getString(R.string.tab_tag))  
    158.                 .setTabListener(listener)  
    159.         );  
    160.         actionBar.addTab(actionBar.newTab()  
    161.                 .setText(getString(R.string.tab_tag))  
    162.                 .setTabListener(listener)  
    163.         );  
    164.   
    165.         // 显示首页  
    166.         indexTab.select();  
    167.     }  
    168.   
    169.     @Override  
    170.     public boolean onCreateOptionsMenu(Menu menu) {  
    171.         getMenuInflater().inflate(R.menu.main, menu);  
    172.         return true;  
    173.     }  
    174.   
    175.     private class MyTabListener implements ActionBar.TabListener  
    176.     {  
    177.         @Override  
    178.         public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft)  
    179.         {  
    180.             int position = tab.getPosition();  
    181.             ZLog.i(TAG, "tab selected: "+ position);  
    182.             // 数据通信  
    183.             /*Bundle bundle = new Bundle(); 
    184.             Fragment fragment = mPager.getItem(tab.getPosition()); 
    185.             Toast.makeText(getApplicationContext(), "position:"+ tab.getPosition(), Toast.LENGTH_SHORT).show(); 
    186.             fragment.setArguments(bundle); 
    187.             FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); 
    188.             fragmentTransaction.add(R.id.fragmentContainer, fragment); 
    189.             fragmentTransaction.commit();*/  
    190.             mViewPager.setCurrentItem(position);  
    191.         }  
    192.   
    193.         @Override  
    194.         public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {  
    195.             ZLog.i(TAG, "tab reselected: "+ tab.getPosition());  
    196.         }  
    197.   
    198.         @Override  
    199.         public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)  
    200.         {  
    201.             ZLog.i(TAG, "tab unselected: "+ tab.getPosition());  
    202.         }  
    203.     };  
    204.   
    205.     public class MyTabsPagerAdapter extends FragmentPagerAdapter  
    206.     {  
    207.         private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();  
    208.   
    209.         public MyTabsPagerAdapter(FragmentManager fm) {  
    210.             super(fm);  
    211.         }  
    212.   
    213.         public ArrayList<Fragment> getFragments() {  
    214.             return this.mFragments;  
    215.         }  
    216.   
    217.         @Override  
    218.         public Fragment getItem(int i) {  
    219.             return this.mFragments.get(i);  
    220.         }  
    221.   
    222.         @Override  
    223.         public int getCount() {  
    224.             return this.mFragments.size();  
    225.         }  
    226.     }  
    227. }  
posted @ 2016-11-05 16:29  天涯海角路  阅读(180)  评论(0)    收藏  举报