ViewPager之Fragment页面切换
一、概述
ViewPager是android-support-v4中提供的类,它是一个容器类,常用于页面之间的切换。
继上篇文章《ViewPager之引导页》之后,本文主要介绍ViewPager更为通用的实践:ViewPager搭配Fragment实现页面切换。
这种实现方式相对于上篇文章而言,可以更好的支持不同页面各自的复杂逻辑,与此同时,也能够保障页面之间的耦合度尽可能的低。
按照惯例,先晒出效果图:

二、实现思路
首先分析一下不同区域的交互需求:
中间灰色区域除了要支持三套完全不同的逻辑之外,还要支持左右滑动切换。而顶部ActionBar和底部Tab切换都只需要更新状态,无需整体变换,也不需要整体滑动;
因此,中间灰色区域用ViewPager实现,它包含三个Fragment子页面。而顶部ActionBar和底部Tab切换各自是一个Fragment,直接隶属于Activity。
然后解决不同Fragment之间的依赖关系:
五个Fragment之间沟通的唯一纽带就是ViewPager页面的切换,因此,ViewPager页面切换时通知到不同的Fragment即可。
[转载请保留本文地址:http://www.cnblogs.com/snser/p/5700754.html]
三、开始干活
3.1 搭建整体框架
本文的五个Fragment采用三种方式载入:
ViewPager中的三个Fragment自然是通过其Adatper进行载入,顶部的TitleFragment(ActionBar)直接声明在layout布局中,而底部的TabFragment采用动态载入。
这样做的目的是方便后续分析不同载入方式的实际载入实际。
ok,整体的layout布局自然也就成了下面的样子:
viewpager_fragment.xml(activity的布局):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_default"
tools:context="${relativePackage}.${activityClass}" > <fragment
android:id="@+id/viewpager_fragment_title"
android:name="cc.snser.cnblog5700754.TitleFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true" /> <android.support.v4.view.ViewPager
android:id="@+id/viewpager_fragment_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/viewpager_fragment_container"
android:layout_below="@+id/viewpager_fragment_title" /> <FrameLayout
android:id="@id/viewpager_fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" >
</FrameLayout> </RelativeLayout>
对应的,主界面的逻辑如下:
ViewPagerFragmentActivity.java:
public class ViewPagerFragmentActivity extends FragmentActivity {
private FragmentManager mManager = getSupportFragmentManager();
private TitleFragment mTitleFragment;
private TabFragment mTabFragment;
private ViewPager mPager;
private ViewPagerAdapter mAdapter;
private static final int DEFAULT_PAGE = 1; //默认页面
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("Snser", "ViewPagerFragmentActivity onCreate");
super.onCreate(savedInstanceState);
Log.d("Snser", "ViewPagerFragmentActivity onCreate setContentView");
setContentView(R.layout.viewpager_fragment);
Log.d("Snser", "ViewPagerFragmentActivity onCreate initView");
initView();
}
private void initView() {
mTitleFragment = (TitleFragment)mManager.findFragmentById(R.id.viewpager_fragment_title);
mTabFragment = new TabFragment();
mTabFragment.setOnTabClickListenser(new ViewPageTabClickListenser());
mManager.beginTransaction().replace(R.id.viewpager_fragment_container, mTabFragment).commit();
mPager = (ViewPager)findViewById(R.id.viewpager_fragment_pager);
mPager.setAdapter(mAdapter = new ViewPagerAdapter(mManager));
mPager.setOnPageChangeListener(new ViewPageChangeListener());
setCurrentItem(DEFAULT_PAGE);
}
@Override
protected void onResume() {
Log.d("Snser", "ViewPagerFragmentActivity onResume");
super.onResume();
}
private class ViewPageChangeListener implements OnPageChangeListener {
@Override
public void onPageSelected(int position) {
setCurrentItem(position);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
private class ViewPagerAdapter extends FragmentPagerAdapter {
private ArrayList<PagerFragment> mFragments = new ArrayList<PagerFragment>();
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
mFragments.add(new ClickFragment());
mFragments.add(new DateFragment());
mFragments.add(new AnimFragment());
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
}
private class ViewPageTabClickListenser implements OnTabClickListenser {
@Override
public void onTabClick(int tab) {
setCurrentItem(tab);
}
}
private void setCurrentItem(int item) {
if (item == mPager.getCurrentItem()) {
//此时是源于initView或onPageSelected的调用
notifyPageChangeToFragments(item);
} else {
//此时是源于initView或onTabClick的调用,后续会自动触发一次onPageSelected
mPager.setCurrentItem(item);
}
}
private void notifyPageChangeToFragments(int item) {
for (int page = 0; page != mAdapter.getCount(); ++page) {
final Fragment fragment = mAdapter.getItem(page);
if (fragment instanceof PagerFragment) {
if (page == item) {
((PagerFragment)fragment).onPageIn();
} else {
((PagerFragment)fragment).onPageOut();
}
}
}
mTitleFragment.setCurrentTab(item);
mTabFragment.setCurrentTab(item);
}
}
可以看到,包含三种完全不同功能的Activity,其主界面代码居然只有区区106行,这甚至比上篇文章《ViewPager之引导页》还要少。
这必须要归功于Fragment的使用。
三个页面的具体逻辑分别由DateFragment、ClickFragment、AnimFragment进行维护,而ViewPager要做的,只是进行Fragment的切换和通知。
具体来说,ViewPagerAdapter负责根据不同的postion取出不同的Fragment。而三个页面Fragment都继承自PagerFragment:
PagerFragment.java:
public class PagerFragment extends Fragment{
public void onPageIn() {
}
public void onPageOut() {
}
}
亦即在页面切换的时候(不管是来自滑动还是点击tab),activity都会通知三个fragment它们各自是否可见。
同时,在 notifyPageChangeToFragments 方法中,也会通知TitleFragment和TabFragment,它们该切换状态了。
3.2 Fragment的各自实现
拿AnimFragment举例,这个动画Fragment的功能是在其切入(onPageIn)的时候播放淡入的动画,而在其切出(onPageOut)的时候销毁动画。
public class AnimFragment extends PagerFragment {
private View mViewRoot;
private ImageView mImg;
private Animation mAnim;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("Snser", "AnimFragment onCreateView");
mViewRoot = inflater.inflate(R.layout.viewpager_fragment_anim, container, false);
mAnim = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_fade_in);
mAnim.setInterpolator(new LinearInterpolator());
initView(mViewRoot);
return mViewRoot;
}
private void initView(View root) {
mImg = (ImageView)root.findViewById(R.id.viewpager_fragment_anim_img);
startAnim();
}
@Override
public void onDestroy() {
super.onDestroy();
stopAnim();
}
@Override
public void onPageIn() {
super.onPageIn();
startAnim();
}
@Override
public void onPageOut() {
super.onPageOut();
stopAnim();
}
private void startAnim() {
if (mImg != null && mAnim != null) {
mImg.startAnimation(mAnim);
}
}
private void stopAnim() {
if (mImg != null && mAnim != null) {
mImg.clearAnimation();
}
}
}
DateFragment的作用是在切入的时候刷新当前日期和时间:
DateFragment.java:
public class DateFragment extends PagerFragment {
private View mViewRoot;
private TextView mTxtDate;
private SimpleDateFormat mFormat = new SimpleDateFormat("yyyy/MM/dd\nHH:mm:ss", Locale.CHINA);
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("Snser", "DateFragment onCreateView");
mViewRoot = inflater.inflate(R.layout.viewpager_fragment_date, container, false);
initView(mViewRoot);
return mViewRoot;
}
@Override
public void onPageIn() {
super.onPageIn();
refreshDate();
}
private void initView(View root) {
mTxtDate = (TextView)root.findViewById(R.id.viewpager_fragment_date_txt);
refreshDate();
}
private void refreshDate() {
if (mTxtDate != null) {
mTxtDate.setText(mFormat.format(new Date()));
}
}
}
ClickFragment则搞定了一个多次连续点击“中彩蛋”的效果:
ClickFragment.java:
public class ClickFragment extends PagerFragment {
private View mViewRoot;
private Button mBtnClick;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("Snser", "ClickFragment onCreateView");
mViewRoot = inflater.inflate(R.layout.viewpager_fragment_click, container, false);
initView(mViewRoot);
return mViewRoot;
}
private void initView(View root) {
mBtnClick = (Button)root.findViewById(R.id.viewpager_fragment_click_btn);
mBtnClick.setOnClickListener(new MultiClickListener());
}
private static class MultiClickListener implements View.OnClickListener {
private int mCount = 0;
private long mLastClickTime = 0;
private static final long MAX_CLICK_COUNT = 4;
private static final long TIMEOUT_CLICK = 500;
private static final int MSG_TIMEOUT_CLICK = 0x1001;
private static Handler sHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_TIMEOUT_CLICK && msg.obj instanceof Button) {
Log.d("Snser", "MultiClickListener 连续点击超时");
((Button)msg.obj).setText("点我");
}
}
};
@Override
public void onClick(View v) {
if (v instanceof Button) {
final long time = System.currentTimeMillis();
if (mCount == 0 || time - mLastClickTime < TIMEOUT_CLICK) {
//这是连续点击
Log.d("Snser", "MultiClickListener 连续点击 mCount=" + (mCount + 1));
++mCount;
mLastClickTime = time;
sHandler.removeMessages(MSG_TIMEOUT_CLICK);
sHandler.sendMessageDelayed(Message.obtain(sHandler, MSG_TIMEOUT_CLICK, v), TIMEOUT_CLICK);
if (mCount < MAX_CLICK_COUNT) {
((Button)v).setText("点我 +" + mCount);
} else {
((Button)v).setText("死机了!");
}
} else {
//这是新的点击
Log.d("Snser", "MultiClickListener 新的点击 mCount=0");
mCount = 0;
onClick(v);
}
}
}
}
}
除了页面Fragment之外,TabFragment和TitleFragment都会在 setCurrentTab 方法中更新自己的状态。
不同的是,TabFragment会将点击tab的事件通过 OnTabClickListenser 反馈给activity:
public class TabFragment extends Fragment {
private View mRootView;
private TextView mTxtTabApp;
private TextView mTxtTabHome;
private TextView mTxtTabUser;
private OnTabClickInternalListener mTabClickInternalListener = new OnTabClickInternalListener();
private OnTabClickListenser mOnTabClickListenser;
private int mDefaultTab = 0;
private int mCurrentTab = -1;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("Snser", "TabFragment onCreateView");
mRootView = inflater.inflate(R.layout.viewpager_fragment_tab, container, false);
initView(mRootView);
return mRootView;
}
private void initView(View root) {
mTxtTabApp = (TextView)root.findViewById(R.id.viewpager_fragment_tab_app);
mTxtTabApp.setTag(Integer.valueOf(0));
mTxtTabApp.setOnClickListener(mTabClickInternalListener);
mTxtTabHome = (TextView)root.findViewById(R.id.viewpager_fragment_tab_home);
mTxtTabHome.setTag(Integer.valueOf(1));
mTxtTabHome.setOnClickListener(mTabClickInternalListener);
mTxtTabUser = (TextView)root.findViewById(R.id.viewpager_fragment_tab_user);
mTxtTabUser.setTag(Integer.valueOf(2));
mTxtTabUser.setOnClickListener(mTabClickInternalListener);
setCurrentTab(mDefaultTab);
}
public interface OnTabClickListenser {
public void onTabClick(int tab);
}
private class OnTabClickInternalListener implements View.OnClickListener {
@Override
public void onClick(View v) {
if (v != null && v.getTag() instanceof Integer) {
final int tab = (Integer)v.getTag();
if (tab != mCurrentTab && mOnTabClickListenser != null) {
setCurrentTab(tab);
mOnTabClickListenser.onTabClick(tab);
}
}
}
}
public void setOnTabClickListenser(OnTabClickListenser listenser) {
mOnTabClickListenser = listenser;
}
public void setCurrentTab(int tab) {
if (mTxtTabApp != null && mTxtTabHome != null && mTxtTabUser != null) {
mCurrentTab = tab;
mTxtTabApp.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_normal));
mTxtTabHome.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_normal));
mTxtTabUser.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_normal));
switch (tab) {
case 0:
mTxtTabApp.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_current));
break;
case 1:
mTxtTabHome.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_current));
break;
case 2:
mTxtTabUser.setTextColor(getResources().getColor(R.color.viewpager_fragment_tab_current));
break;
default:
break;
}
} else {
//TabFragment在Activity的onResume之后才会onCreateView
//setCurrentTab的时候控件还没初始化,存一下初始值在initView里再初始化
mDefaultTab = tab;
}
}
}
public class TitleFragment extends Fragment {
private View mRootView;
private TextView mTxt;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("Snser", "TitleFragment onCreateView");
mRootView = inflater.inflate(R.layout.viewpager_fragment_title, container, false);
initView(mRootView);
return mRootView;
}
private void initView(View root) {
mTxt = (TextView)root.findViewById(R.id.viewpager_fragment_title_txt);
}
public void setCurrentTab(int tab) {
if (mTxt != null) {
switch (tab) {
case 0:
mTxt.setText("#ClickFragment");
break;
case 1:
mTxt.setText("#DateFragment");
break;
case 2:
mTxt.setText("#AnimFragment");
break;
default:
break;
}
}
}
}
3.3 Fragment的加载顺序
回归到3.1节中介绍到本文采用三种方式进行Fragment的载入,目的就是为了比较不同方式下Fragment的加载时机。
在 DEFAULT_PAGE = 1 时,各个Fragment的加载顺序为:
// ViewPagerFragmentActivity onCreate
// ViewPagerFragmentActivity onCreate setContentView
// TitleFragment onCreateView (声明在layout布局中的Fragment在setContentView之后就会被加载)
// ViewPagerFragmentActivity onCreate initView
// TabFragment onCreateView (动态replace的Fragment在replace之后、onResume之前被加载)
// ViewPagerFragmentActivity onResume
// DateFragment onCreateView (ViewPager最先加载默认页)
// ClickFragment onCreateView (ViewPager默认页左边的页面会被预加载)
// AnimFragment onCreateView (ViewPager默认页右边的页面会被预加载)
在 DEFAULT_PAGE = 0 时,各个Fragment的加载顺序为:
// ViewPagerFragmentActivity onCreate
// ViewPagerFragmentActivity onCreate setContentView
// TitleFragment onCreateView (声明在layout布局中的Fragment在setContentView之后就会被加载)
// ViewPagerFragmentActivity onCreate initView
// TabFragment onCreateView (动态replace的Fragment在replace之后、onResume之前被加载)
// ViewPagerFragmentActivity onResume
// ClickFragment onCreateView (ViewPager最先加载默认页)
// DateFragment onCreateView(ViewPager默认页右边的页面会被预加载)
// AnimFragment onCreateView(ViewPager切换到DateFragment时才会预加载AnimFragment)
[转载请保留本文地址:http://www.cnblogs.com/snser/p/5700754.html]
四、demo工程
先晒一下本文的动图效果:

工程源码如下(下载下面的图片,扩展名改成zip即可):

[转载请保留本文地址:http://www.cnblogs.com/snser/p/5700754.html]
ViewPager之Fragment页面切换的更多相关文章
- Android中使用ViewPager实现屏幕页面切换和页面切换效果
之前关于如何实现屏幕页面切换,写过一篇博文<Android中使用ViewFlipper实现屏幕切换>,相比ViewFlipper,ViewPager更适用复杂的视图切换,而且Viewpag ...
- Android开发之ViewPager实现多页面切换及动画效果(仿Android的Launcher效果)
Android开发中经常会有引导页或者切换页面等效果,本文采用ViewPager结合动画效果来实现仿Launcher以及页面切换的效果.源码地址在文章最后给出下载. 效果图如下: 1.Vi ...
- Android实战简易教程-第二十六枪(基于ViewPager实现微信页面切换效果)
1.头部布局文件top.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout x ...
- Android中的Fragment页面切换和selector选择器
效果如图: 提示:下面是用的整个的图片 下面看代码: //--------------------这是主页面布局文件----------------------- <?xml version=& ...
- Android之怎样实现滑动页面切换【Fragment】
Fragment 页面切换不能滑动 所以对于listview 能够加入的左右滑动事件 .不会有冲突比如(QQ的好友列表的删除) Fragment 和viewpager 的差别 Viewpager ...
- 仿知乎程序 fragment的切换以及toolbar在不同页面下显示的menu不同
我们在看知乎的时候,你会发现,首页,发现,关注,收藏,草稿这五项,你在点击之后进入到相应页面之后,侧滑菜单还在,你左侧滑一下,这个侧滑菜单还在,而提问,左滑屏幕,这个页面就没有,有点像返 ...
- Xamarin自定义布局系列——PivotPage,多页面切换控件
PivotPage ---- 多页面切换控件 PivotPage是一个多页面切换控件,类似安卓中的ViewPager和UWP中的Pivot枢轴控件. 起初打算直接通过ScrollView+StackL ...
- Fragment与Fragment相互切换之间的生命周期方法
Fragment 1 切换到 Fragment 2时生命周期变化 1.通过 add hide show 方式来切换 Fragment Fragment1 的生命周期变化为:onCreate().onC ...
- Android之ActionBar、Tabs、Fragment、ViewPager实现标签页切换并缓存页面
感觉 Android 到处都是坑,每个地方都要把人折腾半天. 今天来简单说说 Android之ActionBar.Tabs.Fragment.ViewPager 实现标签页切换并缓存页面 关于他们的介 ...
随机推荐
- MONO x64 amd_x64
SharpPcap 是一个.NET 环境下的网络包捕获框架,基于著名的 pcap/WinPcap 库开发.提供了捕获.注入.分析和构建的功能,SharpPcap 还是一个跨平台的库,可以在Mono/. ...
- 使用“Cocos引擎”创建的cpp工程如何在VS中调试Cocos2d-x源码
前段时间Cocos2d-x更新了一个Cocos引擎,这是一个集合源码,IDE,Studio这一家老小的整合包,我们可以使用这个Cocos引擎来创建我们的项目. 在Cocos2d-x被整合到Cocos引 ...
- Mac 词典工具推荐:Youdao Alfred Workflow(可同步单词本)
想必大家都有用过 Mac 下常见的几款词典工具: 特性 系统 Dictionary 欧路词典 Mac 版 有道词典 Mac 版 在线搜索 ✗ ✔ ✔ 屏幕取词 ☆☆☆ ★★☆ ★☆☆ 划词搜索 ★★★ ...
- Repository 仓储,你的归宿究竟在哪?(三)-SELECT 某某某。。。
写在前面 首先,本篇博文主要包含两个主题: 领域服务中使用仓储 SELECT 某某某(有点晕?请看下面.) 上一篇:Repository 仓储,你的归宿究竟在哪?(二)-这样的应用层代码,你能接受吗? ...
- CanvasWebgl项目介绍
CanvasWebgl 介绍 CanvasWebgl 是一个基于webgl 开发的2d绘图框架,使用TypeScript开发 CanvasWebgl的功能,是在屏幕空间 或者 3D空间产生一个画布 ...
- 设置Fn键 笔记本直接按F1-F12 无须按Fn键 Fn+F12改F12(联想小新300为例)
最近公司给配的笔记本联想小新300 80RT i7-6500U 4G内存 500G机械,后加装120G固态+4G内存 这样就感觉还不错了. 在使用这本子的时候,去了Win10,强行装了Win7.无线 ...
- 如何优雅地使用Sublime Text
Sublime Text:一款具有代码高亮.语法提示.自动完成且反应快速的编辑器软件,不仅具有华丽的界面,还支持插件扩展机制,用她来写代码,绝对是一种享受.相比于难于上手的Vim,浮肿沉重的Eclip ...
- iOS 4s-6Plus屏幕自动适配及颜色转换为十六进制
iOS各种屏幕自动适配及颜色转换为十六进制 ★★★XLJMatchScreen自动适配屏幕★★★ 支持pod导入 pod 'XLJScreenMatching', '~> 1.0.3' 如果发现 ...
- ScrollView嵌套ListView,GridView数据加载不全问题的解决
我们大家都知道ListView,GridView加载数据项,如果数据项过多时,就会显示滚动条.ScrollView组件里面只能包含一个组件,当ScrollView里面嵌套listView,GridVi ...
- Esay ui数据加载等待提示
以视频上传为例: //视频上传 function uploadVedio(fileName){ load();//开始加载时弹出加载层 $.post('< ...