Android - handling orientation changes when using fragments - android

I made two layout files - one for portrait and one for landscape. Here for portrait:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<fragment
android:id="#+id/fragment_newslist"
android:name="com.app.NewsListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >
</fragment>
</LinearLayout>
Here for landscape:
<fragment
android:id="#+id/fragment_newslist"
android:name="com.app.NewsListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >
</fragment>
<fragment
android:id="#+id/fragment_viewnews"
android:name="com.app.ViewNewsFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" >
</fragment>
Then I created an Activity which loads the layout in the onCreate() method. So far, this works fine of course. This Activity doesn't contain more code than that.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news);
}
Inside the NewsListFragment class I am checking if the ViewNewsFragment is available. If not and the user tapped a ListItem a new Activity (that is ViewNewsActiviy) will be started. If it is available the data will show in the existing fragment. So there are two classes: 1. ViewNewsActivity and 2. ViewNewsFragment
But what I actually want is to change to layout on orientation changes. When the device is turned from portrait to landscape I want to have the typical Dual-Pane layout and if it's turned from landscape to portrait I want to show the list solely and the details should be viewed as separate "view".
But how to do this? Till now it works fine when you start the app either in landscape or in portrait. But when you change the orientation the layout remains as initially set.
I really appreciate any help :)!
Thank you very much!
Jens

But how to do this? Till now it works fine when you start the app either in landscape or in portrait. But when you change the orientation the layout remains as initially set.
Android will automatically destroy and recreate your activity on an orientation change, and so each call to onCreate() will get the right layout.
If that is not happening for you, then you did something to stop it, such as by adding an android:configChanges attribute to the <activity> in the manifest.

Related

SlidingUpPanelLayout problem with visibility

I have a problem with SlidingUpPanelLayout. My view is build like that:
<com.sothree.slidinguppanel.SlidingUpPanelLayout xmlns:sothree="http://schemas.android.com/apk/res-auto"
android:id="#+id/list_sliding_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top"
sothree:umanoDragView="#+id/dragView"
sothree:umanoOverlay="true"
sothree:umanoPanelHeight="#dimen/filtering_list_closed_height"
sothree:umanoShadowHeight="#dimen/app_bar_elevation">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
// some views
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<FrameLayout
android:id="#+id/list_filtering_fragment_container"
android:name="com.example.test.scenes.list.filtering.FilteringFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
and it was working until I've added new feature where I have to set list_filtering_fragment_container visibility to GONE. Everything works fine when I switch visibility status but it's not working when I move to another fragment and come back to previous one.
EDIT
It looks like:
Normal state that I want to achieve after set visibility to VISIBLE
State that I have after set visibility to VISIBLE (after changing fragments)
also I can see in layout inspector that location on Screen and height of this element is different for both cases.
I tried to use slidingUpPanel.setPanelState(PanelState.HIDDEN) but it for some reason doesn't work in 100% cases. It look like view goes outside screen and doesn't come back to it's proper position. And question is why it behaves like that?

Fragment being created despite not being in the layout

I'm following the tutorial here: https://developer.android.com/training/basics/fragments/communicating and am receiving a crash when I rotate the device to landscape, then portrait, then back to landscape. The crash occurs in the following snippet.
public void onArticleSelected(int position) {
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager()
.findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// Crash happens here.
articleFrag.updateArticleView(position);
} else {
//...
}
}
}
The reason for this is that when the app is rotated back to portrait, the fragment manager is returning a fragment despite it not being present in the portrait layout. To investigate this, I created a small app containing a horizontal layout like the following:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment android:name="com.example.matt.personfragments.PersonListFragment"
android:id="#+id/person_list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.matt.personfragments.PersonDetailFragment"
android:id="#+id/person_detail"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
and a portrait layout like the following:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment android:name="com.example.matt.personfragments.PersonListFragment"
android:id="#+id/person_list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
I've overridden the constructor in both fragments to log their creation.
As expected, when the app is first created I see PersonListFragment being created. When I rotate the device I see both PersonListFragment and PersonDetailFragment. So far so normal.
However, when I rotate the device back to portrait I see both PersonListFragment and PersonDetailFragment being created again. I believe this is due to the fragment being present in the saved instant state. Is this correct, and if so does that mean that the tutorial is incorrect?
Among the things that the Activity will save (actually technically the activity's FragmentManager) during a configuration change (like rotation) is Fragments.
Normally, this doesn't cause any issues, because the same fragments are used in any orientation. But in master/detail screens where the detail fragment is only visible in landscape orientation, you can run into problems.
Once the detail fragment has been created for the first time (when you rotate to landscape), that fragment will exist in the FragmentManager until you exit the activity. This is true even when you rotate back to portrait. However, the detail fragment will not be added in situations where your activity layout doesn't include that fragment.
So you can change your check from:
if (articleFrag != null)
to
if (articleFrag != null && articleFrag.isAdded())

2 activities appear next to each other depending on phone orientation

I am trying to make a stopwatch app that has Buttons that allow the user to start/pause the timer, reset the timer, and record a lap time. I would also have a Button that when clicked would bring up a new view that contains the lap times in a ScrollView.
That screen would have a Button which would bring the user back to the timer as well.
This alone would be relatively simple to do, it would just be two separate Activities. However, when the phone shifts to landscape mode I would like both screens (the timer and the lap times) to be displayed next to each other.
Is there an easy way to do this?
I was thinking about just having one Activity with two LinearLayouts inside of it (one for the stopwatch screen and one for the lap times) and having the Button that would normally switch between Activities just change the transparency of one of the view so only one was visible?
I imagine there is an easier way to do this, and I'm not sure if this solution would allow me to display each screen side by side anyways.
Thanks in advance!
I don't know of any way to display two activities at once, but I can tell you how I would solve this problem:
I would start by creating the stopwatch layout and the lap times layout in their own dedicated layout files - so in this example, we have /res/layout/layout_stop_watch:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- stopwatch views here -->
</RelativeLayout>
...and /res/layout/layout_lap_times:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- lap times views here -->
</RelativeLayout>
Then in /res/layout/activity_main.xml, we have the following for portrait mode:
<?xml version="1.0" encoding="utf-8"?>
<ViewFlipper
android:id="#+id/root_viewFlipper"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- stopwatch views included in activity_lap_times layout -->
<include layout="#layout/layout_stop_watch" />
<!-- lap time views included in activity_lap_times layout -->
<include layout="#layout/layout_lap_times" />
</ViewFlipper>
The ViewFlipper is there so we can call ViewFlipper.setDisplayedChild(1) to show the lap times layout when the user clicks the button to view the lap times. Simply call ViewFlipper.setDisplayedChild(0) to go back to the stop watch layout.
For landscape mode, we have a separate layout - /res/layout-land/activity_main.xml. This layout will automatically display in landscape mode, so we don't need to handle that programmatically. We can set it up like so:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<RelativeLayout
android:id="#+id/layout_stopwatch"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<!-- stopwatch views included in activity_lap_times layout -->
<include layout="#layout/layout_stop_watch" />
</RelativeLayout>
<FrameLayout
android:id="#+id/layout_lap_times"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<!-- lap time views included in activity_lap_times layout -->
<include layout="#layout/layout_lap_times" />
</FrameLayout>
</LinearLayout>
And that's it.
One thing to keep in mind is the ViewFlipper won't be used in landscape mode, so you'll want to check if it's null before setting the displayed child. Also keep in mind that switching from portrait to landscape will re-create your activity. If you want to save any of the view states, you can use savedInstanceState.
The advantage of doing it this way is that we don't have to write the code twice for the stopwatch and lap times layout, for portrait and landscape. Also, since we're using one activity for all of it, all the associated java code can reside in MainActivity.java.

How do you determine which layout was last used by an activity that switches between multiple layouts?

I'm just starting to use fragments in my app and the idea is one column for screens of a certain width (or less) and two for wider screens. So I have two separate layout files and in the onCreate method of MainActivity I choose which one to show based on the screen width. Since I am interested in the screen width rather than the orientation I can't use the simpler option of 'layout-land'. Both layouts use the same fragments but they can't be hard coded into the layout files because some of them need to be add
added and removed at runtime - thus I use a fragment transaction in the onCreate method to (at the moment) just add the fragments.
The problem comes when the activity is destroyed and recreated. If I don't check for whether savedinstancestate is null, it adds the fragments again (which is to be expected) and everything is doubled up. But if I only do the create code when it's null - as you would if there was only one layout - then when I test screen width again and just use setContentView(one or the other layout) it recreates the one that was shown with no problem but the other is blank. Again that's to be expected because the second one hasn't been instantiated yet. So is it possible to determine from the savedinstancestate which layout was in use when the activity was destroyed? And if it is, is it possible (or safe) to use the information in that to create the other layout - or should I just run the create code again? In other words does the standard savedinstancestate persist all the data I need when more than one layout is in use or will I have to do it all myself?
You can still use resource buckets to contain your layouts. i.e:
use /layout-sw600dp/ that is layout smallest width of 600 dip
From the official documentation
http://developer.android.com/training/multiscreen/screensizes.html#TaskUseSWQuali
res/layout/main.xml, single-pane (default) layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:id="#+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="match_parent" />
</LinearLayout>
res/layout-sw600dp/main.xml, two-pane layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<fragment android:id="#+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="400dp"
android:layout_marginRight="10dp"/>
<fragment android:id="#+id/article"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.ArticleFragment"
android:layout_width="fill_parent" />
</LinearLayout>

Android: How to use screen orientation change to switch between activities

I'm new to Android development. I'm trying to use the orientation change to switch between two activities. My idea is to use three Activities one TabActivity and a normal Activity. Here is some pseudo code:
public class Main extends Activity{
// this is the entry point into my app
#Override
public void onCreate(...){
if(this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
// Start Activity1
} else { //Start Activity2}
}
The Activities Activity1 and Activity2 will have their onPause() functions overridden with an intent to start the Main Activity again.
onPause(){
Intent intent = new Intent(this.ActivityX, Main.class);
startActivity(intent);
}
\/\/\/\/ EDIT \/\/\/\/
Ok, I'm taking a different approach. As i need a TabActivity, because i want to be able to start different activities in my tabs (I think i have to use a TabActivity in this case).
I've edited the Manifest.xml and added android.configChanges="orientation" for the TabActivity and the second Activity. Then I've overridden the onConfigurationChanged() functions. With this approach I'm able to switch from landscape (this is the "normal" activity") to portrait (the TabActivity).
The other way round does not work and i don't know why yet. I've exactly the same onConfigurationChanged functions (Copy & Pasted) and only changed the essential parts.
Overriding the onConfigurationChanged of the Activities started in the tabs has no effect, too.
You don't have to write any code - Android already handles this automatically. Just create two different layout resource folders:
/res/layout-land // layout resources for landscape
/res/layout-port // portrait layout
Put in this folders xyz.xml resource description files with the same name and different content. One using activities for portrait, the other for landscape.
Note that you can use the same technique (-port & -land qualifiers) for drawables (bitmaps) or any other resources (text).
Note: this is not supported on Android 1.5. If you want to support this version you must additionally add the /res/layout folder.
If you still want to use the advantages of TabActivity when using tabs in portrait mode, while not having tabs in landscape mode, you could use the following (ugly but still working) workaround. Create /res/layout-land folder and put there a corresponding layout file (it should have the same name that the file in layout folder, you use for portrait orientation). This file, however, should contain the blocks required by the TabActivity to work. That is fine, add those blocks and set their visibility to "gone", like in the following snippet:
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp">
<TabWidget
android:id="#android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<FrameLayout
android:id="#android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp"
android:visibility="gone" />
<!-- Feel free to add your REAL layout for the landscape -->
</LinearLayout>
</TabHost>
Of course, make sure you handle the content management correctly in your activity class, depending on the orientation.

Categories

Resources