Page 1 of 1

Dialogs and the Android Lifecycle

#1 Dogstopper  Icon User is online

  • The Ninjaducky
  • member icon



Reputation: 2857
  • View blog
  • Posts: 10,960
  • Joined: 15-July 08

Posted 22 May 2011 - 12:09 PM

Android – Dialogs and the Android Lifecycle
Hey you all, in this tutorial, we will explore the activity lifecycle as it pertains to Dialogs and I will show you how not to start dialogs and then then show you the proper way.

The Obvious, Wrong Way
When searching through the Dialog API, you will find methods like show() and dismiss() that look awfully tempting to use. However, I’ll show you why they shouldn’t be used in most instances. For this example, we’ll build a small addition calculator that gives the answer in a dialog.

First, the xml:
/res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
	xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    
    <TextView 
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:textSize="18sp"
    	android:text="@string/header" />
    <EditText
    	android:id="@+id/num1"
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:inputType="number"
    	android:imeOptions="actionNext" 
    	android:hint="@string/hint_1"/>
    <EditText
    	android:id="@+id/num2"
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:inputType="number"
    	android:imeOptions="actionDone"
    	android:hint="@string/hint_2" />
    <Button
    	android:id="@+id/submit"
    	android:layout_width="wrap_content"
    	android:layout_height="wrap_content"
    	android:text="@string/button_text" />   
    	 
</LinearLayout>



/res/layout/dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
  <TextView
  	android:id="@+id/displayer"
  	android:layout_width="fill_parent"
  	android:layout_height="wrap_content" />
</LinearLayout>



/res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">DialogTutorial</string>
    <string name="header">Number Adder - Input 2 numbers</string>
    <string name="hint_1">First Number</string>
    <string name="hint_2">Second Number</string>
    <string name="button_text">Calculate!</string>
</resources>



I’ll assume that you know what most of this XML means, and if you don’t, check out gabehabe’s tutorials that cover it. Basically, I’m just setting up the interface and the id’s so that I can reference them in the Java code.

Next, I’ll show you the dialog. It simply takes the context that is its parent and two integers as constructor parameters, and then stores them. In onCreate(), the text view is grabbed from the XML and the text is placed in it:

package com.thousandcodes.tutorials.lifecycle;

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.widget.TextView;

public class DisplayerDialog extends Dialog {

	private int num1, num2;
	private Context master;
	
	public DisplayerDialog(Context context, int num1, int num2) {
		super(context);
		master = context;
		this.num1 = num1;
		this.num2 = num2;
	}	

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.dialog);
		
		TextView tv = (TextView) findViewById(R.id.displayer);
		int sum = num1 + num2;
		tv.setText(num1 + " + " + num2 + " = " + String.valueOf(sum));
	}	

}



Again, most of this comes from other tutorials. It just displays the content defined by the XML and then displays the summation in the TextView. On the next block of code I am going to show you, we are going to define the DialogActivity class, which allows the user to input the two numbers and call the dialog.

package com.thousandcodes.tutorials.lifecycle;

import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.View;
import android.view.View.onclickListener;
import android.widget.Button;
import android.widget.EditText;

public class DialogActivity extends Activity {
	
	private Button submit;
	private EditText et1;
	private EditText et2;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        findViews();
        addListener();
        
    }

	private void addListener() {
		onclickListener listener = new View.onclickListener() {
			
			@Override
			public void onclick(View v) {
				Dialog dd = new DisplayerDialog(DialogActivity.this,
						Integer.parseInt(et1.getText().toString()),
						Integer.parseInt(et2.getText().toString()));
				dd.show();
			}
		};
		submit.setonclickListener(listener);
	}

	private void findViews() {
		submit = (Button) findViewById(R.id.submit);
		et1 = (EditText) findViewById(R.id.num1);
		et2 = (EditText) findViewById(R.id.num2);
	}
}


onCreate() again just starts the activity with the defined XML as its layout. It in turn, calls findViews(), which finds all the views from the XML and assigns them to variables. In addListener, we make a new onclickListener, which instantiates a new dialog and calls show() on it.

If you run it, it should work very nicely…until the orientation changes from vertical to horizontal or vice-versa (you can cause a flip in the emulator by pressing Numpad-9 when NumLock is off). At that point, the dialog disappears, and an exception occurs in Logcat that indicates a leaked window. In some versions of android, you may have the fatal error screen show up.

This sort of behavior is not something that we want our clients to have happen (even if they don’t notice the exception). What you want to do is allow the Activity to manage the Dialog.

The Not-So-Obvious, Correct Way
From the code that I have already given you, we can make just a few more tweaks. We just need to have the button call startDialog(), which is a method from the class Activity. In turn, that method calls onCreateDialog(int) with an arbitrary id for the dialog that we want to start. But first, we’ll need to add a constant to the DialogActivity class that is a “unique” id:

public class DialogActivity extends Activity {
	
	public static final int DISPLAYER = 100;
	 …



Next, we need to reconfigure the onclickListener to call startDialog():

private void addListener() {
		onclickListener listener = new View.onclickListener() {
			
			@Override
			public void onclick(View v) {
				showDialog(DISPLAYER);
			}
		};
		submit.setonclickListener(listener);
	} 


Notice that we pass the ID of the dialog that we’d wish to start. This method calls onCreateDialog() with that ID.

    @Override
    public Dialog onCreateDialog(int id) {
		Dialog d;
		switch(id) {
			case DISPLAYER:
				d = new DisplayerDialog(this,
						Integer.parseInt(et1.getText().toString()),
						Integer.parseInt(et2.getText().toString()));
				break;
			default:
				d = null;
		}
    	return d;
    }



We do a switch() on the ID, and if it is the DisplayerDialog, then we pass the variables to it. If you run this application now, it will work…the first time. However, if you enter a different set of numbers in, the dialog will still only show the first set of values that you gave to it. This is because createDialog() is only called once on a dialog if it has never been shown before. After that, the value is “cached” and showDialog() returns the old dialog, bypassing onCreateDialog() entirely. This is good when the information that we are displaying is static, but not good in a scenario like this. However, the fix is simple.

In DisplayerDialog, onstop() is called whenever the dialog is stopped (like when the user pushes the back arrow). In that method, we simply have to call the DialogActivity’s removeDialog() method, and it will cause the old dialog to be destroyed, and to force onCreateDialog() to be called again.
Let’s override onstop() in DisplayerDialog:

	@Override
	protected void onstop() {
		((DialogActivity)master).removeDialog(DialogActivity.DISPLAYER);
	}



We have to cast the Context to an Activity, so be careful when instantiating the dialog to actually pass an Activity in. We call removeDialog() on it, which will force the Activity to call onCreateDialog().

Well, that about covers it. For dynamic dialogs, this is the method you want to take. For static dialogs, you want to do it the latter way as well, but don’t worry about the removeDialog() part unless something is supposed to change. Hope you learned something!

Is This A Good Question/Topic? 2
  • +

Replies To: Dialogs and the Android Lifecycle

#2 Topher84  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 113
  • View blog
  • Posts: 359
  • Joined: 04-June 07

Posted 22 May 2011 - 05:16 PM

Not so sure I agree 100%. In your specific case, there probably isn't even a need for another dialog. You could simply make a box and let the current activity update the box rather than have a separate activity JUST for the answer. Also, "simple" dialogs that use .show() are fine for stuff like error messages but it seems like you are kind of stuffing the answer into an entirely new display when MAYBE don't have to.
Was This Post Helpful? 0
  • +
  • -

#3 Dogstopper  Icon User is online

  • The Ninjaducky
  • member icon



Reputation: 2857
  • View blog
  • Posts: 10,960
  • Joined: 15-July 08

Posted 22 May 2011 - 05:31 PM

Like I said at the beginning, the purpose of this tutorial is to demonstrate the dialog lifecycle. You are right that in a simple application like this, it's probably a little over the top. However, the intent was to show the cycle, and not to make a really sweet app. You get what I mean?

It is a trivial example and normally would not be that basic for that much work.
Was This Post Helpful? 0
  • +
  • -

#4 Topher84  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 113
  • View blog
  • Posts: 359
  • Joined: 04-June 07

Posted 22 May 2011 - 10:34 PM

View PostDogstopper, on 22 May 2011 - 06:31 PM, said:

Like I said at the beginning, the purpose of this tutorial is to demonstrate the dialog lifecycle. You are right that in a simple application like this, it's probably a little over the top. However, the intent was to show the cycle, and not to make a really sweet app. You get what I mean?

It is a trivial example and normally would not be that basic for that much work.


Yeah, just wasn't sure if you were actually trying to implement it in this fashion. Nice tutorial though.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1