Can we have a different xml for landscape and different xml for portrait orientation?
I am working on a simple app, have few buttons and textviews, the xml looks good in portrait, But with same xml when I check the landscape orientation, design doesn't look good.
Any suggestions are appreciated..
Thank you.
Yes ofcourse.
You will have to create two versions of xml files and put in layout-port and layout-land folder inside res folder.
eg :
res/layout [Portrait Mode; default]
main.xml
res/layout-land [Landscape Mode]
main.xml
You can refer further more on the same at http://developer.android.com/training/basics/supporting-devices/screens.html
If you want to make another layout for landscape then put it in
res -> layout-land folder .
Both the names of the xml are must be same for which is used for portrait and landscape .
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_orientation_app);
if(getResources().getDisplayMetrics().widthPixels>getResources().getDisplayMetrics().
heightPixels)
{
Toast.makeText(this,"Screen switched to Landscape mode",Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(this,"Screen switched to Portrait mode",Toast.LENGTH_SHORT).show();
}
}
you should make a different xml file for landscape orientation. below link can help you
http://developer.android.com/training/basics/supporting-devices/screens.html
Related
I have created Android layout resource files for landcape mode for all different screen sizes such as, small, large, medium and extra large , however, when I run the app it does not work in landscape mode as half of the buttons, images are missing from the screen.
I have also included the below line in the android Manifest file.
android:configChanges="keyboardHidden|orientation|screenSize"
Please advice how I can make andriod aware of when to use the landscape and portrait mode.
This is happening because you have added android:configChanges="keyboardHidden|orientation|screenSize"
in your manifest which means the orientation changes will not occur hence your landscape layout never gets initialised. By default the android behaviour is that when the mobile is rotated the current activity is destroyed and new one is created.
Remove that line and try
In the java file of an activity this is how you can recognize if you are in a Landscape mode or portrait mode.
int orientation = getResources().getConfiguration().orientation;
if(orientation == Configuration.ORIENTATION_LANDSCAPE) {
//do something
} else {
//do something
}
okay I am giving an example.....i had a imageview in my layout. when I go to portrait mode it is set one image. and when I go to landscape mode it changes to another image.
ImageView headerimage= (ImageView) findViewById( R.id.headerimage);
int orientation = getResources().getConfiguration().orientation;
if(orientation == Configuration.ORIENTATION_LANDSCAPE) {
headerimage.setBackgroundResource(R.drawable.splash_icon_landscape);
} else {
headerimage.setBackgroundResource (R.drawable.splash_icon_portrait);
}
This is a scaled down version of the actual problem. To recreate the difficulty I am facing.
I have taken the example from the official website developer.android.com to cite my problem.
Building a Flexible UI
MainActivity has 2 layouts. One is for the default(portrait in small screen devices) layout in the layout folder. The other layout for both large-screen and landscape mode, kept in layout-large and layout-land folder.
Default layout for activity_main.xml contains only one FrameLayout (R.id.fragment_container) in which I add and replace 2 fragments that I create, dynamically.
The other layout is same for both the layout-land and layout-large folders. It has 2 static fragments [R.id.headlines_fragment - to display a list of headlines] and [R.id.article_fragment - to display the details when headlines are selected]. Horizontally placed. One on the left to show the lists and the one on the right to show details.
This is the code for MainActivity.java which controls all the fragments :
public class MainActivity extends Activity implements OnHeadLineSelectedListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_article);
if(findViewById(R.id.fragment_container) != null) {
if(savedInstanceState != null) {
return;
}
HeadlinesFragment firstFragment = new HeadlinesFragment();
firstFragment.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(R.id.fragment_container, firstFragment).commit();
}
}
#Override
public void onArticleSelected(int position) {
ArticleFragment articleFrag = (ArticleFragment) getFragmentManager().findFragmentById(R.id.article_fragment);
if(articleFrag != null && articleFrag.isVisible()) {
articleFrag.updateArticleView(position);
} else {
articleFrag = new ArticleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction().replace(R.id.fragment_container, articleFrag);
transaction.addToBackStack(null);
transaction.commit();
}
}
}
As soon as the activity is started, I check if fragment_container that is the FrameLayout is present or not. If it is not present, then the layout with the 2 fragments has been loaded. Hence no need to add the fragments dynamicaly as they are already present.
Otherwise, I check if the savedInstanceState is null or not. If null, then I create a new HeadlinesFragment and add it to the frame. If it is not null, then it means that the activity has already been created previously, and hence the HeadlinesFragment must have been added already. No need to add it again. So, return.
And onArticleSelected() method replaces the existing fragment in the frame with the ArticleFragment, or if in the other layout, it simply updates the fragment as it is already present. It is called from the HeadlinesFragment when an item is selected.
Now, this all works perfectly well if we enter the activity in portrait mode and then change the orientation. No problem. Flawless.
But if we enter the activity in the landscape mode, as soon as I change the orientation to the portrait mode, a blank screen is shown.
The reason being, the onCreate() is called, and the savedInstanceState returns as not null. Hence, the HeadlinesFragment is not created and added to the frame.
And yes, if I remove that check, then the app works fine, but that will mean that a new HeadlinesFragment is created and added to the frame each time and gets stacked on top of eachother. Which is not at all desirable.
I cannot implement this by just finding out the orientation and applying the appropriate layout. Because, in large-screen devices, even if it is in portrait mode, it is supposed to show both the fragments at once.
I have tried many convoluted logic. But nothing seems to work. Any help is appreciated.
Entering activity in portrait mode
1> List Items are shown.
2> Clicking items replaces the fragment with the ArticleFragment (details).
3> Changing the orientation, shows both side by side. Everything works.
--->
Entering activity in landscape mode
1> Both the list and details are shown. Everything works.
2> But as soon as the orientation changes, you get the blank screen. As Headlines fragment is not created and added.
-->
It would be really helpful if someone could guide me as to how I can solve this problem. And as the actual project is huge, and this logic has already been implemented, a drastic change in logic is not an option anymore as that will mean re writting thousands of lines of code. Thank you. :)
Ok. I got the problem. The problem is we are using Fragments in xml layouts in large devices.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="#+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="#+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
So android is trying to catch the Fragments in savedInstanceState in MainActivity. When screen rotates, system tries to restore the above Fragments even though different layout loads in potrait mode. And so system considers that the article_fragment is also available on the right side and it tries to update it on click on the Headline.
So, What's the solution ?
I have changed a little code in MainActivity and nothing else :-)
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
setContentView(R.layout.news_articles);
// Check whether the activity is using the layout version with
// the fragment_container FrameLayout. If so, we must add the first fragment
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
/*if (savedInstanceState != null) {
return;
}*/
// Create an instance of ExampleFragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
So what I have done just I told the system that I don't want any thing restored by using super.onCreate(null); so its restoring nothing now.
About the blank screen you are getting
Whenever you start activity in landscape mode. It loads large display by default. Without entering into if satement. Because it can't get fragment_container in landscape mode. And then you rotate screen to load portrait layout and system gets savedInstanceState != null and it returns without loading HeadlinesFragment. So you get bank screen.
So I have commented If statement as you can notice.
/*if (savedInstanceState != null) {
return;
}*/
So now It load everything correctly.
No issue
Download the code sample from this Developer site and modify it according to your needs.
Build Dynamic UI with fragments
Its super and reliable code.
I'm sure there are reasons why the Fragment was rendered correctly in Portrait -> Landscape and not in the reverse but one thing is very clear about the Activity lifecycle
Called when the activity is starting. This is where most
initialization should go: calling setContentView(int) to inflate the
activity's UI, using findViewById(int) to programmatically interact
with widgets in the UI, calling managedQuery(android.net.Uri,
String[], String, String[], String) to retrieve cursors for data
being displayed, etc.
Note the part that says This is where most initialization should go. By exiting after checking that savedInstanceState is null, you're leaving it up to the super class to restore your Fragment which is not a good idea considering the previous view was destroyed.
My advice, inspect the content of savedInstanceState instead of just checking if it is null. Ensure that it contains enough data to restore your previous state, if not initialize your Fragment.
The best practices with Fragments involve implementing all necessary methods to monitor the state.
i am building an android lib with AdMob to use on kony platform. The lib works great for a android phone or for tablets which are in portrait mode by default, but the Ad doesn't display on the galaxy Tab 10.1 (which is a in landscape by default).
i get this error :
Not enough space to show ad. Needs 1280x90 dp, but only has 800x1207 dp.
it seems that Admob get the size in landscape mode instead of the portrait size. (to be clear, portrait being 800x1207 and landscape 1280x800 when a write about it) and i am not sure where does the issue com from
my application is portrait mode only and i use this code to create my banner :
public View onCreateView(Context context) {
adView = new PublisherAdView(context);
adView.setAdUnitId(MY_AD_UNIT_ID);
adView.setAdSizes(AdSize.SMART_BANNER);
/*LayoutParams lparams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
adView.setLayoutParams(lparams);*/
PublisherAdRequest.Builder publisherAdRequestBuilder = new PublisherAdRequest.Builder();
// Optionally populate the ad request builder.
publisherAdRequestBuilder.addTestDevice("XXXXXXX");
adView.loadAd(publisherAdRequestBuilder.build());
return adView;
}
onCreateView is call by the kony platform to display the Ad and I don't really know what's happen on this side. This code works fine for any android phone and tablet which are in portrait mode by default.
i can also display an Ad on the galaxy 10.1 if I change SMART_BANNER by Leaderboard but it's not centered on the Kony app and not as convenient as the smart banner.
I don't know android a lot and might have forgot to check something basic.
is there anything i could try to get the correct size before to investigate if it comes from Kony?
Thanks
Please check your layout for any padding. It just works if you remove any default padding.
Cheers!
Here, I'm trying to start an activity on different devices (Android Tablet and Android Phone). Is there a way to use if statement to check which activity should to load based on the devices?
Here's my code:
Button buttonGo = (Button) findViewById(R.id.button_goToActivity);
buttonGo.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (tablet device) {
startActivity(new Intent(MainActivity.this, TabletActivity.class));
} else {
startActivity(new Intent(MainActivity.this, AndroidPhoneActivity.class));
}
});
Thanks for your effort...
In order to achieve that you must use Fragment not an Activity. the Design Philosophy of Fragments is exactly what you want. you can take a look at Example to see how it is implemented. This is a master-detail app with two Fragments, when device in a portrait mode or it is not a tablet it just shows master, otherwise it also shows detail beside the master and when you select an item from the list you will see the details without going to other Activity.
Firstly as Android Best Practice it is adviced to use Fragments rather than Activity to achieve your requirement.
But if you are keen on using Activity, then below is the solution to find device type and load different activities.
1) Have 2 values folders: values -> strings.xml and values-sw600dp -> strings.xml
2) Create string value in both the xml
<string name="device_type">PHONE</string> in values -> strings.xml
<string name="device_type">TABLET</string> in values-sw600dp -> strings.xml
3) create a method to check if current device is Phone or not
public boolean isPhone() {
String deviceType = getResource().getString(R.string.device_type);
if (deviceType.equalsIgnoreCase("PHONE") {
return true;
} else {
return false;
}
}
4) Depending on the device load your activity as you have already done in your above code snippet
Let me know if this helps...
Most likely, you should not have two separate activities here. Instead, you should have two different layouts for the same activity. The layout for phones should be in the regular res/layout folder and the layout for tablets should be in the res/layout-large folder. Read Supporting Different Screens for more details.
If you are using same fuctionality for Tablet Activity and Phone activity then you do not need two activities for this purpose. You need to make two different Layout xml for Tablet as well as Phone.
Make a Layout folder named : layout-large and layout-xlarge.
Now copy the xml file from normal layout folder and paste in both new layout folders.
No need to change the name of xml file.
Now Adjust xml file of layout-large and layot-xlarge as you want to make design for tablet.
Now on running application Tablet and phone bot will pick xml according to there size and screen density.
Note : the functionality should be same.
And if: you want to show different screen with different functionality for phone and tablet then. use this code for check.
if ((getResources().getConfiguration().screenLayout &
Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE) {
}
Hope this wll help you.
My app crashec when rotating the device. I know it crashes because whenI rotate the screen it tries to create new View to draw and display but how can it create the view and display it properly on the launch but crashes when trying to create the second new view?
I dont know what part of my code I should post here? Can you give a clue to solve this problem?
I found something usefull. I put try/catch statement and now when I rotate the device, it doesnt crash but displays a blank page, when I re-rotate to the old position it display the page. here is my code
#Override
protected void onCreate(Bundle savedInstanceState) {
try{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
} catch (Exception e) {
Log.i("my_error", e.getMessage());
}
}
but eclipse cannot catch the exception.
How can I solve this problem?
There are two possible ways to do this:
1) You don't care about the orientation change of the screen and you just want to be "resized". Just add the following properties in your Manifest file:
<activity
android:name=".SplashScreen"
android:configChanges="orientation|keyboardHidden|screenSize" ... />
and add this lines of code in your activity:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// do nothing, just override
}
2) You do care about the orientation change. Then there are a couple of things that you should know about the activity lifecycle in an orientation change.
Firstly the onConfigurationChanged() is called and you should store in a Bundle everything that you need. For example the user has editted an EditText and you should save the value.
Why to do that? Because afterwards the onCreate() method will be called again in order for you to implement a different layout or alter something in the Views. You should then take the previously saved Bundle and restore the variables that you saved in your newly created layout.
Just add the android:configChanges="orientation" to your activity
<activity android:name=".MyActivity"
android:configChanges="orientation"/>
if you given like this, then oncreate will not called if your screen orientation is changed.
If you need more information about this you can check this link
There are a couple of things you can do:
1) Ignore screen orientation changes. If your app is only designed for portrait mode, most likely it won't look good in landscape mode and vice versa. Best is to specify the target orientation in your activities.
2) If you want to allow orientation changes, then you should have written some code re-create the view with different/modified layout. Mind sharing the code with us?
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
//Override this and recreate your view or set adapter here
}
Dont forget to add android:configChanges="orientation" in your manifest.
On a side note you can fix orientation through manifest like this -
android:screenOrientation="portrait"
Add this to your Activity tag in the manifest:
android:configChanges="orientation|screenSize"
This will cause that the activity is not recreated on rotation change.
The flag sreenSize is for newer devices(API level 13 or higher), with including only the orientation flag the activity will stil recreate when rotating on devices running API level 13 or higher.