Chapter 4 Multiple Activities Passing Data between Activities

Chapter 4: Multiple Activities, Passing Data between Activities, Transitions, Persistent Data

Learning Objectives • What is an activity – Build an app involving more than one activity – Discuss ways for activity to access the same data • Use a Table Layout with Relative. Layout • Handle persistent data – Saving data to the device • Animate the transition between screens

Mortgage App • • More Model-View-Controller Two Screens Two Activities Relative. Layout, Table. Layout Go back and forth between the two Activities • Pass data between Activities • Handle persistent data • Animated transition between screens

Mortgage—the Model • The Mortgage class encapsulates a mortgage. • Amount, interest rate, number of years • Methods to calculate the monthly payment, the total payments • Take a look at the actual app.

Table. Layout (1 of 8) • We use a Table. Layout for screen 1. • Table. Layout is a subclass of Linear. Layout, itself a subclass of View. Group. • A Table. Layout arranges its children in rows and columns.

Table. Layout (2 of 8) • 6 rows, 2 columns • First 5 rows: two Text. Views: label and data • Last row: button (to go to second activity/screen) – Looks like we don’t have to worry about merging cells like in HTML.

Table. Layout (3 of 8) • Typically uses a Table. Row to define a row • Inside a Table. Row, we place two Text. Views for the first 5 rows. • We separate the first 3 rows (user input data) from the next 2 rows (calculated data) with a red line.

Table. Layout (4 of 8) <Table. Row android: layout_width="wrap_content" android: layout_height="wrap_content" > <Text. View android: text="@string/label_amount" android: padding="10 dip" /> <Text. View android: id="@+id/amount" android: text="@string/amount" /> </Table. Row>

Table. Layout (5 of 8) • We use strings defined in strings. xml as needed. • We give ids to elements as needed.

Table. Layout (6 of 8) <!-- red line --> <View android: layout_height="5 dip" android: background="#FF 0000" /> • Looks like this spans the 2 columns.

Table. Layout (7 of 8) • The last row is just one Button. • We center it within the row. • When the user clicks on the button, the modify. Data method executes.

Table. Layout (8 of 8) <Table. Row android: layout_width="wrap_content" android: layout_height="wrap_content" android: gravity="center" android: padding. Top="50 dip" > <Button android: text="@string/modify_data" android: on. Click="modify. Data" /> </Table. Row>

strings. xml • In Version 0, we hard code values in the strings. xml file. • We do not use the Model. We fill the Text. Views with those hard coded values.

styles. xml • We edit styles. xml, defining a text size. <item name = "android: text. Size">42 sp </item> This seems way to big

Screen 2 (1 of 3) • Screen 2 is for user input. • When the user clicks on the button of screen 1, we go to screen 2. • We use a Relative. Layout.

Screen 2 (2 of 3) • Number of years Three radio buttons (10, 15, 30 years) • Mortgage amount Edit. Text • Interest rate Edit. Text

Screen 2 (3 of 3)

Radio Buttons • Radio. Button class/XML element • Each Radio. Button element is placed inside a Radio. Group element they are mutually exclusive.

Radio. Group <Radio. Group android: layout_to. Right. Of="@+id/label_years" android: layout_width="match_parent" android: layout_height="wrap_content" android: layout_align. Left="@+id/data_rate" android: orientation="horizontal">

Radio. Button <Radio. Button android: layout_width="wrap_content" android: layout_height="wrap_content" android: id="@+id/ten" android: text="@string/ten" />

strings. xml • We add strings to strings. xml for the elements in the second screen. . . <string name="thirty">30 </string> <string name="amount. Decimal">100000. 00 </string>. . .

Testing Screen 2 • We can look at screen 2 in the preview OR • We can change the View resource in Main. Activity and run the app // set. Content. View( R. layout. activity_main ); set. Content. View( R. layout. activity_data );

Connecting the Two Activities (1 of 2) • In Version 1, we add code to Main. Activity to go to the second activity and also come back to the first activity. • We create a class for the second activity, Data. Activity. • We edit Android. Manifest. xml to include a second activity element.

Connecting the Two Activities (2 of 2) • Activities are organized on a stack. • What the user sees is the View of the Activity at the top of the stack. • We go to another activity that activity is placed at the top of the stack. • We finish an activity that activity is popped off the stack.

Activity Class Methods public void start. Activity( Intent intent Launches a new activity using intent the Intent to start it. public void finish( ) Closes this activity and pops it off stack; the screen for the prior activity shown. ) as the is

Intent Constructor Intent( Context context, Class<? > cls ) Constructs an Intent that is intended to execute a class modeled by type ? (probably some Activity class) that is in the same application package as context From what I gather “Intent” is used to run a resource on your device Broadcasts the “intent” to start something (like an activity) and can carry information with it. Can do other things like get a photo form another app. Ex. ) # Start the activity connect to the # specified class Intent i = new Intent(this, Activity. Two. class); start. Activity(i);

From Screen 1 to Screen 2 (1 of 2) • Inside modify. Data of Main. Activity • Create an Intent to go to a Data. Activity • Execute that Intent and start that Data. Activity

From Screen 1 to Screen 2 (2 of 2) public void modify. Data( View v ) { Intent my. Intent = new Intent( this, Data. Activity. class ); this. start. Activity( my. Intent ); }

Back to Screen 1 From Screen 2 (1 of 2) • Finish current activity (this Data. Activity) • That pops it off the stack. • The View for previous activity (Main. Activity) the shows

Back to Screen 1 From Screen 2 (2 of 2) • Inside Data. Activity. public void go. Back( View v ) { this. finish( ); }

Android. Manifest. xml (1 of 2) • The Android Manifest – keeps track of all resources/components of the app. – Sets permissions – Hardware & software features the app needs • Add an activity element for the second activity (Data. Activity) • Place it inside the application element (like the first activity element)

Android. Manifest. xml (2 of 2) <application …> <activity android: name=". Main. Activity" … </activity> <activity android: name=". Data. Activity" android: label="@string/app_name" > </activity> Try running it to test the 2 screen functionality

Life Cycle of an Activity • An activity has a life. • Some of its methods are called automatically (on. Create among others). • . . . When the activity goes in the background, comes back to the foreground, is finished. . .

Life Cycle Methods of Activity (2 of 4) • on. Create: called when the activity is first created • on. Start: called after on. Create, when the activity becomes visible • on. Resume: called after on. Start, when the user starts interacting with the activity

Life Cycle Methods of Activity (3 of 4) • on. Pause: called when Android starts or resumes another activity • on. Stop: called when the activity becomes invisible to the user • on. Restart: called when the activity is about to restart • on. Destroy: called when the activity has ended

Life Cycle Methods of Activity (4 of 4) • Let’s look at the Main. Activity and Data. Activity • We use the Logcat to see the debug messages

Sharing Data between Activities (1 of 8) • We want to pass data from screen 2 to screen 1 but also from screen 1 to screen 2. • There are many ways of doing that. – Page 150 lists some ways

Sharing Data between Activities (2 of 8) • Use the put. Extra methods of the Intent class. • We can only pass primitive data types or Strings. • not very convenient here because we want to pass the whole Mortgage object.

Sharing Data between Activities (3 of 8) • An easy way to pass (or more exactly share) data between the two activities is to declare the Mortgage instance variable public and static in the Main. Activity class. public static Mortgage mortgage; • we can access it inside the Data. Activity class using Main. Activity. mortgage. Making the class global

Sharing Data between Activities (4 of 8) • We could also rewrite the Mortgage class as a singleton class. • in this way, only one object of that class can be instantiated. • it is automatically shared among the various activities.

Sharing Data between Activities (5 of 8) • Use a file or a sqllite database to store the data. • Read and write as needed. • Either way, that is an overkill for this app.

Sharing Data between Activities (6 of 8) • For this app, the simplest way to share data among activities is to make the mortgage instance variable public and static. • Note: we will look at the put. Extra method later in the book.

Sharing Data between Activities (7 of 8) • In Main. Activity, we add the update. View method to update the various Text. Views based on data in the Model (mortgage instance variable). • Call update. View inside on. Start

Sharing Data between Activities (8 of 8) • In Data. Activity, add an update. View and an update. Mortgage. Object methods to: – Update the various GUI elements – Update the Model data after user input

Updating the GUI in Screen 1 (1 of 4) • Use the find. View. By. Id method to retrieve a Text. View. • Update the text inside the Text. View with data retrieved from the Model.

Updating the GUI in Screen 1 (2 of 4) public void update. View( ) { // retrieve all the Text. Views // update all the Text. Views // based on the data inside the Model }

Updating the GUI in Screen 1 (3 of 4) public void update. View( ) { Text. View amount. TV = ( Text. View ) find. View. By. Id( R. id. amount ); amount. TV. set. Text( mortgage. get. Formatted. Amount( ) ); Text. View years. TV = ( Text. View ) find. View. By. Id( R. id. years ); years. TV. set. Text( "" + mortgage. get. Years( ) ); … }

Updating the GUI in Screen 1 (4 of 4) • Call update. View every time the View shows. public void on. Start( ) { super. on. Start( ); update. View( ); }

Updating the GUI in Screen 2 (1 of 8) • Same as for screen 1 • Use the find. View. By. Id method to retrieve a Text. View or a Radio. Button • Update the text inside the Text. View with data retrieved from the Model • Check the radio button depending on state of Model

Updating the GUI in Screen 2 (2 of 8) public void update. View( ) { // Get a reference to our Mortgage object Mortgage mortgage =Main. Activity. mortgage; // update the View }

Updating the GUI in Screen 2 (3 of 8) // update the radio buttons if( mortgage. get. Years( ) == 10 ) { Radio. Button rb 10 = ( Radio. Button ) find. View. By. Id( R. id. ten ); rb 10. set. Checked( true ); } else if( mortgage. get. Years( ) == 15 ) { Radio. Button rb 15 = ( Radio. Button ) find. View. By. Id( R. id. fifteen ); rb 15. set. Checked( true ); } // else do nothing (default is 30)

Updating the GUI in Screen 2 (4 of 8) // update the Edit. Texts Edit. Text amount. ET = ( Edit. Text ) find. View. By. Id( R. id. data_amount ); amount. ET. set. Text( "" + mortgage. get. Amount( ) ); Edit. Text rate. ET = ( Edit. Text ) find. View. By. Id( R. id. data_rate ); rate. ET. set. Text( "" + mortgage. get. Rate( ) );

Updating the GUI in Screen 2 (5 of 8) • Access the Mortgage object using Main. Activity. mortgage • Access the GUI elements using the find. View. By. Id method • Retrieve user input • Convert user input (a String) to a float as necessary • Use the mutators of the Mortgage class to change the data inside the Mortgage object

Updating the GUI in Screen 2 (6 of 8) public void update. Mortgage. Object( ) { Mortgage mortgage = Main. Activity. mortgage; // update years in Model Radio. Button rb 10 = ( Radio. Button ) find. View. By. Id( R. id. ten ); Radio. Button rb 15 = ( Radio. Button ) find. View. By. Id( R. id. fifteen ); int years = 30; if( rb 10. is. Checked( ) ) years = 10; else if( rb 15. is. Checked( ) ) years = 15; mortgage. set. Years( years ); … }

Updating the GUI in Screen 2 (7 of 8) // update data amount in Model Edit. Text amount. ET = ( Edit. Text ) find. View. By. Id( R. id. data_amount ); String amount. String = amount. ET. get. Text( ). to. String( ); . . . try { float amount = Float. parse. Float( amount. String ); mortgage. set. Amount( amount ); … } catch( Number. Format. Exception nfe ) { mortgage. set. Amount( 100000. 0 f ); … }

Updating the GUI in Screen 2 (8 of 8) • We update the state of the Model before we go back to activity 1. public void go. Back( View v ) { update. Mortgage. Object( ); this. finish( ); }

Transitions/Animations • In Version 3, we enhance the user experience by setting up transitions between screens. • Fade, scale, move left to right … • Frame by frame or tween (automatically defined between a start frame and an end frame).

Animations XML Class Description set Animation. Set A set of several animations alpha Alpha. Animation Fade in or out Rotate. Animation Rotate around a fixed point scale Scale. Animation Scale from a fixed point translate Translate. Animation Sliding animation

Transitions/Animations (1 of 2) • We can use either XML or code to define animations. • We use XML.

Transitions/Animations (2 of 2) • Create a directory (anim) inside the res directory. • Define animations in XML (one animation per XML file). • Each XML element has attributes to define the animation.

slide_from_left. xml (1 of 2) • Translation: main parameters are: – From position, to position, duration. <translate android: from. XDelta="-100%p" android: to. XDelta="0" android: duration="4000" />

slide_from_left. xml (2 of 2) <? xml version="1. 0" encoding="utf-8"? > <set xmlns: android= "http: //schemas. android. com/apk/res/android" android: interpolator= "@android: anim/accelerate_interpolator" > <translate android: from. XDelta="-100%p" android: to. XDelta="0" android: duration="4000" /> </set>

fade_in_and_scale. xml (1 of 2) • Can define several animations that will run concurrently • We place them inside a set element. • First, we define a fade animation. • We follow it with a scale animation.

fade_in_and_scale. xml (2 of 2) • • Translation: main parameters From position, to position, duration <set … > <alpha android: from. Alpha="0. 0" android: to. Alpha="1. 0" android: duration="3000" /> <scale android: from. XScale="0. 0" android: to. XScale="1. 0" … /> </set>

Specifying a Transition • Now that we have defined animations, we can use them in order to transition from screen 1 to screen 2 and back from screen 2 to screen 1.

override. Pending. Transition (1 of 2) • Two animation resources are specified. • One to enter the new activity and one to exit the current activity.

override. Pending. Transition (2 of 2) • We use this method from the Activity class to specify a transition from one activity to another. void override. Pending. Transition ( int enter. Anim. Resource, int exit. Anim. Resource )

Inside Main. Activity public void modify. Data( View v ) { Intent my. Intent = new Intent( this, Data. Activity. class ); this. start. Activity( my. Intent ); override. Pending. Transition( R. anim. slide_from_left, 0 ); /* 0 no animation is used to transition from screen 1 */ }

Inside Data. Activity public void go. Back( View v ) { update. Mortgage. Object( ); this. finish( ); override. Pending. Transition( R. anim. fade_in_and_scale, 0 ); }

Managing Persistent Data (1 of 5) • Next time we use the app, we want the app to remember the last mortgage data and use that data on the first and second screens. • that data is persistent. • In Version 4, we make the data persistent.

Managing Persistent Data (2 of 5) • When the user uses the app for the first time, we show the default values for the three mortgage parameters: the mortgage amount, the interest rate, and the number of years. • But when the user uses the app again, we want to show the values that were used the last time the user used the app.

Managing Persistent Data (3 of 5) • In order to implement that functionality, we write to a file on the device the mortgage parameters every time they are changed. • When we start the app the first time, the file does not exist and we use the default parameters for the mortgage. When we run the app afterwards, we read the mortgage parameters from the file.

Managing Persistent Data (4 of 5) • Although we could use the open. File. Output and open. File. Input methods of the Context. Wrapper class to open a file for writing and reading, it is easier to use the user preferences system in order to store and retrieve persistent data.

Managing Persistent Data (5 of 5) • Preferences for an app are organized as a set of key/value pairs, like a hashtable. • In this app, since we have three values for a mortgage, we have three key/value pairs.

Shared. Preferences • The Shared. Preferences interface includes the functionality to write to and read from the user preferences. • Its static inner interface, Editor, enables us to store user preferences.

Shared. Preferences. Editor • We can use the put. Data. Type methods of Shared. Preferences. Editor to store data on the device. They have this general method header: public Shared. Preferences. Editor put. Data. Type( String key, Data. Type value )

Method Description Shared. Preferences. Editor put. Int( String key, int value) Associates value with key in this Shared. Preferences. Editor. These key/value pairs should be committed by calling either the commit or apply method. Returns this Shared. Preferences. Editor so that method calls can be chained. Shared. Preferences. Editor put. Float( String key, float value)

Shared. Preferences. Editor (1 of 2) • Assuming we have a Shared. Preferences. Editor reference named editor, in order to associate the value 10 with the key rating, we write: // editor is a Shared. Preferences. Editor editor. put. Int( "rating", 10 );

Shared. Preferences. Editor (2 of 2) • In order to actually write to the user preferences, we need to call the commit or apply method. // editor is a Shared. Preferences. Editor editor. put. Int( "rating", 10 ); editor. commit( );

Shared. Preferences (1 of 5) • To retrieve data previously written to the user preferences, we use the get. Data. Type methods of the Shared. Preferences interface. • The get. Data. Type methods have this general method header: public Data. Type get. Data. Type( String key, Data. Type default. Value )

Shared. Preferences (2 of 5) Method Description int get. Int( String key, int Returns the int value associated with default. Value) key in this Shared. Preferences object; returns default. Value if the key is not found. float get. Float( String Returns the float value associated key, float default. Value ) with key in this Shared. Preferences object; returns default. Value if the key is not found.

Shared. Preferences (3 of 5) • Assuming we have a Shared. Preferences reference named pref, in order to retrieve the value that was previously associated with the key rating and written to the preferences, we write: // pref is a Shared. Preferences // default value is 1 int stored. Rating = pref. get. Int( "rating", 1 );

Shared. Preferences (4 of 5) • We can use the get. Default. Shared. Preferences static method of the Preference. Manager class in order to get a Shared. Preferences reference. Method Description static Shared. Preferences get. Default. Shared. Preferences( Context context ) Returns the Shared. Preferences for context.

Shared. Preferences (5 of 5) • Since the Activity class inherits from Context and our Main. Activity and Data. Activity classes inherit from Activity, we can pass the keyword this as the argument of this method. • Thus, inside an Activity class, in order to get a Shared. References inside our two classes, we can write: Shared. Preferences pref = Reference. Manager. get. Default. Shared. Preferences( this );

Shared. Preferences. Editor • Once we have a Shared. References reference, we can call the edit method of Shared. Preferences to obtain a Sharedpreferences. Editor reference: Shared. Preferences pref = Reference. Manager. get. Default. Shared. Preferences( this ); Shared. Preferences. Editor editor = pref. edit( );

Impact on Our Classes (1 of 2) • The View components of our app are still the same. • Most of the changes take place in the Model, the Mortgage class. • We also have small changes in the Controller classes, Main. Activity and Data. Activity. Look at project

Impact on Our Classes (2 of 2) • We modify the Mortgage class so that it includes a method to write mortgage data to the user preferences system and a constructor to read data from it. • In both the Main. Activity and Data. Activity classes, we use these methods to either load or write the mortgage parameters from and to the user preferences system.

Mortgage Class (1 of 2) • Inside the constructor, we read the preferences and assign the corresponding values to the three instance variables: amount, rate, and years. • If it is the first time the app is run, there are no preferences set; we assign default values to the three instance variables.

Mortgage Class (2 of 2) • Since we need three keys (for three values that we store in the preferences), we define three constants for them: private static final String PREFERENCE_AMOUNT = "amount"; private static final String PREFERENCE_YEARS = "years"; private static final String PREFERENCE_RATE = "rate";

Mortgage Constructor public Mortgage( Context context ) { Shared. Preferences pref = Preference. Manager. get. Default. Shared. Preferences( context ); set. Amount( pref. get. Float( PREFERENCE_AMOUNT, 100000. 0 f ) ); set. Years( pref. get. Int( PREFERENCE_YEARS, 30 ) ); set. Rate ( pref. get. Float( PREFERENCE_RATE, 0. 035 f ) ); }

Mortgage Class (1 of 2) • We include the method set. Preferences: it writes to the preferences. • We include a Context parameter so we can pass it to the get. Default. Shared. Preferences method. public void set. Preferences( Context context )

Mortgage Class (2 of 2) • When we call the set. Preferences method from the Data. Activity class using the Mortgage object reference mortgage, we will pass this. mortgage. set. Preferences( this );

set. Preferences Method (1 of 3) public void set. Preferences( Context context ) { // Get a Shared. Preferences. Editor // With it, write the data to the preferences }

set. Preferences Method (2 of 3) • First we get a Shared. Preferences. Editor reference. Shared. Preferences pref = Preference. Manager. get. Default. Shared. Preferences( context ); Shared. Preferences. Editor editor = pref. edit( );

set. Preferences Method (3 of 3) • Next, we write the data into the preferences. • Amount, years, and rate are three instance variables of the Mortgage class. editor. put. Float( PREFERENCE_AMOUNT, amount ); editor. put. Int( PREFERENCE_YEARS, years ); editor. put. Float( PREFERENCE_RATE, rate ); editor. commit( );

Main. Activity—on. Create • Inside the on. Create of Main. Activity, we instantiate the Mortgage instance variable with this statement (an Activity "is a" Context. Wrapper): mortgage = new Mortgage( this );

Data. Activity— update. Mortgage. Object • Inside the update. Mortgage. Object method, after calling the mutators to update the Mortgage data, we call set. Preferences so that we write the current Mortgage data to the preferences. mortgage. set. Preferences( this );

Android. Manifest. xml • We need to specify in the manifest that this app writes to the device file system • We add a uses-permission element inside the manifest element as follows: <uses-permission android: name= "android. permission. WRITE_EXTERNAL_STORAGE" />
- Slides: 98