我正在尝试在Android应用中实现导航标签 . 由于不推荐使用TabActivity和ActivityGroup,我想使用Fragments来实现它 .
我知道如何为每个选项卡设置一个片段,然后在单击选项卡时切换片段 . 但是,如何为每个选项卡分别设置一个后备堆栈?
例如,片段A和B将位于选项卡1下,片段C和D位于选项卡2下 . 启动应用程序时,将显示片段A并选择选项卡1 . 然后片段A可能被片段B替换 . 当选择标签2时,应显示片段C.如果选择了选项卡1,则应再次显示片段B.此时应该可以使用后退按钮显示片段A.
此外,重要的是在旋转设备时保持每个标签的状态 .
BR马丁
12 回答
该框架目前不会自动为您执行此操作 . 您需要为每个选项卡构建和管理自己的后台堆栈 .
说实话,这似乎是一个值得怀疑的事情 . 我无法想象它会产生一个像样的UI - 如果后面的键会根据我的选项卡做不同的事情,特别是如果后面的键也有正常的行为关闭整个活动的时候在顶部堆栈......听起来很讨厌 .
如果您正在尝试构建类似Web浏览器UI的东西,那么获取对用户来说很自然的用户体验将会涉及很多细微的行为调整,这取决于上下文,所以你肯定需要做自己的后台栈管理而不是依赖于框架中的一些默认实现 . 举个例子,尝试注意后面的键如何以各种方式与标准浏览器进行交互 . (浏览器中的每个“窗口”本质上都是一个选项卡 . )
我对这个问题非常迟 . 但是因为这个帖子非常有用,对我有帮助,所以我觉得我最好在这里发两个便士 .
我需要这样的屏幕流程(一个简约的设计,每个标签有2个标签和2个视图),
我过去有同样的要求,我使用
TabActivityGroup
(当时也已弃用)和活动 . 这次我想使用Fragments .所以我就这样做了 .
1.创建基础片段类
应用中的所有片段都可以扩展此Base类 . 如果你想使用像
ListFragment
这样的特殊片段,你也应该为它创建一个基类 . 如果您完整阅读帖子,您将清楚onBackPressed()
和onActivityResult()
的用法 .2.创建一些Tab标识符,可在项目中的任何位置访问
没什么可解释的..
3.好的,主要标签活动 - 请在代码中查看评论..
4. app_main_tab_fragment_layout.xml(万一有兴趣 . )
5. AppTabAFirstFragment.java(标签A中的第一个片段,适用于所有标签的simliar)
这可能不是最完美和正确的方式 . 但它在我的案例中运作得很好 . 此外,我只在纵向模式下有此要求 . 我从来不必在支持两种方向的项目中使用此代码 . 所以不能说我面临什么样的挑战..
编辑:
如果有人想要一个完整的项目,我已将示例项目推送到github .
我们必须实现与您最近为应用描述的完全相同的行为 . 应用程序的屏幕和整体流程已经定义,因此我们必须坚持使用它(这是一个iOS应用程序克隆...) . 幸运的是,我们设法摆脱了屏幕上的后退按钮:)
我们使用TabActivity,FragmentActivities(我们使用片段支持库)和片段的混合物来破解解决方案 . 回顾过去,我很确定这不是最好的架构决策,但我们设法让事情发挥作用 . 如果我不得不再次这样做,我可能会尝试做一个更基于活动的解决方案(没有片段),或尝试只有一个活动的标签,让所有其余的视图(我发现更多可重复使用而不是整体活动) .
因此要求是在每个选项卡中都有一些选项卡和可嵌套屏幕:
等等...
所以说:用户在标签1中开始,从屏幕1导航到屏幕2然后导航到屏幕3,然后他切换到标签3并从屏幕4导航到6;如果切换回标签1,他应该再次看到屏幕3,如果他按下了回来他应该回到屏幕2;再回来,他在屏幕1;切换到标签3,然后再次进入屏幕6 .
应用程序中的主要Activity是MainTabActivity,它扩展了TabActivity . 每个选项卡都与一个活动相关联,比如说ActivityInTab1,2和3.然后每个屏幕都是一个片段:
每个ActivityInTab一次只保存一个片段,并且知道如何将一个片段替换为另一个片段(与ActvityGroup几乎相同) . 很酷的是,通过这种方式为每个标签保留单独的后备堆很容易 .
每个ActivityInTab的功能都完全相同:知道如何从一个片段导航到另一个片段并维护一个后台,所以我们将它放在一个基类中 . 我们简单地称它为ActivityInTab:
activity_in_tab.xml就是这样的:
如您所见,每个选项卡的视图布局都是相同的 . 那是因为它只是一个名为内容的FrameLayout,它将保存每个片段 . 片段是具有每个屏幕视图的片段 .
只是为了奖励积分,我们还添加了一些小代码,以便在用户按下Back时显示确认对话框,并且没有更多片段可以返回:
这就是设置 . 正如您所看到的,每个FragmentActivity(或者只是Android中的Activity> 3)都会使用它自己的FragmentManager来处理所有后向堆叠 .
像ActivityInTab1这样的活动非常简单,它只会显示它的第一个片段(即屏幕):
然后,如果一个片段需要导航到另一个片段,它必须做一些讨厌的演员...但它并没有那么糟糕:
所以's pretty much it. I' m非常确定这不是一个非常规范(并且大多数肯定不是很好)的解决方案,所以如果你能指出一些链接或材料来解释哪种是Android方法来解决这个问题,我会很感激(制表符,标签中的嵌套屏幕等) . 请在评论中随意撕开这个答案:)
作为这个解决方案不是很好的标志,最近我不得不在应用程序中添加一些导航功能 . 一些奇怪的按钮,应该将用户从一个选项卡带到另一个选项卡并进入嵌套屏幕 . 以编程方式执行此操作是一个痛苦的屁股,因为谁知道谁的问题和处理何时实际实例化和初始化的片段和活动 . 我认为如果这些屏幕和标签都只是视图真的会更容易 .
最后,如果您需要更改方向更改,则使用setArguments / getArguments创建片段非常重要 . 如果在片段的构造函数中设置实例变量,则会被搞砸 . 但幸运的是,这很容易解决:只需在构造函数中的setArguments中保存所有内容,然后在onCreate中使用getArguments检索这些内容以使用它们 .
存储对片段的强引用不是正确的方法 .
FragmentManager提供putFragment(Bundle, String, Fragment)和saveFragmentInstanceState(Fragment) .
任何一个都足以实现一个后端堆栈 .
使用
putFragment
,而不是替换片段,分离旧片段并添加新片段 . 这就是框架对替换添加到backstack的事务所做的工作 .putFragment
存储当前活动碎片列表的索引,并且这些碎片在方向更改期间由框架保存 .第二种方法,使用
saveFragmentInstanceState
,将整个片段状态保存到Bundle,允许您真正删除它,而不是分离 . 使用这种方法可以更容易地操作后堆栈,因为您可以随时弹出Fragment .我在这个用例中使用了第二种方法:
我不希望用户通过按后退按钮从第三个屏幕返回到“注册”屏幕 . 我也在他们之间翻转动画(使用
onCreateAnimation
),所以hacky解决方案将无法工作,至少没有用户明确注意到某些事情是不对的 .这是自定义backstack的有效用例,执行用户期望的...
使用ChildFragmentManager可以轻松实现这一点
这是与相关项目有关的帖子 . 看一看,
http://tausiq.wordpress.com/2014/06/06/android-multiple-fragments-stack-in-each-viewpager-tab/
免责声明:
我觉得这是发布一个相关解决方案的最佳位置,我已经研究过类似的问题,似乎是非常标准的Android内容 . 它不会为每个人解决问题,但它可能有所帮助 .
如果是主要的您的片段之间的差异只是备份它们的数据(即,没有很多大的布局差异),那么您可能不需要实际替换片段,而只需交换基础数据并刷新视图 .
以下是此方法的一个可能示例的说明:
我有一个使用ListViews的应用程序 . 列表中的每个项目都是具有一定数量子项的父项 . 点击该项目时,需要在与原始列表相同的ActionBar选项卡中打开这些子项的新列表 . 这些嵌套列表具有非常相似的布局(这里或那里可能有一些条件调整),但数据不同 .
这个应用程序在初始父列表下面有几层后代,当用户尝试访问第一个以外的任何特定深度时,我们可能有也可能没有来自服务器的数据 . 因为列表是从数据库游标构造的,并且片段使用游标加载器和游标适配器用列表项填充列表视图,所以在注册点击时需要发生的是:
1)使用适当的“to”和“from”字段创建一个新的适配器,该字段将匹配添加到列表中的新项目视图和新游标返回的列 .
2)将此适配器设置为ListView的新适配器 .
3)根据单击的项构建新URI,并使用新URI(和投影)重新启动游标加载器 . 在此示例中,URI通过从UI传递的选择args映射到特定查询 .
4)当从URI加载新数据时,将与适配器关联的光标交换到新光标,然后列表将刷新 .
由于我们没有使用事务,因此没有与此相关的backstack,因此您必须构建自己的事务,或者在退出层次结构时反向播放查询 . 当我尝试这个时,查询速度足够快,我只是在oNBackPressed()中再次执行它们直到我处于层次结构的顶部,此时框架再次接管后退按钮 .
如果您发现自己处于类似情况,请务必阅读文档:http://developer.android.com/guide/topics/ui/layout/listview.html
http://developer.android.com/reference/android/support/v4/app/LoaderManager.LoaderCallbacks.html
我希望这可以帮助别人!
我有完全相同的问题,并实现了一个开源github项目,涵盖堆叠选项卡,后退和向上导航,并经过充分测试和记录:
https://github.com/SebastianBaltesObjectCode/PersistentFragmentTabs
我想建议我自己的解决方案,以防有人正在寻找并想要尝试为他/她的需求选择最好的解决方案 .
https://github.com/drusak/tabactivity
创建库的目的非常平庸 - 像iPhone一样实现它 .
主要优点:
使用带有TabLayout的android.support.design库;
每个选项卡都有自己的堆栈使用FragmentManager(不保存片段的引用);
支持深层链接(当你需要打开特定标签和特定片段的级别时);
保存/恢复选项卡的状态;
标签中片段的自适应生命周期方法;
非常容易实现满足您的需求 .
希望它可以帮助某人 .
谢谢!
这是一个复杂的问题,因为Android只处理1个后栈,但这是可行的 . 我花了几天时间创建一个名为Tab Stacker的库,它完全符合您的要求:每个选项卡的片段历史记录 . 它是开源的,完整记录,可以轻松地与gradle包含在内 . 你可以在github上找到这个库:https://github.com/smart-fun/TabStacker
您还可以下载示例应用程序,以查看该行为是否符合您的需求:
https://play.google.com/apps/testing/fr.arnaudguyon.tabstackerapp
如果您有任何疑问,请随时发送邮件 .
简单的解决方案:
每次更改选项卡/根视图调用时:
它将清除BackStack . 请记住在更改根片段之前调用此方法 .
并添加片段:
注意
.addToBackStack(null)
和transaction.add
可以例如用transaction.replace
改变 .这个帖子非常有趣和有用 .
感谢Krishnabhadra的解释和代码,我使用你的代码并进行了一些改进,允许从更改配置(主要是旋转)中保留堆栈,currentTab等 .
在真正的4.0.4和2.3.6设备上测试,未在模拟器上测试
我在“AppMainTabActivity.java”上更改了这部分代码,其余部分保持不变 . 也许Krishnabhadra会加上这个在他的代码上 .
恢复关于创建的数据:
保存变量并放入Bundle:
如果存在以前的CurrentTab,请设置此项,否则创建一个新的Tab_A:
我希望这有助于其他人 .
我建议不要使用基于HashMap的backstack>“不要保持活动”模式中有很多错误 . 如果您深入到片段的堆栈中,它将无法正确恢复状态 . 并且还将嵌套在嵌套的 Map 片段中(使用exeption:Fragment没有为ID找到视图) . Coz HashMap>在background \ foreground app之后将为null
我使用fragment的backstack优化上面的代码
It is bottom TabView
Main activity Class
tags_activity.xml
<
tags_icon.xml