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

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

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

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

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

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

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

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

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

解答:

一、Fragment 的事件回调:

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

这里主要用到 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 完整代码:

1package com.ai9475.meitian.ui; 2 3import android.os.Bundle; 4import android.support.v4.app.Fragment; 5import android.support.v4.app.FragmentManager; 6import android.support.v4.app.FragmentPagerAdapter; 7import android.support.v4.app.FragmentStatePagerAdapter; 8import android.support.v4.app.FragmentTransaction; 9import android.support.v4.view.ViewPager; 10import android.support.v7.app.ActionBar; 11import android.view.Menu; 12import android.widget.ListView; 13 14import com.ai9475.meitian.AppManager; 15import com.ai9475.meitian.R; 16import com.ai9475.meitian.ui.fragment.DiaryListFragment; 17import com.ai9475.meitian.ui.fragment.Test2Fragment; 18import com.ai9475.meitian.ui.fragment.Test3Fragment; 19import com.ai9475.meitian.view.DiaryList; 20import com.ai9475.util.ZLog; 21 22import java.util.ArrayList; 23 24public 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}
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

​一篇文章总结一下Python库中关于时间的常见操作

前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在

Java获得今日零时零分零秒的时间(Date型)

publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS