0

I am needing to reformat an existing Android app to have a landscape version. It uses a TextWatcher to calculate a tip amount when numbers are entered into 2 EditTexts (bill amount & tip percent). It works by itself, but when I add a second layout, the TextWatcher stops working and doesn't calculate anything. It rotates and looks how I want it to, but doesn't function. My landscape layout has all the same parts as the portrait with the same EditText & TextView IDs. This is what I am using to change the layouts:

package com.example.tiporientation; import androidx.appcompat.app.AppCompatActivity; import android.content.res.Configuration; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.widget.EditText; import android.widget.TextView; import java.text.NumberFormat; public class MainActivity extends AppCompatActivity { private TipCalculator tipCalc; private NumberFormat money = NumberFormat.getCurrencyInstance(); private EditText billEditText; private EditText tipEditText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tipCalc = new TipCalculator(.17f,100); setContentView(R.layout.activity_main); billEditText = findViewById(R.id.EDIT_BillAmount); tipEditText = findViewById(R.id.EDIT_EnterTip); //create inner-class for this... puts it at bottom of MainActivity //TextChangeHandler is a "listener" //attach it to our EDIT texts, so it is listening to changes in bill $ & tip % TextChangeHandler tch = new TextChangeHandler(); billEditText.addTextChangedListener(tch); tipEditText.addTextChangedListener(tch); Configuration config = getResources().getConfiguration(); modifyLayout(config); } private void modifyLayout(Configuration newConfig) { if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) setContentView(R.layout.activity_main_landscape); //we create new XML for this layout else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) setContentView(R.layout.activity_main); } public void onConfigurationChanged(Configuration newConfig) { Log.w("MainActivity", "Inside onConfigurationChanged"); super.onConfigurationChanged(newConfig); modifyLayout(newConfig); //if orientation changes again, send back to modifyLayout } public void calculate () { //convert edit texts to a string ??? String billString = billEditText.getText().toString(); String tipString = tipEditText.getText().toString(); //2 text views from XML TextView tipTextView = findViewById(R.id.TXT_TipTotal); TextView totalTextView = findViewById(R.id.TXT_TotalAmount); try { //convert billString to float & tipString to int -- can't do math with strings float billAmount = Float.parseFloat(billString); int tipPercent = Integer.parseInt(tipString); //update the model -- referencing TipCalculator class! tipCalc.setBill(billAmount); tipCalc.setTip(.01f * tipPercent); //ask model to calculate float tip = tipCalc.tipAmount(); float total = tipCalc.totalAmount(); //update view with formatted tip & total amount tipTextView.setText(money.format(tip)); totalTextView.setText(money.format(total)); } catch(NumberFormatException nfe) { } } //create implements... select all 3 they are then stubbed out in the class private class TextChangeHandler implements TextWatcher { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { calculate(); } } } 

Manifest:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.tiporientation"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:configChanges="orientation|screenSize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 
4
  • Hi - you do not need to change the layouts manually. Just in the layout preview of Android Studio (xml editor) select "Create landscape" version and let studio assign everything for you. This way the correct layout file is placed in the correct folder and you do not need any code at all to rotate. Post the registration of your activity in the manifest - are there configuration entries? the method you posted is from the onConfigurationChanged callback? Commented Mar 5, 2020 at 18:28
  • My assignment wants us to make 2 separate XMLs and use an if/else if like I used above to switch between the two. Commented Mar 5, 2020 at 18:45
  • There is nothing wrong with 2 xml's --- android studio will do the same. It's the place WHERE they are stored and let android do what it can do best. Still missing your manifest registration of the activity and your onConfigrationChanged method. can't help without that Commented Mar 6, 2020 at 5:40
  • updated I added my whole MainActivity and manifest !! Commented Mar 6, 2020 at 16:01

1 Answer 1

1

With the onConfigurationChanged callback your activity is destroyed and re-created. Values in your text fields can be stored in the savedInstanceState and then retrieved in onCreate.

I am not entirely sure, but I think what happens here is:

  • Phone rotates
  • Phone recreates the activity (your onCreate runs)
  • you receive the onConfiguration callback
  • there you assign a new contentView(_landscape)
  • and with this you destroy the layout just rendered in onCreate and your listeners

Your onCreate has already run, thus your TextWatcher was attached to a layout you just destroyed with your setContentView call from modifyLayout.

I still think you should let android manage this for you, but to solve the problem, I suggest:

  • remove the code from onCreate beginning with the first findViewById up to (and including) the .addTextWatcher lines

  • remove the other clutter with an additional modifyLayout call from your onCreate

  • put this code in a method, say connectWatchers

  • call this method from onCreate

  • call this method from modifyLayout

It should look like this:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tipCalc = new TipCalculator(.17f,100); setContentView(R.layout.activity_main); connectWatchers(); } private void modifyLayout(Configuration newConfig) { if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) setContentView(R.layout.activity_main_landscape); //we create new XML for this layout else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) setContentView(R.layout.activity_main); connectWatchers(); } public void onConfigurationChanged(Configuration newConfig) { Log.w("MainActivity", "Inside onConfigurationChanged"); super.onConfigurationChanged(newConfig); modifyLayout(newConfig); //if orientation changes again, send back to modifyLayout } private void connectWatchers() { billEditText = findViewById(R.id.EDIT_BillAmount); tipEditText = findViewById(R.id.EDIT_EnterTip); TextChangeHandler tch = new TextChangeHandler(); billEditText.addTextChangedListener(tch); tipEditText.addTextChangedListener(tch); } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.