Fragment系列文章:
1、Fragment全解析系列(一):那些年踩过的坑
2、Fragment全解析系列(二):正确的使用姿势
3、Fragment之我的解决方案:Fragmentation
如果你通读了本系列的前两篇,我相信你可以写出大部分场景都能正常运行的Fragment了。如果你想了解更多,那么你可以看看我封装的这个库:Fragmentation。
本篇主要介绍这个库,解决了一些BUG,使用简单,提供实时查看栈视图等实用功能。
源码地址:Github,欢迎Star,Fork。
Demo网盘下载(V_0.9.0)
Demo演示:
单Activity + 多Fragment,项目中有3个Demo。
流式的单Activity+多Fragment:
流式的单Activity+多Fragment
类似微信交互方式的单Activity+多Fragment:(全页面支持滑动返回)
类似微信交互方式的单Activity+多Fragment
类似新版仿知乎交互方式的单Activity+多Frgment:
类似新版仿知乎交互方式的单Activity+多Frgment
Fragmentation
为"单Activity + 多Fragment的架构","多模块Activity + 多Fragment的架构"而生,帮你简化使用过程,轻松解决各种复杂嵌套等问题,修复了官方Fragment库存在的一些BUG。
特性
1、可以快速开发出各种嵌套设计的Fragment App
2、悬浮球/摇一摇实时查看Fragment的栈视图Dialog,降低开发难度
3、增加启动模式、startForResult等类似Activity方法
4、类似Android事件分发机制的Fragment回退方法:onBackPressedSupport(),轻松为每个Fragment实现Back按键事件
5、提供onSupportVisible()等生命周期方法,简化嵌套Fragment的开发过程; 提供统一的onLazyInitView()懒加载方法
6、提供 Fragment转场动画 系列解决方案,动态更换动画
7、更强的兼容性, 解决多点触控、重叠等问题
8、支持SwipeBack滑动边缘退出(需要使用Fragmentation_SwipeBack库,详情README)
通过logFragmentStackHierarchy(TAG)查看Log如何使用
1. 项目下app的build.gradle中依赖:
// appcompat-v7包是必须的
compile 'me.yokeyword:fragmentation:{version}'
// 如果不想继承SupportActivity/Fragment,自己定制Support,可仅依赖:
// compile 'me.yokeyword:fragmentation-core:{version}'
// 如果想使用SwipeBack 滑动边缘退出Fragment/Activity功能,完整的添加规则如下:
compile 'me.yokeyword:fragmentation:{version}'
// swipeback基于fragmentation, 如果是自定制SupportActivity/Fragment,则参照SwipeBackActivity/Fragment实现即可
compile 'me.yokeyword:fragmentation-swipeback:{version}'
2. Activity继承SupportActivity:
// v1.0.0开始,不强制继承SupportActivity,可使用接口+委托形式来实现自己的SupportActivity
public class MainActivity extends SupportActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(...);
// 栈视图等功能,建议在Application里初始化
Fragmentation.builder()
// 显示悬浮球 ; 其他Mode:SHAKE: 摇一摇唤出 NONE:隐藏
.stackViewMode(Fragmentation.BUBBLE)
.debug(BuildConfig.DEBUG)
...
.install();
if (findFragment(HomeFragment.class) == null) {
loadRootFragment(R.id.fl_container, HomeFragment.newInstance()); // 加载根Fragment
}
}
3. Fragment继承SupportFragment:
// v1.0.0开始,不强制继承SupportFragment,可使用接口+委托形式来实现自己的SupportFragment
public class HomeFragment extends SupportFragment {
private void xxx() {
// 启动新的Fragment, 另有start(fragment,SINGTASK)、startForResult、startWithPop等启动方法
start(DetailFragment.newInstance(HomeBean));
// ... 其他pop, find, 设置动画等等API, 请自行查看WIKI
}
}
网友评论
此时这个Fragment的Destory方法走了,但是退出新的Activity时,页面还是显示这个Fragment
Intent loginIntent = new Intent(mActivity, LoginActivity.class);
startActivity(loginIntent);
pop();
startWithPop(SupportFragment fragment)
// 启动目标Fragment,并关闭targetFragment之上的Fragments
startWithPopTo(SupportFragment fragment, Class targetFragment, boolean includeTargetFragment)
大神请问下, startWithPopTo要怎么调用呀, 我都调用不了呀
经过调式,Acitivtity回收之后Fragment依旧走onCreateView onViewCreated等创建方法,我的初始化动作都在这两个方法中,虽然都有走,为什么白屏,测试机,小米5
为什么我项目里用这设置导航按钮会发生错乱?
你现在这个框架的Transaction共享控件动画是只有返回,没有进入的
如果是类似这种写法:
fromFragment.getFragmentManager().beginTransaction()
.replace(R.id.flContainer, toFragment)
.addToBackStack(null)
.addSharedElement(swipeLayout, "Content")
.commit();
就能完美呈现出进入和退出的共享动画
望一起研究!谢谢大神!
但是我在某一个页面还是看到部分重影
或者通过Activity作用域的EventBus来解决,我稍后会上传该辅助Module到Fragmentation
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:380)
at android.support.v4.app.BackStackRecord.add(BackStackRecord.java:375)
at android.support.v4.app.FragmentPagerAdapter.instantiateItem(FragmentPagerAdapter.java:103)
at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:1034)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1182)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1116)
at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1642)
at android.view.View.measure(View.java:19141)
at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:825)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:511)
at android.view.View.measure(View.java:19141)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6245)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:223)
RadioGroup实现吗?
FragmentManager对象不对,使用了错误层级的FM了,所以该FM里可能本来就没有Fragment;
你可以简单复现被杀的场景 : 开发者模式-> 不保留活动 打开,然后回到桌面,再进入,页面白屏一会代表被强杀
你运行下demo看看吧
所以如果第一个Fragment已经是onActivityCreated()时,再点击就会启动第二个。 不必调任何方法的
登录后跳转mainActivity 怎么让其显示的是刚点击跳转的fragment(我的) ,并且数据发生变化呢! 我发现你这个demo 好像实现不了,当我退出登录时候,按home 键会先跳到数据清空的界面然后退出
通知数据变化 如果建议eventbus
可以实现你想要的效果 但是栈结构是需要调整的:
想replace的那个tabFG,不要直接去replace(不要尝试add和replace在同一栈层级混合使用),而是add一个空的父容器FG, replace的是这个父容器fg里的子fg
现在我想请教个问题,看你前两篇文章中提到用viewpager可以不考虑fragment的保存问题,
但是使用viewpager后,fragment生命周期不能正常运行了:getitemposition返回-2时,全部fragment都会在notify后重建,返回-1时,notify后生命周期完全不执行。有没有办法在我remove一个fragment,notify后只销毁我remove的fragment,其他的保持原状呢?
1、拓展性没得比
2、管理数据的方式更优雅:栈的方式
3、内存不足 页面重启时, listview的方式难办~
在你的模仿知乎示例中,第二个Tab页ZhihuSecondFragment类中初始化方法这么写:
loadRootFragment(R.id.fl_second_container, ViewPagerFragment.newInstance());
然后在ViewPagerFragment中再处理ViewPager切换三个Fragment。
为什么不直接在ZhihuSecondFragment类中处理ViewPager呢?这样不是少一层Fragment嵌套吗?
另外再请教个问题,比如ViewPager中有4个Fragment,分别为ABCD,切换到C时A被销毁,切换到D时B被销毁,这个在Fragmention中有办法处理么?
2、同样EventBus,让A去刷新
我理解你的意思,就像BroadCast是给App之间通信使用的,LocalBroadCastManager是App内部的通信使用的, 你想有个东西可以让其在Activity内的Fragment之间通信;
这个之前有考虑过,这方面会再留意下; 因为这个随着业务的复杂会带来一些问题:比如,
如果我这个AC下的某个Fragment需要和另外一个AC的某个Fragment通信,可能又需要不得不使用EventBus这样的库了; 除非这个库可以做到任意宿主AC下的Fragment之间都可以通信~
至于实现,肯定是基于观察者模式的一套方案了;回头考虑下哈
因为基于Fragment间通信的,我感觉应该是必须要的,
又换句话说,如果我单单只是为了这个fragment间通信,就要去引入EventBus(理想状态下,还没有用到过这个),然后为了这个需求,而去引入,会不会说不过去
因为这个是很通用的,无论是平级还是顺序的,都是需要必须的;我看了demo,看到了使用EventBus的来交互的,我个人而言,并不是会选择这个,所以就想问下,是否能F跟F之间来相互通信
1:他们3个是平级的,这个时候我C(可以是A或者是B)操作了,然后要通信其他两个,是否有解决方案
2:如果不是平级的,此时我C处理完数据,然后同时A刷新,此时还是显示C界面的,是否能支持?
Process: me.yokeyword.sample, PID: 21063
java.lang.IllegalStateException: Fragment already active
at android.support.v4.app.Fragment.setArguments(Fragment.java:548)
at me.yokeyword.sample.demo_flow.MainActivity$2.run(MainActivity.java:149)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5438)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629)
但是已经在栈内的Fragment是不能调用其setArugments()的,会报你提示的异常~
看样上次提交我还是得回滚,还是统一用库里的putNewBundle()来传递数据~
.add(R.id.frl_content,homeFragment,homeFragment.getClass().getName())
.hide(homeFragment)
.add(R.id.frl_content, invertFragment,invertFragment.getClass().getName())
.hide(invertFragment)
.add(R.id.frl_content,assetsFragment,assetsFragment.getClass().getName())
.hide(assetsFragment)
.add(R.id.frl_content, moreFragment,moreFragment.getClass().getName())
.hide(moreFragment)
.commit();
getSupportFragmentManager().getFragments().size();
大神,最后一句为啥报空指针异常?我是放在activity的oncreate里的
一方面是demo没太注意这方面 写的比较糙,主要演示一些用法~
另一方面是库中的Fragment都是有背景色的,即使你没设置,这是为了动画和侧划返回显示正常而添加的,所以会多一层,其实是可以接收的~ 侧划功能即使是Activity也是会多一层的
findFragment
_mActivity.getSupportFragmentManager().getFragments();
都无法找到对应fragment 从而 出栈pop 无效
理解能力有限 看不太懂您是如何处理的
@Override
public void onBackToFirstFragment() {
mBottomBar.setCurrentItem(0);
}
你这个是显示指定的fragment,但我现在不知道C的上一个fragment是哪个,(因为有可能现在的流程是A->B->A->C),那要怎么做呢
或者(页面重启)屏幕旋转
Demo里有show/hide同级切换时的懒加载处理BaseLazyMainFragment,还一种懒加载情况时ViewPager时的懒加载,网上可以找到方案。
1、类似QQ那种通过show/hide 控制TabFragment的场景(如果你是在初始化时就把所有TabFragment都加载进来时)
2、ViewPager+Fragment的场景(因为默认FragmentPager(State)Adapter会加载当前Fragment临近的1个Fragment)
比如https://github.com/roughike/BottomBar这个,现在项目里在用
希望博主能回复一下,非常感谢
这个库我看了README,应该是可以的,在加载Tab Fragment时,使用loadMultipleRootFragment(int containerId, int showPosition, SupportFragment... toFragments) 方法加载根部的TabFragment即可
强烈建议用最新的0.7X版本,升级非常大,可以看看库里新的仿知乎Demo,同时新增了wiki,有比较详细的说明。
另外你试下最新的库,看是否还会出现该问题
在设置转场动画时,尽量不要直接在onCreateView里立即进行其他Fragment事务操作,考虑放到主线程的消息队列里(Handler.post)
另外我的Fragmentation库有个滑动返回的拓展库,如果觉得重的话,可以看我的单独的SwipeBackFragment库,地址在发的这篇简书链接里都能找到
fragmentManager.popBackStack();
放在第一行执行,放最后一行执行时,返回结果创建新fragment的话,直接就把新的返回了,
旧的还在。。
不知道是不是我的PopupWindow的使用姿势不对。
pop()时会执行到这个。。。。我汗,你写的好吧。
执行下面
setFramgentResult(RESULT_OK, null);
pop();
我在返回结果后会启动另一个Fragment,这时发现pop掉了新启动的Fragment的
我都是绕过这些坑,直接replace+缓存刷新的。
内存重启也是finish所有activities,重走app流程的。
看来我也该参考下你的方案。感谢分享。
只用Activity的界面,绝大多数场景下,不需要做保存。但是一旦牵扯到Fragment,需要保存的变量的场景就多了很多,这也是Fragment坑多的一个体现,因为需要考虑的变多了
嗯嗯,问题倒不是出在activity和fragment的重启,而是那些static变量的重新初始化。
总不能不用static,也不能所有的都声明成单例。不知道这块你是怎么考虑的。