java - Fragment design: Adapting to multiple screen layouts by showing/hiding fragments within a single Activity?
up vote 38 down vote favorite 15 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: The Fragments document on Android Developer guide. Google IO app Fragments sample from ActionBar Sherlock. All of these advocate a multiple Activity approach: On a large screen, display a single Activity with multiple Fragments On a smaller screen, split up the Fragments among multiple Activitys. I thought of another approach - a single Activity one: Have a single Activity with all the Fragments in it. Depending on the screen size and orientation, show/hide the appropriate Fragment(s) (using FragmentTransaction.show() / FragmentTransaction.hide()) . To illustrate with the same "News article list/article contents" example that Android developer guide uses: Have the News activity containing both an ArticleListFragment and ArticleReaderFragment. On a tab, both fragments are always displayed. On a phone, the ArticleReaderFragment is initially hidden. When an article is selected from the list, the ArticleListFragment is hidden and the ArticleReaderFragment is shown. 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 must use FragmentTransaction for this. EDIT 1: Description of a Hypothetical Scenario 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. 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. 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 three fragments instead of two. 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: Frag1 and Frag2 are being displayed. Frag2 and Frag3 are being displayed. 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? 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? java android android-layout android-fragments
this question edited Apr 12 '12 at 3:53 asked Apr 7 '12 at 5:06 curioustechizen 6,743 4 42 94 I would personally use as less activities as possible, given that this question exposes exactly the mess of API that Activities/Fragments is. Instead of transform activities and allow more than one to be shown at the same time, the Android devs injected these "pseudo activities" called Fragments, which have their own lifecycle "kind of tied" to an activity (but not really in all the cases). So dealing with less activities gives you, in my opinion, the closest you can get to imagining a nice world where you only rely on your Fragment(s). – Martin Marconcini Dec 5 '13 at 20:28 @MartínMarconcini That is the opinion I am leaning towards of late :-). This works fine until you come across nested fragments and a few gotchas with those. – curioustechizen Dec 6 '13 at 10:30 Well yeah, nested fragments are a hack on top of a hack, but they "work", kinda. :) – Martin Marconcini Dec 6 '13 at 21:39 One thing I will say, fragments load alot faster than activities. – mattblang May 13 '14 at 18:14
| 4 Answers
up vote 15 down vote ---Accepted---Accepted---Accepted---
This answer 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. 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. 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 and DetailsFragment Both fragments are always present in NewsActivity. Depending on the current dual-pane-ness, I show/hide the appropriate fragment. 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. 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 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: <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. Then, in the code it is a matter of 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: In dual-pane mode, just call super.onBackPressed(); In single-pane mode, if we are in TitlesListFragment, call super.onBackPressed(); In single-pane mode, if we are in DetailsFragment, then treat it as closing the fragment. This means hiding it and showing the 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.
this answer edited yesterday Community ♦ 1 1 answered Apr 16 '12 at 17:48 curioustechizen 6,743 4 42 94 1 Can't hiding/showing a fragment be a Transaction? Thus, this might exclude the need of handling onBackPressed()? – Sherif elKhatib Oct 29 '12 at 10:07 @SherifelKhatib From the top of my head, I cannot find any fault with your proposal :) .. but I guess I should try it out - only then we'll know. – curioustechizen Oct 29 '12 at 11:56 How does this approach handle Intents and Bundles? If the application is split between multiple activities it's easy to programmatically invoke an intent for a certain list element. Does your multiplePaneActivity act on those scenarios? If so, then how? – Rekin Feb 19 '14 at 14:38 @Rekin it is easy to "convert" intent based communication between activities into bundle based communication between fragments. Buy I suspect that's not what you're asking about. Could you put up some example code somewhere describing your question in more detail? – curioustechizen Feb 19 '14 at 16:29
| up vote 10 down vote +50 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 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.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 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 Be sure to check out http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources to see how you can take advantage of resource qualifiers to mitigate some of the headaches of different screens and orientations
this answer edited Apr 12 '12 at 21:43 answered Apr 11 '12 at 4:21 Taylor Clark 530 2 10 The original thought behind this question was - how "scalable" is the "separate Activity" approach? Imagine a file browser app. On a phone, I display contents of one folder at a time. On a 7-inch tablet in landscape, I can show two panes. On a 10-inch tab: 3 panes. How about a TV screen? Probably drill-down 5-6 panes side-by-side? I realize that my single-activity approach does little to address this scalability concern. But, there's the headache associated with saving activity state while switching between activities. This can be mitigated if everything happens within a single Activity – curioustechizen Apr 11 '12 at 6:15 The key here is how your application functions. Remember that Activities are supposed to represent a single, focused thing a user can do. – Taylor Clark Apr 11 '12 at 19:50 [Accidentally submitted the comment] I tend to think that multiple activities, each with a maximum of, say, 5 fragments would be the most scalable solution. At some point there are diminishing returns to presenting more data to the user at one time. Think about Windows Explorer for example. Each window you can open can be thought of as a fragment. Yes, you can open up 10 file browsrs at once, but things would become cluttered and confusing quickly. – Taylor Clark Apr 11 '12 at 20:14 I see your point about diminishing returns. I still don't see how to predictably work with multiple activities and fragments without running into way too many combinations. Updated my question with a hypothetical 3-pane scenario. – curioustechizen Apr 12 '12 at 3:55 see edit above. – Taylor Clark Apr 13 '12 at 5:42 | show 1 more comment up vote 1 down vote 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. 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? 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.
this answer edited Dec 5 '13 at 20:02 answered Dec 5 '13 at 19:52 smok 1,250 8 8 Your problem is what Google engineers refer to as "rotational stability". The way I see it - ActivityB is a portrait-mode-only Activity. It should never be displayed in a dual-pane configuration. So basically the solution you described is the way to go . It certainly isn't ugly. – curioustechizen Dec 6 '13 at 10:28 Hmm where did you see that "rotational stability" term? I can see your point with "never displayed activity", but the problem is that Android platform does not allow me to enforce that rule in a beautiful way - the activity is just recreated when I switch to landscape mode in Activity B. Any solution in this case looks like a hack to me. – smok Dec 6 '13 at 13:19 I remember it being used either in a Google IO talk or in Android Design in Action (can't quite recollect which one). I agree that the "finish itself if no longer required" behavior of Activity B may seem like a hack. But this is how even some of the samples provided by Google work. – curioustechizen Dec 6 '13 at 14:48
| up vote 1 down vote In this article http://developer.android.com/guide/practices/tablets-and-handsets.html#Fragments Google says: "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)."
this answer edited Dec 14 '15 at 9:42 answered Dec 13 '15 at 10:02 Lic 60 2 12
- 2Android APK反编译详解（附图）
- 3JAVA用TCP 实现反向连接屏幕监视
- 5JAVA 正则表达式 （超详细）