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


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


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


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


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


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


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


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



解答:


一、Fragment 的事件回调:



package com.ai9475.meitian.ui.fragment;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
 
import com.ai9475.meitian.R;
import com.ai9475.meitian.view.DiaryList;
import com.ai9475.util.ZLog;
 
/**
  *
  * Created by ZHOUZ on 14-1-21.
  */
public class DiaryListFragment extends BaseFragment
{
     private static final String TAG = "DiaryListFragment" ;
 
     @Override
     public void onAttach(Activity activity)
     {
         ZLog.i(TAG, "onAttach" );
         super .onAttach(activity);
     }
 
     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         ZLog.i(TAG, "onCreate" );
         super .onCreate(savedInstanceState);
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
     {
         ZLog.i(TAG, "onCreateView" );
         return inflater.inflate(R.layout.fragment_diary_list, container, false );
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState)
     {
         ZLog.i(TAG, "onActivityCreated" );
         super .onActivityCreated(savedInstanceState);
         ZLog.i(TAG, "DiaryList0" );
         DiaryList diaryList = new DiaryList(
                 getActivity().getApplicationContext(),
                 (ListView) getView().findViewById(R.id.diaryListCt)
 
         );
         ZLog.i(TAG, "DiaryList load0" );
         diaryList.load( "http://m.ai9475.com/?con=meitian_app" );
         this .setRetainInstance( true );
     }
 
     @Override
     public void onStart()
     {
         ZLog.i(TAG, "onStart" );
         super .onStart();
     }
 
     @Override
     public void onResume()
     {
         ZLog.i(TAG, "onResume" );
         super .onResume();
     }
 
     @Override
     public void onPause()
     {
         ZLog.i(TAG, "onPause" );
         super .onPause();
     }
 
     @Override
     public void onStop()
     {
         ZLog.i(TAG, "onStop" );
         super .onStop();
     }
 
     @Override
     public void onDestroyView()
     {
         ZLog.i(TAG, "onDestroyView" );
         super .onDestroyView();
     }
 
     @Override
     public void onDestroy()
     {
         ZLog.i(TAG, "onDestroy" );
         super .onDestroy();
     }
 
     @Override
     public void onDetach()
     {
         ZLog.i(TAG, "onDetach" );
         super .onDetach();
     }
}


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



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


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


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



二、切换页面卡顿问题


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


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


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


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




< android.support.v4.view.ViewPager
         android:id = "@+id/tabsViewPager"
         android:layout_width = "match_parent"
         android:layout_height = "match_parent"
         >
     </ android.support.v4.view.ViewPager >


mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);
mViewPager.setOnPageChangeListener(
                 new ViewPager.SimpleOnPageChangeListener() {
                     private static final String TAG = "ViewPager.SimpleOnPageChangeListener" ;
                     private ArrayList hasLoadedPages = new ArrayList<Integer>();
                     @Override
                     public void onPageSelected( int position) {
                         ZLog.i(TAG, "onPageSelected position:" + position);
                         getSupportActionBar().setSelectedNavigationItem(position);
                     }
 
                     @Override
                     public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels)
                     {
                         ZLog.i(TAG, "onPageScrolled position: " + position + ", positionOffset:" + positionOffset + ", positionOffsetPixels:" + positionOffsetPixels);
                     }
 
                     @Override
                     public void onPageScrollStateChanged( int state) {
                         ZLog.i(TAG, "onPageScrollStateChanged" );
                         int position = mViewPager.getCurrentItem();
 
                         switch (state) {
                             // 正在拖动
                             case ViewPager.SCROLL_STATE_DRAGGING :
                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:" + position);
                                 break ;
                             // 拖动释放后正在沉降的过程
                             case ViewPager.SCROLL_STATE_SETTLING :
                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:" + position);
                                 break ;
                             // 切换动画全部完成结束
                             case ViewPager.SCROLL_STATE_IDLE :
                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:" + position);
                 // 已加载过则不再加载
                                 if (hasLoadedPages.contains(position)) break ;
                                 Fragment fragment = mPager.getFragments().get(position);
                                 runCallback(position, fragment);
                                 hasLoadedPages.add(position);
                                 break ;
                         }
                     }
 
                     public void runCallback( int position, Fragment fragment) {
                         ZLog.i(TAG, "runCallback" );
                         DiaryList diaryList;
                         switch (position) {
                             case 0 :
                                 ZLog.i(TAG, "DiaryList0" );
                                 diaryList = new DiaryList(
                                         getApplicationContext(),
                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)
 
                                 );
                                 ZLog.i(TAG, "DiaryList load0" );
                                 diaryList.load( "http://m.ai9475.com/?con=meitian_app" );
                                 break ;
                             case 1 :
                                 ZLog.i(TAG, "DiaryList1" );
                                 diaryList = new DiaryList(
                                         getApplicationContext(),
                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)
 
                                 );
                                 ZLog.i(TAG, "DiaryList load1" );
                                 diaryList.load( "http://m.ai9475.com/?con=meitian_app&act=hot" );
                                 break ;
                             case 2 :
                                 ZLog.i(TAG, "DiaryList2" );
                                 diaryList = new DiaryList(
                                         getApplicationContext(),
                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)
 
                                 );
                                 ZLog.i(TAG, "DiaryList load2" );
                                 diaryList.load( "http://m.ai9475.com/?con=meitian_app" );
                                 break ;
                         }
                     }
                 }

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



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


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



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


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



// 设置缓存多少个 Tab对应的 fragment
         mViewPager.setOffscreenPageLimit(6);


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



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



四、ActionBar 中的 Tabs


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




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



package com.ai9475.meitian.ui;
 
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.view.Menu;
import android.widget.ListView;
 
import com.ai9475.meitian.AppManager;
import com.ai9475.meitian.R;
import com.ai9475.meitian.ui.fragment.DiaryListFragment;
import com.ai9475.meitian.ui.fragment.Test2Fragment;
import com.ai9475.meitian.ui.fragment.Test3Fragment;
import com.ai9475.meitian.view.DiaryList;
import com.ai9475.util.ZLog;
 
import java.util.ArrayList;
 
public class MainActivity extends BaseActivity
{
     private static final String TAG = "MainActivity" ;
     private MyTabsPagerAdapter mPager;
     private ViewPager mViewPager;
 
     @Override
     protected void onCreate(Bundle savedInstanceState)
     {
         ZLog.i(TAG, "start" );
         // 执行父级初始化方法
         super .onCreate(savedInstanceState);
         //requestWindowFeature(Window.FEATURE_NO_TITLE);
         ZLog.i(TAG, "setContentView" );
         setContentView(R.layout.activity_main);
 
         // 滑动页面视图配置
         ZLog.i(TAG, "MyTabsPagerAdapter start" );
         mPager = new MyTabsPagerAdapter(getSupportFragmentManager());
         mPager.getFragments().add( new DiaryListFragment());
         mPager.getFragments().add( new Test2Fragment());
         mPager.getFragments().add( new Test3Fragment());
         mPager.getFragments().add( new Test3Fragment());
         mPager.getFragments().add( new Test3Fragment());
         mPager.getFragments().add( new Test3Fragment());
         // 滑动分页容器
         mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);
         // 设置缓存多少个 fragment
         mViewPager.setOffscreenPageLimit( 6 );
         mViewPager.setAdapter(mPager);
         // 页面滑动事件
         mViewPager.setOnPageChangeListener(
                 new ViewPager.SimpleOnPageChangeListener() {
                     private static final String TAG = "ViewPager.SimpleOnPageChangeListener" ;
                     private ArrayList hasLoadedPages = new ArrayList<Integer>();
                     @Override
                     public void onPageSelected( int position) {
                         ZLog.i(TAG, "onPageSelected position:" + position);
                         getSupportActionBar().setSelectedNavigationItem(position);
                     }
 
                     @Override
                     public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels)
                     {
                         ZLog.i(TAG, "onPageScrolled position: " + position + ", positionOffset:" + positionOffset + ", positionOffsetPixels:" + positionOffsetPixels);
                     }
 
                     @Override
                     public void onPageScrollStateChanged( int state) {
                         ZLog.i(TAG, "onPageScrollStateChanged" );
                         int position = mViewPager.getCurrentItem();
 
                         switch (state) {
                             // 正在拖动
                             case ViewPager.SCROLL_STATE_DRAGGING :
                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:" + position);
                                 break ;
                             // 拖动释放后正在沉降的过程
                             case ViewPager.SCROLL_STATE_SETTLING :
                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:" + position);
                                 break ;
                             // 切换动画全部完成结束
                             case ViewPager.SCROLL_STATE_IDLE :
                                 ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:" + position);
                                 /*if (hasLoadedPages.contains(position)) break;
                                 Fragment fragment = mPager.getFragments().get(position);
                                 runCallback(position, fragment);
                                 hasLoadedPages.add(position);*/
                                 break ;
                         }
                     }
 
                     public void runCallback( int position, Fragment fragment) {
                         ZLog.i(TAG, "runCallback" );
                         DiaryList diaryList;
                         switch (position) {
                             case 0 :
                                 ZLog.i(TAG, "DiaryList0" );
                                 diaryList = new DiaryList(
                                         getApplicationContext(),
                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)
 
                                 );
                                 ZLog.i(TAG, "DiaryList load0" );
                                 diaryList.load( "http://m.ai9475.com/?con=meitian_app" );
                                 break ;
                             case 1 :
                                 ZLog.i(TAG, "DiaryList1" );
                                 diaryList = new DiaryList(
                                         getApplicationContext(),
                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)
 
                                 );
                                 ZLog.i(TAG, "DiaryList load1" );
                                 diaryList.load( "http://m.ai9475.com/?con=meitian_app&act=hot" );
                                 break ;
                             case 2 :
                                 ZLog.i(TAG, "DiaryList2" );
                                 diaryList = new DiaryList(
                                         getApplicationContext(),
                                         (ListView) fragment.getView().findViewById(R.id.diaryListCt)
 
                                 );
                                 ZLog.i(TAG, "DiaryList load2" );
                                 diaryList.load( "http://m.ai9475.com/?con=meitian_app" );
                                 break ;
                         }
                     }
                 }
         );
 
         ActionBar actionBar = getSupportActionBar();
         actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
         // Tab 页面切换
         MyTabListener listener = new MyTabListener();
         // 默认的首页 tab
         ActionBar.Tab indexTab = actionBar.newTab()
                 .setText(getString(R.string.tab_index))
                 .setTabListener(listener);
         actionBar.addTab(indexTab);
         actionBar.addTab(actionBar.newTab()
                 .setText(getString(R.string.tab_hot))
                 .setTabListener(listener)
         );
         actionBar.addTab(actionBar.newTab()
                 .setText(getString(R.string.tab_tag))
                 .setTabListener(listener)
         );
         actionBar.addTab(actionBar.newTab()
                 .setText(getString(R.string.tab_tag))
                 .setTabListener(listener)
         );
         actionBar.addTab(actionBar.newTab()
                 .setText(getString(R.string.tab_tag))
                 .setTabListener(listener)
         );
         actionBar.addTab(actionBar.newTab()
                 .setText(getString(R.string.tab_tag))
                 .setTabListener(listener)
         );
 
         // 显示首页
         indexTab.select();
     }
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.main, menu);
         return true ;
     }
 
     private class MyTabListener implements ActionBar.TabListener
     {
         @Override
         public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft)
         {
             int position = tab.getPosition();
             ZLog.i(TAG, "tab selected: " + position);
             // 数据通信
             /*Bundle bundle = new Bundle();
             Fragment fragment = mPager.getItem(tab.getPosition());
             Toast.makeText(getApplicationContext(), "position:"+ tab.getPosition(), Toast.LENGTH_SHORT).show();
             fragment.setArguments(bundle);
             FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
             fragmentTransaction.add(R.id.fragmentContainer, fragment);
             fragmentTransaction.commit();*/
             mViewPager.setCurrentItem(position);
         }
 
         @Override
         public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
             ZLog.i(TAG, "tab reselected: " + tab.getPosition());
         }
 
         @Override
         public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
         {
             ZLog.i(TAG, "tab unselected: " + tab.getPosition());
         }
     };
 
     public class MyTabsPagerAdapter extends FragmentPagerAdapter
     {
         private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
 
         public MyTabsPagerAdapter(FragmentManager fm) {
             super (fm);
         }
 
         public ArrayList<Fragment> getFragments() {
             return this .mFragments;
         }
 
         @Override
         public Fragment getItem( int i) {
             return this .mFragments.get(i);
         }
 
         @Override
         public int getCount() {
             return this .mFragments.size();
         }
     }
}

文章知识点与官方知识档