感觉 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();
}
}
}

Android之ActionBar、Tabs、Fragment、ViewPager实现标签页切换并缓存页面的更多相关文章

  1. ActionBar+Fragment实现顶部标签页

    用ActionBar的TABS模式,和Fragment实现程序顶部的标签页切换. 一. MainActivity         public class MainActivity extends A ...

  2. tabs标签页的数据缓存

    一进入tabs标签页默认就将所有标签页的数据请求到,并渲染到页面上, 这样如果数据量太大的话会渲染很久, 我的需求就是点击不同的标签时再请求数据,同时对点击过的标签页数据进行缓存,下次点击时不再重新请 ...

  3. 在Bootstrap开发中解决Tab标签页切换图表显示问题

    在做响应式页面的时候,往往需要考虑更多尺寸设备的界面兼容性,一般不能写死像素,以便能够使得界面元素能够根据设备的不同进行动态调整,但往往有时候还是碰到一些问题,如Tab标签第一页面正常显示,但是切换其 ...

  4. JavaScript 实现 标签页 切换效果

    JavaScript 实现 标签页 切换效果 版权声明:未经授权,严禁分享! 构建主体界面 HTML 代码 <h1>实现标签页的切换效果</h1> <ul id=&quo ...

  5. JS实现标签页切换效果

    本文实例为大家分享了JS标签页切换的具体代码,供大家参考,具体内容如下   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ...

  6. Android tab导航的几种方法:ActionBar tab +fragment,Viewpager+pagerTitleStrip,开源框架ViewPageIndicator 和 ViewPager

    action来实现tab标签 并跟fragment结合 因为要写新闻客户端这个tab导航是必须的 这里我写几个小练习,希望大家融会贯通. 1actionbar设置tab +fragment 布局是个l ...

  7. Android Viewpager+Fragment实现滑动标签页

    ViewPager 结合Fragment实现一个Activity里包含多个可滑动的标签页,每个标签页可以有独立的布局及响应. 主页布局 <?xml version="1.0" ...

  8. Android 底部按钮BottomNavigationView + Fragment + viewPager 的使用(一)

    实现的效果,左右滑动,底部栏跟着滑动,中间加的是分帧的页面        上代码:主页面activity_main.xml <?xml version="1.0" encod ...

  9. Android主页导航:fragment+viewpager

    简单实现Fragment+ViewPager实现主页导航控制,效果如下: 一.activity_main.xml布局文件: <?xml version="1.0" encod ...

随机推荐

  1. 游戏排行榜-Python实现

    背景介绍 排行榜通常是游戏中为了激发玩家的一种策略,那么对于开发人员来说如何完成一个排行榜的设计呢?如果这个排行榜是动态的如何才能高效的对比出结果呢?如果排行榜实时性较高如何给用户展示出用户是进步了还 ...

  2. 再谈加密-RSA非对称加密的理解和使用

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  3. hdu 1848 Fibonacci again and again(简单sg)

    Problem Description 任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的:F(1)=1;F(2)=2;F(n)=F(n-1)+F(n-2 ...

  4. CSS3学习之——【特殊属性】

    一.CSS3的一些特殊属性 1.1 text-shadow text-shadow曾经在css2中就出现过,但在css2.1版本中又被抛弃了,现在css3.0版本又重新捡回来了.这说明text-sha ...

  5. gimagex 2.0.17 汉化版

    软件名称: gimagex 2.0.17 汉化版 软件语言: 简体中文 授权方式: 免费软件 运行环境: Win 32位/64位 软件大小: 1.31MB 图片预览: 软件简介: gimagex 2. ...

  6. Spring Security(19)——对Acl的支持

    目录 1.1           准备工作 1.2           表功能介绍 1.2.1     表acl_sid 1.2.2     表acl_class 1.2.3     表acl_obj ...

  7. SQL如何实现远程数据库链接

    利用sp_addlinkeserver 进行远程数据库连接可以把多个数据库的数据,放置到一个数据库中, 或者有设置操作权限的情况下,我们可以通过这种方式进行查询,备份数据等操作. 首先,我们要创建连接 ...

  8. UserDefault数据读取

    //GameScene.h #include "cocos2d.h" USING_NS_CC; class GameScene : public cocos2d::Layer{pu ...

  9. vultr优惠码ssd vps赠送50美金,长期有效

    vultr最新优惠码.vultr vps注册教程,是大家关心的问题.网上流传很多vultr vps优惠码,鱼龙混杂,难以判断.其实,获取vultr优惠赠送美元的方式很简单. 第一种,新用户使用绑定信用 ...

  10. Chapter 21_2 模式匹配函数

    基础函数比较简单,就是几个普通的函数string.byte.string.char.string.rep.string.sub.string.format还有大小写转换函数upper和lower. 接 ...