My app has a webview and some buttons inside a LinerLayout.
the problem is, I want the buttons to be on bottom in portrait mode and on left in landscape mode while the webview maintains it's state.
Two different layout doesn't work as it force recreation of the activity that refresh the webview. for now I use android:configChanges="orientation" in activity tag so webview doesn't get refreshed on orientation change.
Is there anyway to replace the layout of buttons on the change of screen mode?
portrait mode
landscape mode
I tested fragments, but dealing with fragment makes things much more complex and the fragment itself needs saving and restoring which may not work in a webview which has javascript state, So I searched more and find a nice article somewhere and with some modification I came to a solution which I suggest:
First, add android:configChanges="orientation|screenSize|keyboard|keyboardHidden" so the app handles the config changes instead of android.
Make two different layout for landscape and portrait. In both layouts instead of webview place a FrameLayout which acts as a placeholder for the webview.
Define initUI method like this and put everything related to UI initialization in this method:
public void initui()
{
setContentView(R.layout.main);
if (wv == null) wv = new WebView(this);
((LinearLayout)findViewById(R.id.webviewPlace)).addView(wv);
findViewById(R.id.home).setOnClickListener(this);
}
If the webview doesn't exist yet it will be created and after setContentView(R.layout.main) it will be added to the layout. Any other UI customization you need came after this.
and in onConfigurationChanged:
#Override
public void onConfigurationChanged(Configuration newConfig)
{
((LinearLayout)findViewById(R.id.webviewPlace)).removeAllViews();
super.onConfigurationChanged(newConfig);
initUI();
}
In onConfigChange the webview is removed from old placeholder and initui will be called which will add it back to the new layout.
In oncreate() call initui() so the ui will be initialized for the first time.
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
initUI()
}
I wish it would be helpful for someone.
If you are using android:configChanges="orientation|screenSize"
In manifest,it ignores the XML in "layout-land". If you create a different XML for landscape don't use the android:configChanges="orientation|screenSize" tag for that activity in manifest.
put that at layout-land for the layouts you want as landscape.
The idea is, you shouldn't really use configChange="orientation" because it has its downsides. You can find a detailed post here . You should manually handle your state if you want to change your layout. Of course you can programmaiclly do this but if you want to use xml to do this. You can maintain you webView with fragments.
Try to use this code:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.activity_splash);
}
Make your view a relative layout. When the orientation changes just adjust the layout params for each view in code.
Have your buttons be contained in a linear layout
As in..
Portrait
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
buttonLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
buttonLinearLayout.setLayoutParams(lp);
LayoutParams webParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
lp.addRule(RelativeLayout.ABOVE, R.id.buttons);
webView.setLayoutParams(webParams);
Landscape
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
buttonLinearLayout.setOrientation(LinearLayout.VERTICAL);
buttonLinearLayout.setLayoutParams(lp);
LayoutParams webParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
lp.addRule(RelativeLayout.TO_RIGHT_OF, R.id.buttons);
webView.setLayoutParams(webParams);
Make sure the LayoutParams you are using are the RelativeLayout params (always use the Layoutparams of the view's parent)
Add in the manifest
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:label="#string/app_name">
and
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Resources.getSystem().getDisplayMetrics().widthPixels>
Resources.getSystem().getDisplayMetrics().heightPixels)
setContentView(R.layout.activity_layout1);
else setContentView(R.layout.activity_layout2);
...
}
and
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
setContentView(R.layout.activity_layout1);
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
setContentView(R.layout.activity_layout2);
}
}
Documents here:
https://developer.android.com/guide/topics/resources/runtime-changes.html
You can add values-(configuration that you require - eg. land,portrait) folder under resources and mention the layout you need for this configuration.. It's as simple as that.
Please checkout the below link for further information-
http://developer.android.com/training/multiscreen/screensizes.html#TaskUseAliasFilters
Related
I have an activity whose layout I need to change after a rotation and part of the layout is a graph that is drawn using the width and height of the view that it will be placed into. The first time my code runs, the graph is drawn correctly, however after the rotation the width and height of the container view are not correct, in fact they appear to be the view as if it was not rotated.
Here is what I have so far,
In my manifest for the activity I am working:
android:configChanges="keyboardHidden|orientation|screenSize"
In my activity I have these following methods:
onCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
patient_id = extras.getInt("patient_id");
patient_name = extras.getString("patient_name");
historyDurationType = 12;
constructLayout();
}
constructLayout
public void constructLayout(){
if(landScape){
setContentView(R.layout.activity_bg_history_static_land);
//Set buttons
btnTwelve = (Button)findViewById(R.id.btnTwelveHoursLand);
btnTwentyFour = (Button)findViewById(R.id.btnTwentyFourHoursLand);
btnSeven= (Button)findViewById(R.id.btnSevenDaysLand);
btnTwelve.setOnClickListener(this);
btnTwentyFour.setOnClickListener(this);
btnSeven.setOnClickListener(this);
btnTwelve.setBackgroundColor(getResources().getColor(R.color.light_blue_regular));
btnTwentyFour.setBackgroundResource(android.R.drawable.btn_default);
btnSeven.setBackgroundResource(android.R.drawable.btn_default);
}else{
setContentView(R.layout.activity_bg_history_static);
//Set buttons
btnTwelve = (Button)findViewById(R.id.btnTwelveHours);
btnTwentyFour = (Button)findViewById(R.id.btnTwentyFourHours);
btnSeven= (Button)findViewById(R.id.btnSevenDays);
btnTwelve.setOnClickListener(this);
btnTwentyFour.setOnClickListener(this);
btnSeven.setOnClickListener(this);
btnTwelve.setBackgroundColor(getResources().getColor(R.color.light_blue_regular));
btnTwentyFour.setBackgroundResource(android.R.drawable.btn_default);
btnSeven.setBackgroundResource(android.R.drawable.btn_default);
btnComment = (Button)findViewById(R.id.btnCommentGraph);
btnComment.setOnClickListener(this);
populateOtherContent(officialReadings12);
TextView tvStats = (TextView)findViewById(R.id.txtStatistics);
Typeface chunkFiveFont = Typeface.createFromAsset(getAssets(), "fonts/chunkfivettfversion.ttf");
tvStats.setTypeface(chunkFiveFont);
TextView tvReading = (TextView)findViewById(R.id.txtReadingTitle);
tvReading.setTypeface(chunkFiveFont);
comment = null;
}
if(needData){
getLatestReadings();
}
populateGraph();
}
populateGraph
public void populateGraph(){
if(landScape){
graph_container = (LinearLayout)findViewById(R.id.graph_land_content_layout);
}else{
graph_container = (LinearLayout)findViewById(R.id.graph_content_layout);
}
//Create graphlayout
mainGraph_Layout = new RelativeLayout(this);
RelativeLayout.LayoutParams glParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mainGraph_Layout.setId(909);
mainGraph_Layout.setLayoutParams(glParams);
graph_container.addView(mainGraph_Layout);
graph_container.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if(needsGraph){
layoutGraph();
needsGraph = false;
}
}
});
}
layoutGraph
public void layoutGraph(){
viewWidth = mainGraph_Layout.getWidth();
viewHeight = mainGraph_Layout.getHeight();
//MORE STUFF IS HERE BUT NOT IMPORTANT
}
onConfigurationChanged
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
ActionBar actionBar = getActionBar();
if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
//Config is landscape here
actionBar.hide();
needData = false;
landScape = true;
needsGraph = true;
constructLayout();
}else{
//Config is portrait here
actionBar.show();
needData = false;
landScape = false;
needsGraph = true;
constructLayout();
}
}
After rotation, it is at the layoutGraph() viewWidth and viewHeight objects where I have the problem. I had assumed by that point (having used the global layout listener) that the values would be correct. My understanding was that the listener would only have been triggered once "graph_container" was completed (and landscape or portrait) and so when calling layoutGraph() the width and height of "mainGraph_layout" (a child a graph_container, widths and heights set to MATCH_PARENT) would be good to go. It appears that the width and height I am getting are as if the phone is still portrait, and worth noting it appears that the removal of the action bar has also been taken into account.
Sorry for the long question but I thought it best to show all the code. If anything else needs to be shown then please let me know.
Thanks in advance,
Josh
There is a much better way to do this.
Use resource folders
Put your default layout files in res/layout, and the ones for landscape in res/layout-land. In other words, move res/layout/activity_bg_history_static_land.xml to res/layout-land/activity_bg_history_static.xml.
In onCreate, call
setContentView(R.layout.activity_bg_history_static);
The system will pick the file from res/layout-land when you are in landscape orientation, res/layout otherwise.
If you have views that are only present in one layout but not the other e.g. the comment button, wrap the code inside a null check like this:
btnComment = (Button) findViewById(R.id.btnCommentGraph);
if (btnComment != null) {
btnComment.setOnClickListener(this);
}
For populateGraph(), make sure both res/layout/activity_bg_history_static.xml and res/layout-land/activity_bg_history_static.xml has android:id="#+id/R.id.graph_content. Then you can do findViewById(R.id.graph_content) and get the LinearLayout you need.
Save data across rotation
In your activity, override onSaveInstanceState(), and save the data from getLatestReadings() into the bundle.
Then, in onCreate:
if (savedInstanceState == null) {
getLatestReadings();
} else {
// Restore latest readings from savedInstanceState
}
With that, you can let the system handle the rotation i.e. remove this from your manifest:
android:configChanges="keyboardHidden|orientation|screenSize"
Since the system is handling the rotation, you don't need to have a view tree observer any more. And you don't have to override onConfigurationChanged.
I'm using SwipeView (http://jasonfry.co.uk/blog/android-swipeview/) inside my activity's layout to display several pages. Moreover, I want to handle the orientation changes manually so I added
android:configChanges="orientation|keyboardHidden|screenSize"
into the application's manifest, so that onDestroy/onCreate are not called. Unfortunately, by doing this, the elements inside the swipeview are not resized accordingly. I already tried the following methods:
invalidate
requestLayout
forceLayout
None of them did the trick!
Any help would be appreciated.
Thanks
In case anyone struggles with this, I finally found a solution.
The trick was to request layout not on the swipeview itself but rather on getChildContainer().
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
SwipeView swipeView = (SwipeView)findViewById(R.id.swipeView);
int pageWidth = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();
swipeView.setPageWidth(pageWidth);
for ( int i = 0; i < swipeView.getChildContainer().getChildCount(); i++ )
swipeView.getChildContainer().getChildAt(i).getLayoutParams().width = pageWidth;
swipeView.getChildContainer().requestLayout();
swipeView.scrollToPage(swipeView.getCurrentPage());
}
I seem to be going bald trying to fix what probably is a very simple problem.
I have an activity in which users can click a button and an ImageView is dynamically added to the screen. This ImageView is also stored in an ArrayList. When the user pauses the activity or the device is rotated all the images added to the layout disappears. They are still stored in the ArrayList however. I attempted to loop through the ArrayList to add the images to the layout again, however then I get an error thrown - the specified child already has a parent.
Below is my code for adding images to the layout and the ArrayList
public void AddImage() {
RelativeLayout rl = (RelativeLayout) findViewById(R.id.top);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
iv = new ImageView(this);
id++;
iv.setId(id);
params.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
iv.setImageResource(R.drawable.foo);
iv.setLayoutParams(params);
rl.addView(iv);
arrayList.add(iv);
}
You need to create Map instead of List and then do something like :
map.put(imageViewId,R.drawable.imageViewResource);
Then after pause just do :
RelativeLayout rl = (RelativeLayout) findViewById(R.id.top);
ImageView imageView = (ImageView)rl.findViewById(imageViewId);
Integer imageViewResource = map.get(imageViewId);
imageView.setImageResource(imageViewResource);
If this will not update your image try to :
imageView.post(new Runnable() {
#Override
public void run()
{
imageView.setImageResource(imageViewResource);
}
});
If your map is empty after rotation and you haven't time to play with save/restore instance state, just make map static.
Best wishes.
Declare your imageview variable within the scope of the method:
ImageView iv = new ImageView(this);
You cannot reuse the variable once it is added.
Add configChanges for that activity in manifest
<activity name="Your ClassName with Package" android:configChanges="orientation"/>
and call your method AddImage() in OnCongigurationChanged as below:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int ot = getResources().getConfiguration().orientation;
switch (ot) {
case Configuration.ORIENTATION_LANDSCAPE:
AddImage();
break;
case Configuration.ORIENTATION_PORTRAIT:
AddImage();
break;
}
}
#SuppressWarnings("deprecation")
#Override
public Object onRetainNonConfigurationInstance() {
return super.onRetainNonConfigurationInstance();
}
Add configChanges for that activity in manifest
<activity
....
android:configChanges="orientation"
.... >
I have a linear layout wrapping a button, a gridview and a textview set in a xml file. When the screen is in portrait orientation all these elements are displayed fine. However, when I change the orientation of the screen into landscape orientation the textview disappears. I'd like to change the layout on configuration change. I'd like to display the elements horizontally in a way that each of them takes up a certain percentage of the screen. I also have to change the number of items shown per row in the gridview, and maybe some others parameters. Until now I have this, but i don't know how to change the rest of the parameters.
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
LinearLayout linearlayout= (LinearLayout) findViewById(R.id.LinearLayout02);
GridView gridview=(GridView) findViewById(R.id.gridview);
if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
linearlayout.setOrientation(LinearLayout.HORIZONTAL);
}
else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
linearlayout.setOrientation(LinearLayout.VERTICAL);
}
}
You'll want to create a folder in your /res/ directory that is titled layout-land and then create a new layout xml in that folder that is the same name as your other layout and place the items (TextView, etc.) where you would like them when the device is in landscape and from there Android will take care of the rest.
Edit: Use the following code:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.my_main_layout);
//save your text from textviews, edittexts, etc. in temp variables
InitializeUI(); //take your findViewById stuff out of onCreate and put it in its own method that can be called here as well.
//set you textviews, etc back to previous values from temp vars
}
That should do it.
I defined dynamically all GUI components.When the Cntrl+F11 is first pressed the layout updates correctly to from portrait to landscape. But when I press Cntrl+F11 again, layout remains in its "landscape" look. How can I update it for "portrait" look again? The source code:
//Main Layout ll
LinearLayout ll ;
ll.setOrientation (LinearLayout.VERTICAL);
ll.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
//Inner layout ll2 is inside of the main layout ll
LinearLayout ll2 = new LinearLayout (this);
ll2.setWeight(1.0f);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT);
lp.weight = 0.25f;
//Button b is inside of the inner layout ll2
Button b = new Button (this);
b.setLayoutParams(lp);
ll2.addView (b);
ll.addView(ll2);
I tried this solution (from this site):
I added function
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(ll);
}
and set I in AndroidManifest
android:configChanges="keyboardHidden|orientation"
but it stiil doesn't work.Thanks.
use this in xml file
android:orientatoin="portrait "
it is also possible to write in onCreate() method also
You Also specify in ANDROID_MANIFEST like this
<activity android:name=".activityName" android:screenOrientation="landscape"></activity>
To revert configuration back to Portrait use key-combinations ctrl+F9