Android View width and height have not changed after rotation - android

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.

Related

Set presentationDisplay Orientation programmatically

The app I am working on will have 2 displays. Using the DisplayManager I am casting my presentation to a second screen.
I want to find a way to set the orientation of my presentation class to landscape and my activity class to portrait. Code bellow called onCreate of activity to render the presentation.
#Override
protected void onCreate(Bundle savedInstanceState) {
// Get the display manager service.
mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
Display[] presentationDisplays = mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
if (presentationDisplays.length > 0) {
mProductPresentation = new ProductPresentation(this, presentationDisplays[0], product);
mProductPresentation.show();
Log.d(TAG, " on display #" + presentationDisplays[0].getDisplayId() + ".");
}
}
The code works perfectly and the display gets rendered correctly. I just want to know if its possible to flip the orientation of the presentation class without changing the orientation of the activity
I ended up just grabbing the layout and rotating it. See code below
#Override
protected void onCreate(Bundle savedInstanceState) {
// Be sure to call the super class.
super.onCreate(savedInstanceState);
//Assign the correct layout
mViewLayout = getLayoutInflater().inflate(R.layout.presentation_product_details, null);
setContentView(mViewLayout);
//We need to wait till the view is created so we can flip it and set the width & height dynamically
mViewLayout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener());
}
private class OnGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener{
#Override
public void onGlobalLayout() {
//height is ready
mViewLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int width = mViewLayout.getWidth();
int height = mViewLayout.getHeight();
mViewLayout.setTranslationX((width - height) / 2);
mViewLayout.setTranslationY((height - width) / 2);
mViewLayout.setRotation(90.0f);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( height, width);
// Inflate the layout.
setContentView(mViewLayout, lp);
findViews();
}
}

Android: Can I set converted view layout in getView()

I'm setting different height for my custom listview items based on screen orientation, and as in code below I listen to screen orientation changes, and set a global value according to it, and when getView(...) gets called on listview items I set the height of the converted view.
My question is, is there a better solution than this?
How bad this approach affect the UI loading speed process?
I'm targeting API14+
P.S: (200 & 300) below are added here as example, they are not fixed at runtime, they are changed during runtime according to screen dpi.
int mConvertViewHeight;
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
{
mConvertViewHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
}
else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
{
mConvertViewHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300, getResources().getDisplayMetrics());
}
}
In my listview custom array adapter
#Override
public View getView(final int aConvertViewPosition, View aConvertView, ViewGroup parent)
{
mParams = aConvertView.getLayoutParams();
mParams.height = mMainActivity.mConvertViewHeight;
}
If you need just different item view in portrait/landscape mode, you can create different layouts in layout-land and layout-port folders

Three sliding panes - the Middle pane above Left and Right panes

I am trying to have the same navigation style as Viber's interface (the discussion page), without using a third-part Library such as SlidingMenu.
I thought that they have used SlidingPaneLayout to achieve this nice effect, but when I tried to code it, I noticed that the last pane is always over the second.
My questions :
Is this really a SlidingPaneLayout ?
If yes how to achieve this please ?
If no, is there an android native way to do the same thing ?!
Left Pane
Right Pane
First of all declare this all variable in your Class
/** Sliding Menu */
boolean alreadyShowing = false;
private int windowWidth;
private Animation animationClasses;
private RelativeLayout classesSlider;
LayoutInflater layoutInflaterClasses;
then inside onCreate method declare this, this will help you to get screen's height and width
Display display = getWindowManager().getDefaultDisplay();
windowWidth = display.getWidth();
display.getHeight();
layoutInflaterClasses = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
and then any of your button or image where by clicking you want to open slider put below code.
findViewById(R.id.slidermenu).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (!alreadyShowing) {
alreadyShowing = true;
openSlidingMenu();
}
}
});
and then outside the onCreate declare openSlidingMenu() as below.
private void openSlidingMenu() {
// showFadePopup();
int width = (int) (windowWidth * 0.8f);
translateView((float) (width));
#SuppressWarnings("deprecation")
int height = LayoutParams.FILL_PARENT;
// creating a popup
final View layout = layoutInflaterClasses.inflate(
R.layout.option_popup_layout,
(ViewGroup) findViewById(R.id.popup_element));
ImageView imageViewassignment = (ImageView) layout
.findViewById(R.id.assignment);
imageViewassignment.setOnClickListener(this);
final PopupWindow optionsPopup = new PopupWindow(layout, width, height,
true);
optionsPopup.setBackgroundDrawable(new PaintDrawable());
optionsPopup.showAtLocation(layout, Gravity.NO_GRAVITY, 0, 0);
optionsPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
public void onDismiss() {
// to clear the previous animation transition in
cleanUp();
// move the view out
translateView(0);
// to clear the latest animation transition out
cleanUp();
// resetting the variable
alreadyShowing = false;
}
});
}
just replace
final View layout = layoutInflaterClasses.inflate(
R.layout.option_popup_layout,
(ViewGroup) findViewById(R.id.popup_element));
this above code with your custom screen XML name and by it's ID. and here is other methos's which you need.
private void translateView(float right) {
animationClasses = new TranslateAnimation(0f, right, 0f, 0f);
animationClasses.setDuration(100);
animationClasses.setFillEnabled(true);
animationClasses.setFillAfter(true);
classesSlider = (RelativeLayout) findViewById(R.id.classes_slider);
classesSlider.startAnimation(animationClasses);
classesSlider.setVisibility(View.VISIBLE);
}
private void cleanUp() {
if (null != classesSlider) {
classesSlider.clearAnimation();
classesSlider = null;
}
if (null != animationClasses) {
animationClasses.cancel();
animationClasses = null;
}
}
remember here animationClasses = new TranslateAnimation(0f, right, 0f, 0f); you can play with this parameter for some different effect and also do not forget to change this line's ID with your current screen's ID like for example check below id
classesSlider = (RelativeLayout) findViewById(R.id.classes_slider);
here you need to replace this ID with your Current java screen's XML file's ID.
Hope this will help you.

Resize elements in bar chart by multiplying with a factor

I am trying to create a bar chart in android where each bar consists of one Viewand one TextView.
I add these cells to a HorizontalScrollView. This works fine, all my bars appear.
Now I want to resize these bars(as they now all appear in full height) by multiplying them with a factor.
This is the activity class that i Use.
private ViewGroup linearScrollLayout;
private HorizontalScrollView hScrollView;
private ArrayList<NutrientCell> nutrientsData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.box_plot);
this.hScrollView = (HorizontalScrollView) findViewById(R.id.horizontalScrollView1);
this.linearScrollLayout = (ViewGroup) this.hScrollView
.findViewById(R.id.linearScrollLayout);
try {
Intent intent = getIntent();
this.nutrientsData = (ArrayList<NutrientCell>) intent
.getSerializableExtra("nutrientsData");
//
} catch (Exception e) {
// TODO: handle exception
}
this.initChart();
}
private void initChart() {
for (NutrientCell c : this.nutrientsData) {
RelativeLayout rlt = (RelativeLayout) getLayoutInflater().inflate(
R.layout.hv_boxplot_cell, this.linearScrollLayout, false);
((TextView) rlt.findViewById(R.id.textView1)).setText(c
.getNutrientName().toString());
View v = ((View) rlt.findViewById(R.id.vv1));
v.setBackgroundColor(c.getColor());
this.linearScrollLayout.addView(rlt);
}
}
Now, in the loop found in initChart() I iterate over all elements in an ArrayList, each of which has a percentage variable. This is the variable I would like to multiply the height with.
I have tried to just multiply the height of the LayoutParams of the inflated RelativeLayout found in initChart but as this is called in onCreate(), its height property is not correct.
So how can I make sure that the inflated RelativeLayout I add to my View has a height corresponding to the percentage obtained from c(c.getPercentage())
What exactly is the percentage used for? If the percentage is used to scale the view based on it's initial size, you could probably use the view's setScaleY() method to change it's height.

Android: Configuration Change Not Updating

I am using the XML tag
android:configChanges="orientation|keyboardHidden|keyboard"
and the following code to detect device orientation changes and change layouts:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
disp = getWindowManager().getDefaultDisplay();
swidth = disp.getWidth();
sheight = disp.getHeight();
parent.removeAllViews();
parent = new RelativeLayout(this);
if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
layoutPortrait();
else
layoutLandscape();
}
This works fine going form portrait to landscape. But, for whatever reason, going from landscape to portrait (starting in landscape or switching to it then back) doesn't change the screen back to portrait.
Through the use of Log messages I've determined that after being in Landscape mode, the Display and Configuration classes DO NOT UPDATE. They remain holding the same orientation/length/width values as when the device was in landscape.
Does anyone have any idea why this is?
Additional Code (requested by contributor)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
disp = getWindowManager().getDefaultDisplay();
swidth = disp.getWidth();
sheight = disp.getHeight();
int ornt;
if(swidth == sheight)
ornt = Configuration.ORIENTATION_SQUARE;
else if(swidth < sheight)
ornt = Configuration.ORIENTATION_PORTRAIT;
else if(swidth > sheight)
ornt = Configuration.ORIENTATION_LANDSCAPE;
else
ornt = Configuration.ORIENTATION_UNDEFINED;
parent = new RelativeLayout(this);
labelOne = new TextView(this);
labelOne.setText("Temperature");
labelOne.setId((int)(Math.random()*Integer.MAX_VALUE));
temp = new EditText(this);
temp.setSingleLine(true);
temp.setBackgroundColor(Color.WHITE);
temp.setTextColor(Color.BLACK);
temp.setId((int)(Math.random()*Integer.MAX_VALUE));
labelTwo = new TextView(this);
labelTwo.setText("Humidity(%)");
labelTwo.setId((int)(Math.random()*Integer.MAX_VALUE));
humidity = new EditText(this);
humidity.setSingleLine(true);
humidity.setBackgroundColor(Color.WHITE);
humidity.setTextColor(Color.BLACK);
humidity.setId((int)(Math.random()*Integer.MAX_VALUE));
output = new TextView(this);
output.setBackgroundColor(Color.WHITE);
output.setTextColor(Color.BLACK);
output.setId((int)(Math.random()*Integer.MAX_VALUE));
submit = new Button(this);
submit.setText("Calculate");
submit.setId((int)(Math.random()*Integer.MAX_VALUE));
submit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Double result = Calculate.calculate(getInputs());
if(result !=null) {
String answer = String.format("%,.1f",result);
if(f.isChecked()) {
output.setText(answer + "°F");
} else {
output.setText(answer + "°C");
}
}
}
});
f = new RadioButton(this);
f.setText("Fahrenheit");
c = new RadioButton(this);
c.setText("Celsius");
rg = new RadioGroup(this);
rg.setOnCheckedChangeListener(new ListenForMode());
rg.addView(f);
rg.addView(c);
rg.setId((int)(Math.random()*Integer.MAX_VALUE));
f.setChecked(true);
if(ornt == Configuration.ORIENTATION_PORTRAIT || ornt == Configuration.ORIENTATION_SQUARE)
layoutPortrait();
else
layoutLandscape();
}
Final Update
Issue is with the emulator not responding correctly to the orientation changes. I changed the onConfigurationChanged(...) orientation checking to use the condition width < height for portrait and all else landscape. This works perfectly on my Android device.
Thanks to all contributors to this question!
You really shouldn't be overriding the configChanges in your manifest. Android can handle redrawing the Views for you, and it actually handles it really well, especially if you have filtered layouts in your resource folder.
If you are overriding configChanges because of issues with AsyncTask losing its Context (as I once did), I would recommend using an IntentService to handle your asynchronous work as Services aren't tied to any one Activity.
If it happens on emulator, then it's OK - emulator behaves a little bit weird in sense of configuration changes. I have been trying to solve similar problem, unless I noticed that the app worked fine on a real device.
It seems that the emulator ignores the android:configChanges="orientation" attribute, but in a bit strange way: it does force the configuration change but doesn't call onConfigurationChanged(..) method every time it should be called.

Categories

Resources