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

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.

Related

How to share common layout between activities without fragment

Is there any possible way to share layout(part) between activities? For example, in my app, all activities have similar layout, the top part is long operation indicator (a progress bar, hidden when no operation is being executed), the bottom part is for showing errors. Only the middle part is different for all activities. See the picture below.
so my question is, is it possible to reuse the common layout(loading and error part) for all activities in my app? (currently I don't want to use fragment to do it for some reasons)
maybe the layout resources should like this:
layoutfolder
activity_common.xml
activity_one_content.xml
activity_two_content.xml
thanks
You can create an abstract 'base' activity that all your activities extend from, overriding setContentView to merge the base, and sub activity layouts.
This way you can handle all the loading/error code in the base activity, and simply toggle between hiding and showing the views in the sub activities.
The abstract activity:
public abstract class BaseActivity extends Activity {
protected RelativeLayout fullLayout;
protected FrameLayout subActivityContent;
#Override
public void setContentView(int layoutResID) {
fullLayout = (RelativeLayout) getLayoutInflater().inflate(R.layout.activity_base, null); // The base layout
subActivityContent = (FrameLayout) fullLayout.findViewById(R.id.content_frame); // The frame layout where the activity content is placed.
getLayoutInflater().inflate(layoutResID, subActivityContent, true); // Places the activity layout inside the activity content frame.
super.setContentView(fullLayout); // Sets the content view as the merged layouts.
}
}
the layout file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The main content view -->
<FrameLayout
android:id="#+id/loading_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- The main content view -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/error_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
You could use include in XML to, well.. include the re-useable part of your layout code.
As an example, here's my layout file for the Toolbar I used in my app:
// /res/layout/component_toolbar.xml
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:taggr="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/primary"
android:minHeight="?attr/actionBarSize"
taggr:popupTheme="#style/ThemeOverlay.AppCompat.Light"
taggr:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" />
Now, say if I want to use that Toolbar again in a different Activity, this is all I'd have to write:
// /res/layout/whatever_layout_this_might_be.xml
<include layout="#layout/component_toolbar" />
Bear in mind that this would only copy the layout - not the actual behavior of said widget/component.
If you want to actually copy all of the aspects (layout, behaviour) I'm afraid Fragment is the only way to go.
Although ActivityGroup is deprecated fro API 13 but if you don't wish to go with fragments then this can be your best choice.
According to documentation, an ActivityGroup is:
A screen that contains and runs multiple embedded activities.
You can find a tutorial here and here Although the mentioned tutorial uses a Tablayout you can replace that with your common layout in XML.
A second Approach could be Reuse the layout with include tag, in this approach you could just reuse your once created common layout everywhere in the app.

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>

Complex Android UI design guidance needed (fragments)

I am developing an applications that is aimed at Tablets and Google TVs. It will be like many standard Google TV applications with a LeftNavBar and a top Search bar that is common to all application screens. It will look something like the following image:
Main Screen
The RED area will be different for all other screens. It may contain data like following screens mockups:
Activity One loaded into main container
Activity Two loaded into main container
So you can see that completely different sections can be loaded in the main area.
Screen 3 can be loaded as a detailed section when selecting any list item in Screen 2 (say in fragment list) OR it can be loaded as a result of selecting a tab (which will appear in LeftNavBar).
Here is how I am trying to implement it.
Step 1. I Created a main Activity with the following XML:
<?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="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#9ccc" >
<!-- Top Bar -->
</LinearLayout>
<FrameLayout
android:id="#+id/mainContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<!-- main Red Container that will load other Activities -->
</FrameLayout>
</LinearLayout>
mainContainer is the RED container where I want to load the Activities. LeftNavBar will be added to this Activity as its the parent of All.
Step 2 I created ActivityOne & ActivityTwo with two & three Fragments in them respectively (as shown in above second & third image).
*Step 3 I am trying to load the ActivityOne in main page's mainContainer FrameLayout... But I cannot add it.
I tried by adding the ActivityOne to mainContainer as follows:
View v = (new ActivityOne()).getWindow().getDecorView();
FrameLayout mainContainer = (FrameLayout) findViewById(R.id.mainContainer);
mainContainer.addView(v);
but the getWindow() returns null....
Other issue occurs because all the data comes from a remote services .. so please also suggest how would I be able to hold references to all the loaded Activities in mainContainer in a some kind of stack ... so I can just reload the already loaded activity instead of creating its new instance.. This will be used on BACK button press.
OR
Instead of loading an activity into the above RED container, I should create two Activities each with their own Fragments & a LeftNavBar. This might be easier than the aforementioned approach. or this might be the only solution.... however I feel that saving state for BACK buttons might get messy .. but I will try implementing this
What would you do if you had to create this type of application?
How would you design the UI layout for best performance/practice?
Your suggestions in helping me setting this app's layout are much appreciated.
Disclaimer
This is where fragments can get tricky. The problem would be simple if Activity 1 & 2 had identical layouts so that you could simply attach/detach fragments and use the fragment back stack to unwind.
Because you want 2 unique layouts to house your fragments, things are going to be a little more involved. If at all possible I would try to use the same layout so that you can take the easy path.
As another option, you could use two activities as you outline above and send data back and forth with Intents.
That said, if I really had to implement this solution as written, here is what I would do. Note that I am not advocating this solution but myself do not know of a better way of doing things.
The Solution
Create a FragmentActivity whose view would be Main Screen as you've defined above. The layout for the Main Screen would contain:
Left nav bar
Top bar
2 layouts. layout1 and layout2. These would be contained in a parent layout i.e. RelativeLayout or LinearLayout and would contain the necessary FrameLayout elements for your fragments.
Example using your XML (note, tags are a bit brief):
<?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="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#9ccc" >
<!-- Top Bar -->
</LinearLayout>
<LinearLayout android:id="#+id/layout1">
<FrameLayout android:id="#+id/listFragment" />
<FrameLayout android:id="#+id/contentFragment" />
</LinearLayout>
<LinearLayout android:id="#+id/layout2">
<FrameLayout android:id="#+id/imageFragment" />
<FrameLayout android:id="#+id/boxFragment1" />
<FrameLayout android:id="#+id/boxFragment2" />
<FrameLayout android:id="#+id/boxFragment3" />
</LinearLayout>
</LinearLayout>
The main idea is that you then show/hide layout1 & layout2 i.e. set android:visibility="gone" based on the state of your app.
The disadvantages of this method are:
Using fragment backstack may be impossible, instead you'll have to track where the user is in your UI flow and manage the back button to show/hide layout
You may need to take special care to attach/detach fragments when you show/hide their parent view to reduce resource consumption while the fragments are invisible
The advantages are:
Easy communication between fragments and the base activity since only 1 activity is used
Re: The nested Fragments problem
To get around the 'nested Fragments' problem in our application where (as you correctly note) Fragments cannot add Fragments I hosted a single templating Fragment under the activity whose only purpose was to define a set of place holders for other fragments to anchor to. When adding further Fragments to the activity past this point I used the templating Fragment's view place holder +#ids to identify the "root" or parent view Id for the Fragment being added.
getSupportFragmentManager().beginTransaction().add(#someIdFromTheTemplateFrag, fragment, fragmentTag).commit();
The Fragment I was adding then knew where to anchor itself in the current layout and of course then went about it's merry way of add it's view. This had the effect of attaching a Fragment to another Fragment hence achieving the desired visual 'nesting'...

Android - handling orientation changes when using fragments

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.

Android views within tabs method

I am creating an application where each tab has different screens to switch between. Originally I was going to have each tab show a different activity but from what I have read, it seems that is a bad way of doing it.
So I have been using an xml file which has the layout for each screen all in the one file. And I use Java to program which to display each screen required and hide the ones I don't need. The problem I have with this approach is it means all my Java is one file, and all my XML is in one file. Could someone explain to me a better way of doing this as I am pretty sure there must be a better way. My activity has many screens and its getting an eye sore scrolling through all this code at once, as the more screens I add, the larger the code is getting.
Ideally I would like to have the code for each screen in its own Java class, and the layouts for each screen in their own xml files and just switch between these.
To give you example of my code
<TabHost android:id="#+id/tabhost" android:layout_width="fill_parent"
android:windowSoftInputMode="adjustResize"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#92c223" android:layout_height="fill_parent">
<LinearLayout android:id="#+id/tab1"
android:layout_above="#android:id/tabs" android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ScrollView android:layout_width="fill_parent"
android:layout_height="wrap_content">
<RelativeLayout android:layout_width="match_parent"
android:layout_height="wrap_content">
XML CODE FOR LAYOUT GOES IN HERE. EACH NEW SCREEN IS CONTAINED WITHIN THESE LAYOUTS.
</RelativeLayout>
</ScrollView>
</LinearLayout>
<TabWidget android:layout_width="fill_parent" android:id="#android:id/tabs" android:layout_weight="0"
android:layout_gravity="bottom" android:layout_height="wrap_content"></TabWidget>
</FrameLayout>
</TabHost>
Then in my Java code, I switch between tabs using
if ("home".equals(tabId)) {
tab1.setVisibility(View.VISIBLE);
tab2.setVisibility(View.GONE);
tab3.setVisibility(View.GONE);
tab4.setVisibility(View.GONE);
tab5.setVisibility(View.GONE);
and when switching between screens within an activity, when user clicks a button in that activity, I use on click listener and do this
tab2.setVisibility(View.GONE);
cvv.setVisibility(View.VISIBLE);
I am sure there is a better way of doing this, and would be grateful if someone would give me suggestions.
Another problem is, as its all in one activity, whenever I hit the back button, it just exits the application. Would prefer to have it so when they hit back, it goes back to previous screen.
The 'better way' is giving each tab an activity of its own. Why did you dismiss this approach in your first paragraph? This is exactly what a TabHost was designed for.
What I usually do is subclass TabActivity. In its onCreate(Bundle savedInstanceState) method, I do the following once for each tab:
tabHost.addTab(
tabHost.newTabSpec("tab1")
.setIndicator("Tab 1", res.getDrawable(R.drawable.tab1))
.setContent(new Intent().setClass(this, Tab1Activity.class))
);
Then you develop each Tab#Activity in an independent file. It's nice and modular. Switching between tabs is handled by the library. You don't need to worry about all that.
Within your tabs, you can capture the back button by overriding the onKeyDown method in your inner activity:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// Do your thing
return true;
}
return super.onKeyDown(keyCode, event);
}

Categories

Resources