I have a screen, that show different user interface when device is lanscape and portrait. It shows a ListView when device is portrait, and it shows a GridView when device is lanscape.
It looks like youtube app or google play store app.
Thanks for reading and answering my question.
EDIT: I want to add more details about my question:
I am using fragment to manage this view.
I need save all data and in this view when it rotate (avoid reloading a lot of data).
I tried to add android:configChanges on manifest and use onConfigurationChanged in this fragment. But it was not success.
I hope see the particular example or a detail solution.
But any answer is appropriated.
Thanks
There is two things to develop this functionnality.
First you will have to make two different layouts:
res
|- layout
|- mylayout.xml
|- layout-land.xml
|- mylayout.xml
In the layout/mylayout.xml file, include a ListView where you need it and in layout-land/mylayout.xml, include your GridView.
Android will select automatically the correct layout, depending of the current screen orientation, when you call setContentView.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
mListView = (ListView) findViewById(R.id.mylistview);
mGridView = (GridView) findViewById(R.id.mygridview);
// !!!!!!!
// mListView will be null in landscape
// mGridView will be null in portrait
// !!!!!!!
}
setContentView(R.layout.mylayout);
Now, in code you will need to check in which orientation you are to make if/else statements.
To detect in which orientation you are, follow the following states:
create a file in res/values/bool.xml
<resources>
<bool name="isLandscape">false</bool>
</resources>
create a file in res/values-land/bool.xml
<resources>
<bool name="isLandscape">true</bool>
</resources>
Now in your activity you can easily test, every time you need, if you are in landscape or portrait mode and apply the corresponding action
boolean landscape = getResources().getBoolean(R.bool.isLandscape);
if (landscape) {
// do something with your GridView
} else {
// do something with your ListView
}
To respond to your edit:
Fragments are very similar to Activities for this.
If you want to save data during orientation change, follow the official documentation on this topic, especially the part on Retaining an Object During a Configuration Change.
You should not use the android:configChangesin Manifest as explained in the documentation:
Handling the configuration change yourself can make it much more
difficult to use alternative resources, because the system does not
automatically apply them for you. This technique should be considered
a last resort when you must avoid restarts due to a configuration
change and is not recommended for most applications.
Try this and changing your layout accordingly.
public String getOrientation(Context context){
final int rotation = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getOrientation();
switch (rotation) {
case Surface.ROTATION_0:
return "portrait";
case Surface.ROTATION_90:
return "landscape";
case Surface.ROTATION_180:
return "reverse portrait";
default:
return "reverse landscape";
}
}
This method detects and returns the device orientation change. Might be quite useful.
A good way of saving data in an application which is independent of the activities in the application is to have an application object. Although the documentation says "There is normally no need to subclass Application", I find them very useful. Your application class needs to be mentioned in the application manifest, and it gets created before any activity objects. So store all your data in your application class, and then it won't matter what happens to your activities when the orientation changes.
Related
I am currently working on a app and it might be used on all android devices. I find it really challenging to adjust my XML layout files according to various screen sizes. I have surfed a lot over this topic and found a useful doc at Developer site. The document is decent and provides enough information on what should be done for screen compatibility.
Questions :
1.If I have two different layouts in folders like res/layout-sw600dp and res/layout-sw720dp, will the app automatically decides which one of these layouts is to be used ?
2.Assuming that I prefer a ListView for handsets and GridView for Tabs as a Home Page display, how will I define my layouts and how will I refer them for UI ?
Any ideas on how I can pull off the 2nd question's feature will be highly appreciated. Thanks in advance.
You can defferenciate it in java by checking "hasHoneycomb", tabs will give tru as return value. One way is : you can set different layout from setcontentview() according to condition.
if(hasHoneycomb()) {
setcontentView(layout_for_tabs);
} else {
setcontentView(layout_for_phones);
}
1) Yes, it will, based on the screen size.
2) Give them different IDs and see which one is visible:
ListView mList = (ListView) findViewById( R.id.homeList );
GridView mGrid = (GridView) findViewById( R.id.homeGrid );
if( mList != null ) {
// set list adapter
} else if( mGrid != null ) {
// set grid adapter
} else {
// neither view exists...
}
Yes, app automatically decide which layout file to use for current device screen.
Give the ListView and GridView different ids. In code use findViewById() method when creating views.
If ListView found (findViewById(R.id.list) returned View) app is running on handset, otherwise (findViewById(R.id.list) returned null and findViewById(R.id.grid) returned View) app is running on tablet.
For question 1, Yes Android will automatically choose and decide the most suitable amongst the two.
For 2, here is link - Determine if the device is a smartphone or tablet?
Hope it helps. :)
I'm new to Android and I'm building a simple application to start with. It consists of a client with three screens. In the first screen the user is prompted for an Ip to connect to a server (I use an EditText and a button). If the connection is successfully established, some data will be retrieved from the server and the client will show the data on a blank screen (I use a TextView). This would be the second screen. Then, the user could ask the server for detailed information about any data that has been retrieved from the server, which would be the third screen (I use a TextView again).
The problem is that I don't know what's the best way to go about it. I have currently one activity and one XML file containing all the components of the view (EditText, button, TextView). Until now, I've been using setVisibility(View.GONE);to hide certain components depending on the screen the user is in. (For example in the first screen I have to hide both TextViews).
One of the problems I'm facing is that when I put the phone in a horizontal position the components I had hidden show up again. I don't know if hiding views is the ideal thing to do for my purpose.
I've thought that maybe I should use more than one activity, shouldn't I?
I really appreciate any help you can give me to structure my first app.
I would definitely recommend splitting up your App into multiple Activities/Fragments. Depending on how big the logic for each screen gets you will be glad you did it later on because each Activity only has one responsibility.
Look at your mail app for example. You got your List Activity showing you all your mails and then when you select one it starts the Detail Activity showing you the content of your mail. Each Activity is only responsible for one thing which make each one easier to write and maintain.
It also simplifies your layout definitions because each one only contains the relevant parts.
Seems like this is coming up a lot. Android destroys and recreates and Activity when the configuration changes. Screen rotation is part of the orientation. In order to avoid that, the Activity is responsible for retaining state. The mechanisms given for that are the onCreate and onSaveInstanceState. In your example, you could do something like the following:
int uiPhase = 1;
#Override
void onCreate( Bundle data ) {
uiPhase = data.getInt( "uiPhase", 1 );
// inflate layout
setPhase( uiPhase );
}
// invoke the following each time your screen changes
void setPhase( int newPhase ) {
uiPhase = newPhase;
switch( uiPhase ) {
case 1: // show UI elements for first screen, hide others
break;
case 2: // show UI elements for second screen, hide others
break;
case 3: // show UI elements for third screen, hide others
break;
}
}
#Override
void onSaveInstanceState( Bundle data ) {
data.put( "uiPhase", uiPhase );
}
I didn't want to complicate the pattern above too much, but a good method for setting visibility is as follows:
phase1view.setVisibility( uiPhase == 1 ? View.VISIBLE : View.GONE );
phase2view.setVisibility( uiPhase == 2 ? View.VISIBLE : View.GONE );
phase3view.setVisibility( uiPhase == 3 ? View.VISIBLE : View.GONE );
That pulls the repetition in the setPhase method quite a bit together.
Set button visibility to GONE (button will be completely "removed" -- the buttons space will be available for another widgets) or INVISIBLE (button will became "transparent" -- its space will not be available for another widgets):
use in place of
setVisibility(View.GONE)
change to
setVisibility(View.INVISIBLE) and try
i use different Layouts for different Screensizes and Devices. I use Fragments with specific Layout Folders.
The Concept is great, for Tablets and Devices with a Large Screen i place a Layout file in
layout-sw600dp and Android manages to deliver the right layout on the different devices.
What Bugs me is: How can i find out what Layout is used inside my Code.
My Fragments needs slightly different Codes for the different Layouts.
In General whats the Best Practice to separate Custom Layout Programming Logic inside my Fragments/Activities?
My approach now is kind of hacky and not in sync with the different Layout folders.
private boolean isTabletDevice() {
if (android.os.Build.VERSION.SDK_INT >= 11) { // honeycomb
// test screen size, use reflection because isLayoutSizeAtLeast is
// only available since 11
Configuration con = getResources().getConfiguration();
try {
Method mIsLayoutSizeAtLeast = con.getClass().getMethod("isLayoutSizeAtLeast", int.class);
Boolean r = (Boolean) mIsLayoutSizeAtLeast.invoke(con, 0x00000004); // Configuration.SCREENLAYOUT_SIZE_XLARGE
return r;
} catch (Exception x) {
x.printStackTrace();
return false;
}
}
return false;
}
and then
if(isTabletDevice()) {
//findViewById(R.id.onlyInTabletLayoutButton);
}else{
//
}
This is the method I use personally:
In each layout, I add a Tag to the root of the layout, and make sure that all of the layout roots have the same id. So for example, I'll have a layout that goes something like:
<RelativeLayout
android:id="#+id/rootView"
android:tag="landscapehdpi">
<!-- Rest of layout -->
</RelativeLayout>
And then have another one like:
<RelativeLayout
android:id="#+id/rootView"
android:tag="portraitmdpi">
<!-- Rest of layout -->
</RelativeLayout>
Then once the layout has been inflated, I use:
View rootView = (View) findViewById(R.id.rootView);
This returns the layout root currently in use. Now to determine which layout it is exactly and run the appropriate code, I use a series of if-else blocks:
String tag = rootView.getTag().toString();
if(tag.equals("landscapehdpi"))
{
//Code for the landscape hdpi screen
}
else if(tag.equals("portraitmdpi"))
{
//Code for the portrait mdpi screen
}
//And so on...
So basically using this you can know which layout has been loaded at runtime, and run the appropriate code.
I think you are looking for the same solution as this question here,
How can I detect which layout is selected by Android in my application?.
if you would like to see the best answer, there are two available options.
First one is to use config for your values folder and from there get the String from your xml file and cross check it. (To use it as a flag).
https://stackoverflow.com/a/11670441/603744
And the next one is to set Tag to your layouts, and get the tag from your code to find out which tag it prints and based on that find the layout what it has used. But you also have to note that there is a little bug in this approach. But I haven't tired them yet.
https://stackoverflow.com/a/11205220/603744
Everything I've read about Intents talks about using them to push data, or to start one Activity from another Activity. I want to pull data from an Activity that's already running.
The Tab Layout tutorial at http://developer.android.com/resources/tutorials/views/hello-tabwidget.html illustrates what I want to do. (My app is doing some engineering calculations instead, but the tutorial code provides a good analogy to my app.) The tutorial creates an app with three tabs, and each tab hosts a separate activity.
To expand on the example in the tutorial, suppose I select an artist in the Artists tab/activity. I want to be able to select the Albums tab/activity and have it display all the albums featuring that artist.
It seems to me that I need to use an Intent to do this. All of the tutorials I've found assume that I would create a "See albums" Button in the Artists tab/activity, and that pressing the Button would execute an Intent that starts the Albums activity and passes artistName.
I DO NOT want to create that Button. Real estate on the Artists layout is precious, and I have a perfectly good Albums tab, AND the HelloTabWidget activity already contains an intent to create the Albums tab.
Besides, a user will want to skip back and forth between Album and Artist in order to change artist selections, and the tabs are a perfectly good way to do this. There's no need to complicate the UI with another button.
So how can I have the Albums activity PULL artistName from the Artists activity when the Albums tab is selected (or the Albums layout is displayed), rather than have the Artists activity START Albums and PUSH the artistName?
Equivalents I can think of from other programming worlds:
Global variables. Discouraged in Android devt, right? And if they do exist, what are they called?
A getter, like artistName = Artists.getArtistName(); . I get the feeling that it's not that easy.
Writing to, and reading from, a file - that is, mass storage or non-volatile memory. I don't need the artistName value to be permanent. It will be reset to null every time the user launches the application.
So how is it done in the Android world? Do I use an Intent - and if so, how?
Global variables were the right answer.
I thought Java discouraged their use, but a couple of links that appeared in the "Related" links on the right margin of this window mentioned them directly. One was "Android: How to declare global variables?" and the other was "how to pass value betweeen two tab in android". Both pointed to the Application Class as the place to define global variables and methods. Armed with this new knowledge, I found an article called "Android Application Class" on the Xoriant blog that expanded on the StackOverflow answers.
It's best to review those three links first. I need to add some tips to what those authors have said.
Your Application class has to be in its own separate file. (That might be a "duh" to some people, but not to everybody.) Here's a good framework for an example called Something.java:
public class Something extends Application {
// Put application wide (global) variables here
// Constants are final, so they don't have to be private
// But other variables should be declared private;
// use getters/setters to access them
public final boolean FEET = false;
public final boolean METERS = true;
private boolean units = FEET;
#Override
public void onCreate() {
super.onCreate();
// Put any application wide (global) initialization here
}
// Put application wide (global) methods here
public boolean getUnits() {
return units;
}
public void setUnits(boolean whichOne) {
units = whichOne;
}
}
I'm using Eclipse with the ADT plug-in, in Windows XP. Eclipse doesn't always behave properly if you edit XML code directly, so it's best to open AndroidManifest.xml, then select the Application tab and enter your application name in the Name field. You don't need to put a dot or period in front of the name. Just type in the name of your class, like "Globals" or "MyApplication" or whatever. (Note that this is the default application in your Manifest. You don't have to create a separate <application></application> tag.
This step may not be necessary on an actual Android device, but it was necessary for the emulator: you need to use the getApplicationContext() command in every onCreate() and every method that will be accessing the global variables and methods. I tried to put it outside of onCreate() with the rest of my activity wide variables, and it didn't work. Putting it inside every method seems wasteful, but both the emulator and the Android device work fine with it that way. Here's a sample showing how I used it:
public void fooBar() {
// Access to global variables and methods
final Something s = (Something)getApplicationContext();
// ...
// This next line demonstrates both a global method and a global variable
if (s.getUnits() == s.FEET) {
// do something with feet
} else {
// do something with meters instead
}
// ...
}
Those were the only hiccups I encountered. The three references that I have listed, taken together, are otherwise pretty complete.
In my Android app, I have two custom view classes - PortraitClass and LandscapeClass. They both do the same thing. On running the app, the view class fetches some pictures from an SDCard and then maniputlates (skew etc) and displays them. The only difference between the two classes is that the layout of pictures is slightly different on the screen.
I have two display.xml files (one under layout folder and the other under layout-land). The one under the layout folder adds Portrait and the other adds the Landscape class.
On orientation change, I would like to send information (picture numbers and few bitmaps) from one class to another so I won't have to fetch all the bitmaps again and also so that I display the ones that were being displayed.
I find the parcelable thing kind of confusing. I tried following this_example, but I noticed that in onRestoreInstance, the Parcelable has null for the "mSuperState" and I get a classCastException # "SavedState ss = (SavedState)state". The integer (picture number) that I am trying to pass is there. I am not sure what I am doing wrong.
You could use a global singleton in the Application class. For example an "AssetManager" is available here:
public class YourApplication extends Application {
public AssetManager assetManager = new AssetManager();
#Override
public void onCreate()
{
super.onCreate();
}
}
You can then call it from another activity:
YourApplication application = ((YourApplication ) this.getApplication());
application.assetManager.someFunction();
Not sure if this is what you are looking for but my stuff always works in orientation change with onCreate(Bundle savedInstanceState). Mine is all text in edit text boxes though so I am not sure how it would work for you.
Also check this out about midway down under "Persisting State Information During Configuration Change"
http://www.devx.com/wireless/Article/40792/1954