java 片段设计:通过在单个 Activity 中显示/隐藏片段来适应多个屏幕布局?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10051962/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Fragment design: Adapting to multiple screen layouts by showing/hiding fragments within a single Activity?
提问by curioustechizen
I am trying to understand how to use Fragments to create apps that adapt well to multiple screens and layouts. I have studied a few examples:
我试图了解如何使用 Fragment 来创建能够很好地适应多个屏幕和布局的应用程序。我研究了几个例子:
- The Fragmentsdocument on Android Developer guide.
- Google IO app
- Fragments sample from ActionBar Sherlock.
- Android 开发者指南中的Fragments文档。
- 谷歌 IO 应用
- 来自ActionBar Sherlock 的片段示例。
All of these advocate a multiple Activity
approach:
所有这些都提倡采用多种Activity
方法:
- On a large screen, display a single
Activity
with multipleFragment
s - On a smaller screen, split up the
Fragment
s among multipleActivity
s.
- 在大屏幕上,显示一个
Activity
带有多个Fragment
s - 在较小的屏幕上,将
Fragment
s拆分为多个Activity
s。
I thought of another approach - a single Activity
one:
我想到了另一种方法 - 一个方法Activity
:
- Have a single
Activity
with all theFragment
s in it. - Depending on the screen size and orientation, show/hide the appropriate
Fragment
(s) (usingFragmentTransaction.show()
/FragmentTransaction.hide()
) .
- 有一个
Activity
包含所有Fragment
s的单曲。 - 根据屏幕尺寸和方向,显示/隐藏适当的
Fragment
(s) (使用FragmentTransaction.show()
/FragmentTransaction.hide()
)。
To illustrate with the same "News article list/article contents" example that Android developer guide uses:
使用 Android 开发者指南使用的相同“新闻文章列表/文章内容”示例来说明:
- Have the
News
activity containing both anArticleListFragment
andArticleReaderFragment
. - On a tab, both fragments are always displayed.
- On a phone, the
ArticleReaderFragment
is initially hidden. When an article is selected from the list, theArticleListFragment
is hidden and theArticleReaderFragment
is shown.
- 让
News
活动同时包含 anArticleListFragment
和ArticleReaderFragment
。 - 在选项卡上,始终显示两个片段。
- 在手机上,
ArticleReaderFragment
最初是隐藏的。当从列表中选择一篇文章时,ArticleListFragment
隐藏并ArticleReaderFragment
显示 。
Has anybody used a similar approach? Are there any practical downsides this method might have? Does it seem better/worse compared to the multiple-activity way? For example, fragments cannot be shown/hidden in XML - one mustuse FragmentTransaction
for this.
有没有人使用过类似的方法?这种方法可能有什么实际缺点吗?与多种活动方式相比,它看起来更好/更糟吗?例如,片段不能在 XML 中显示/隐藏 -必须FragmentTransaction
为此使用。
EDIT 1: Description of a Hypothetical Scenario
编辑 1:假设情景的描述
Imagine an app which can display up to three "panes" at a time on the screen. Further, these are the factors to consider:
想象一个应用程序,它可以在屏幕上一次最多显示三个“窗格”。此外,这些是需要考虑的因素:
- A phone can display only one pane at a time (regardless of portrait/landscape orientation)
- A 7-inch tablet can display 2 panes, split vertically in Portrait, and split horizontally in landscape mode.
- A 10+ inch tablet can display 2 panes, split vertically in Portrait; 3 panes split horizontally in landscape.
- 一部手机一次只能显示一个窗格(无论纵向/横向)
- 一个 7 英寸的平板电脑可以显示 2 个窗格,纵向拆分,横向拆分。
- 10 英寸以上的平板电脑可以显示 2 个窗格,纵向拆分;3 个窗格在横向中水平拆分。
For simplicity, lets keep TV screens out of the discussion.
为简单起见,让我们不要讨论电视屏幕。
Now, translating this to design:
现在,将其转化为设计:
- We have three fragments: Frag1, Frag2 and Frag3.
- In the simplest case, All three fragments are in a single Activity (lets call it ActivityA). This is the 10-inch, landscape case.
- The other "simple" case is when each Fragment is in its own Activity - ActivityA contains Frag1; ActivityB contains Frag2 and ActivityC contains Frag3.
- 我们有三个片段:Frag1、Frag2 和 Frag3。
- 在最简单的情况下,所有三个片段都在一个活动中(我们称之为 ActivityA)。这是 10 英寸的横向机箱。
- 另一个“简单”的情况是每个 Fragment 都在它自己的 Activity 中——ActivityA 包含 Frag1;ActivityB 包含 Frag2,ActivityC 包含 Frag3。
So far, we have not considered anything which is significantly different from the News Reader example presented in the Android developer guide. The only major difference is having threefragments instead of two.
到目前为止,我们还没有考虑任何与 Android 开发人员指南中提供的新闻阅读器示例显着不同的内容。唯一的主要区别是有三个片段而不是两个。
Now, the case of 7-inch tabs which can accommodate only 2 fragments. How would this work? Note that there are two combinations possible here:
现在,只能容纳 2 个碎片的 7 英寸标签的情况。这将如何运作?请注意,这里有两种可能的组合:
- Frag1 and Frag2 are being displayed.
- Frag2 and Frag3 are being displayed.
- 正在显示 Frag1 和 Frag2。
- 正在显示 Frag2 和 Frag3。
I'm just unable to wrap my head around this. Do I do all of this within ActivityA? Do I just create an altogether new ActivityD? How many layouts would I need to create (I counted around 8)? Isn't it too many permuations?
我只是无法解决这个问题。我是否在 ActivityA 中执行所有这些操作?我只是创建一个全新的 ActivityD 吗?我需要创建多少个布局(我数了大约 8 个)?是不是太多的排列?
I do realize that the single-activity approach I proposed above might also not be a good fit for this scenario - since showing/hiding fragments in itself is non-trivial.
我确实意识到我上面提出的单一活动方法也可能不适合这种情况 - 因为显示/隐藏片段本身并不简单。
Any suggestions on how to handle this without getting overwhelmed with layouts and combinations?
关于如何处理这个问题而不会被布局和组合淹没的任何建议?
采纳答案by curioustechizen
Thisanswer by @Taylor Clark was pretty informative. However, it wasn't an actual sharing of experience with using the single-activity approach as I asked in my original question. I set out to modify the News Reader example from the Android developer guide to use the single-activity approach and came up with a workable solution.
@Taylor Clark 的这个回答非常有用。但是,这并不是我在最初的问题中提出的使用单一活动方法的实际经验分享。我着手修改 Android 开发人员指南中的新闻阅读器示例以使用单一活动方法,并提出了一个可行的解决方案。
What remains to be seen is what are the use cases where this approach is preferable over the multiple-activity method (or whether there are any such cases at all). Also, I haven't looked in detail about the 3-pane scenario described in Edit 1 of my question.
有待观察的是,在哪些用例中这种方法优于多活动方法(或者是否存在任何此类情况)。此外,我还没有详细了解我的问题的编辑 1 中描述的 3 窗格场景。
I hope to post my entire project shortly, but here is a quick overview of how I went about it:
我希望尽快发布我的整个项目,但这里是我如何进行的快速概述:
- Single
Activity
: NewsActivity - Two Fragments:
TitlesListFragment
andDetailsFragment
- Both fragments are always present in
NewsActivity
. Depending on the current dual-pane-ness, I show/hide the appropriate fragment.
- 单
Activity
:新闻活动 - 两个片段:
TitlesListFragment
和DetailsFragment
- 这两个片段始终存在于
NewsActivity
. 根据当前的双窗格,我显示/隐藏适当的片段。
Some problems I came across:
我遇到的一些问题:
Designating a Layout as Dual-pane or not:
是否将布局指定为双窗格:
In the original News Reader example, dual-pane layouts have a FrameLayout
for holding the news Details. We figure out whether we are currently in a dual-pane layout by testing for the existence of this Frame Layout.
在最初的新闻阅读器示例中,双窗格布局有一个FrameLayout
用于保存新闻详细信息。我们通过测试此 Frame Layout 的存在来确定我们当前是否处于双窗格布局中。
However, in my solution, both fragments are always present in all layouts. I hacked this by including a View
with id dualPane
and android:visibility="gone"
in those layouts that I want to be dual-pane and omitting this view in the single-pane layout. Then, it was a matter of
但是,在我的解决方案中,这两个片段始终存在于所有布局中。我通过在那些我想要双窗格的布局中包含一个View
with iddualPane
并android:visibility="gone"
在单窗格布局中省略此视图来解决这个问题。然后,这是一个问题
mDualPane = findViewById(R.id.dualPane)!=null;
mDualPane = findViewById(R.id.dualPane)!=null;
EDIT:
编辑:
There are better ways to designate dual pane-ness than having a dummy view. The one I prefer is to create a boolean resource. For example, I have a config.xml
as follows:
有比使用虚拟视图更好的方法来指定双重窗格。我更喜欢的是创建一个布尔资源。例如,我有一个config.xml
如下:
<resources>
<bool name="dual_pane">false</bool>
</resources>
I can then place additional config.xml
files in folders like values-xlarge-land
, values-port
or values-sw600dp
etc and adjust the boolean value to true
or false
as I desire.
然后,我可以将其他config.xml
文件放在values-xlarge-land
,values-port
或values-sw600dp
等文件夹中,并将布尔值调整为true
或 ,false
如我所愿。
Then, in the code it is a matter of getResources().getBoolean(R.bool.dual_pane);
然后,在代码中它是一个问题 getResources().getBoolean(R.bool.dual_pane);
Closing the Details Fragment
关闭详细信息片段
This was a problem of differentiating between the Activity
close and the Fragment
close. In the end, I had to override onBackPressed()
as follows:
这是区分Activity
收盘价和Fragment
收盘价的问题。最后,我不得不重写onBackPressed()
如下:
- In dual-pane mode, just call
super.onBackPressed()
; - In single-pane mode, if we are in
TitlesListFragment
, callsuper.onBackPressed()
; - In single-pane mode, if we are in
DetailsFragment
, then treat it as closing the fragment. This means hiding it and showing theTitlesListFragment
.
- 在双窗格模式下,只需调用
super.onBackPressed()
; - 在单窗格模式下,如果我们在 中
TitlesListFragment
,则调用super.onBackPressed()
; - 在单窗格模式下,如果我们在 中
DetailsFragment
,则将其视为关闭片段。这意味着隐藏它并显示TitlesListFragment
.
This is not ideal, but it is the best I could come up with.
这不是理想的,但这是我能想到的最好的。
EDIT:
编辑:
Based on the suggestion by @SherifelKhatib in the comments, there is a much cleaner way to handle back-button presses: Simply add to the Fragment backstack, the transaction of showing/hiding the details fragment. That way, when you press the back button, the fragment transaction is reversed. You can also pop the backstack manually if you wish to do so on other button clicks.
根据@SherifelKhatib 在评论中的建议,有一种更简洁的方法来处理后退按钮按下:只需将显示/隐藏细节片段的事务添加到 Fragment backstack。这样,当您按下后退按钮时,片段交易就会被逆转。如果您希望在其他按钮点击时这样做,您也可以手动弹出 backstack。
回答by Taylor Clark
Generally applications are split into Activities because each represents a specific "thing" that a user can do. For example, in an email application a defined set of actions could be:
通常,应用程序被拆分为活动,因为每个活动代表用户可以执行的特定“事情”。例如,在电子邮件应用程序中,一组定义的操作可以是:
- Viewing a list of messages
- Viewing a message's detail
- Composing a reply to an email.
- 查看消息列表
- 查看消息的详细信息
- 撰写对电子邮件的回复。
Each action could be its own Activity that shows a single (or set) of Fragments. However, if you had the screen real-estate it would make sense to combine the viewing of a message list AND the message detail into a single Activity that can show/hide the "detail view" Fragment. Essentially, Fragments allow you to show the user multiple 'activities' at one time.
每个动作都可以是它自己的活动,显示单个(或一组)片段。但是,如果您拥有屏幕空间,那么将消息列表的查看和消息详细信息合并到一个可以显示/隐藏“详细信息视图”片段的活动中是有意义的。本质上,片段允许您一次向用户显示多个“活动”。
Things to consider when making your decision:
做出决定时需要考虑的事项:
- Fragments specified in xml cannot be supplied arguments
- If your decision is based on screen orientation you can always use resource qualifiers to point to another layout with more/less Fragments (ex. layout-land-large)
- Fragments cannot be maintained Activities
- Are the Fragments working together to provide the user a streamlined experience?
- Is there a time where one of your Fragments will be gone for good? If so, maybe it's time for a new Activity
- Go with your gut. If its "natural" for you application to swap-out fragments, go for it. Just remember that if you find yourself doing it a lot, maybe a separate Activity is a more appropriate solution
- 无法为 xml 中指定的片段提供参数
- 如果您的决定是基于屏幕方向,您始终可以使用资源限定符来指向具有更多/更少 Fragment 的另一个布局(例如 layout-land-large)
- 片段无法维护活动
- 这些片段是否协同工作以向用户提供简化的体验?
- 有没有时候你的一个 Fragment 会永远消失?如果是这样,也许是时候创建一个新的 Activity
- 跟着你的直觉走。如果您的应用程序交换片段是“自然的”,那就去吧。请记住,如果您发现自己经常这样做,也许单独的 Activity 是更合适的解决方案
I've usually employed the "multiple Fragment approach" for tablet versions of applications and a "one Fragment per Activity" for phones, which sounds in-line with your first approach listed. Not that the second doesn't have a time and place, but I could see the implementation getting messy quick!
我通常对平板电脑版本的应用程序采用“多片段方法”,对手机采用“每个活动一个片段”,这听起来与您列出的第一种方法一致。并不是说第二个没有时间和地点,但我可以看到实现很快变得混乱!
Sorry for the wordy reply! Perhaps you could tell more about your specific use case? Hope this helps!
抱歉回复太啰嗦!也许您可以详细介绍一下您的特定用例?希望这可以帮助!
Response to Question Edit One
对问题的回应编辑一
Here's how I imagine your application project could be setup:
这是我想象您的应用程序项目可以设置的方式:
Source files:
源文件:
yourapp.package.phone: NewsActivity1, NewsActivity2, NewsActivity3
yourapp.package.phone: NewsActivity1, NewsActivity2, NewsActivity3
yourapp.package.tablet: NewsMultipaneActivity
yourapp.package.tablet:NewsMultipaneActivity
Resources
资源
layout/
布局/
activity_news.xml- phone version, only includes Fragment1
activity_news_detail.xml- phone version, only includes Fragment2
activity_news_<something>.xml- phone version, only includes Fragment3
layout-large/
布局大/
activity_news.xml- 7" tablet version, includes Fragment2 and an empty fragment container. Split vertically
layout-large-land/
布局-大地/
activity_news.xml- same as layout-large, but with the split being horizontally
layout-xlarge-land/
布局-大-土地/
activity_news.xml- 10"+ tablet version, contains all three fragments split horizontally
So what happens here?
那么这里会发生什么呢?
- If your app is running on a phone, start NewsActivity1
- If you app is runnin on a tablet, start NewsMultipaneActivity
- 如果您的应用在手机上运行,请启动 NewsActivity1
- 如果您的应用程序在平板电脑上运行,请启动 NewsMultipaneActivity
Android will swap layouts for you, based on screen size and orientation as long as the file names are the same. Because Fragment2 will always be displayed, you can 'hard code' it into your tablet layouts. Then you can use FragmentTransactions to switch between Fragment1 and Fragment3 in the container as necessary
只要文件名相同,Android 就会根据屏幕大小和方向为您交换布局。由于 Fragment2 将始终显示,您可以将其“硬编码”到您的平板电脑布局中。然后就可以根据需要使用 FragmentTransactions 在容器中的 Fragment1 和 Fragment3 之间切换
Be sure to check out http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResourcesto see how you can take advantage of resource qualifiers to mitigate some of the headaches of different screens and orientations
请务必查看http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources以了解如何利用资源限定符来减轻不同屏幕和方向的一些麻烦
回答by smok
Thank you for this question and your insights. I used multiple-activity approach until I stepped into a problem. My app is like the doc-example news reader. Consider this scenario:
感谢您提出这个问题和您的见解。在遇到问题之前,我使用了多种活动的方法。我的应用程序就像文档示例新闻阅读器。考虑这个场景:
- (initial state) I'm using a tablet in landscape mode (two-pane), activity A is displaying article list on the left and some article on the right.
- I rotate the device, now activity A is in single-pane mode and displays a list of articles.
- I click on a list item, activity B starts with aritcle opened.
- Now I rotate the device back to landscape mode.
- (初始状态)我在横向模式下使用平板电脑(两窗格),活动 A 在左侧显示文章列表,在右侧显示一些文章。
- 我旋转设备,现在活动 A 处于单窗格模式并显示文章列表。
- 我单击一个列表项,活动 B 以打开的文章开始。
- 现在我将设备旋转回横向模式。
What's happening? Activity B is displaying an article full-sized, while of course I want to go back to two panes. I'm not sure how to solve this nicely. Of course activity B may check for multi-pane mode and finish itself, and activity A may check what was the last article displayed... That looks ugly. Thoughts?
发生了什么?活动 B 正在显示一篇全尺寸的文章,而我当然想回到两个窗格。我不确定如何很好地解决这个问题。当然,活动 B 可能会检查多窗格模式并自行完成,而活动 A 可能会检查最后显示的文章是什么……这看起来很难看。想法?
PS I have a suspicion that GMail app uses single-activity approach. I don't see any activity transition when I select an e-mail from the list. It also behaves properly in the scenario I described.
PS 我怀疑 GMail 应用程序使用单一活动方法。当我从列表中选择电子邮件时,我没有看到任何活动转换。它在我描述的场景中也表现得很好。
回答by Lic
In this article http://developer.android.com/guide/practices/tablets-and-handsets.html#FragmentsGoogle says:
在这篇文章http://developer.android.com/guide/practices/tablets-and-handsets.html#Fragments谷歌说:
"The approach you choose depends on your design and personal preferences."
“您选择的方法取决于您的设计和个人喜好。”
and
和
"Other times, however, dynamically swapping fragments for your handset design can make your code more complicated, because you must manage all the fragment combinations in the activity's code (rather than use alternative layout resources to define fragment combinations) and manage the back stack of fragments yourself (rather than allow the normal activity stack to handle back-navigation)."
“然而,在其他时候,为您的手机设计动态交换片段可能会使您的代码更加复杂,因为您必须管理活动代码中的所有片段组合(而不是使用替代布局资源来定义片段组合)并管理自己分段(而不是让正常的活动堆栈来处理反向导航)。”