分享

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

 ccccshq 2015-05-12

感觉 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./?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./?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./?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./?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 还是找到了办法,之前花了很大功夫来自己实现,后来偶然发现他居然有个自带的方法

  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./?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./?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./?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. }  




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多