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:
All of these advocate a multiple Activity
approach:
Activity
with multiple Fragment
sFragment
s among multiple Activity
s.I thought of another approach - a single Activity
one:
Activity
with all the Fragment
s in it.Fragment
(s) (using FragmentTransaction.show()
/ FragmentTransaction.hide()
) .To illustrate with the same "News article list/article contents" example that Android developer guide uses:
News
activity containing both an ArticleListFragment
and ArticleReaderFragment
.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.
Imagine an app which can display up to three "panes" at a time on the screen. Further, these are the factors to consider:
For simplicity, lets keep TV screens out of the discussion.
Now, translating this to design:
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:
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?
While creating a Fragment we must use onCreateView() callback to define the layout and in order to run a Fragment. Here the inflater parameter is a LayoutInflater used to inflate the layout, container parameter is the parent ViewGroup (from the activity's layout) in which our Fragment layout will be inserted.
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:
Activity
: NewsActivityTitlesListFragment
and DetailsFragment
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>
<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.
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:
super.onBackPressed()
;TitlesListFragment
, call super.onBackPressed()
;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.
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:
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:
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!
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?
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
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With