Android Development 24

Android Dialog BoxesIn this part of my Android Development tutorial I will cover how to use Android Dialog Boxes. We’ll cover how to design their layouts, how to call for them to open and how to send data from dialog boxes to other fragments and activities using putSerializable and getSerializableExtra.

Because I’ll be working specifically with birth dates we’ll also take a look at the Date, Calendar and GregorianCalendar. All of the code can be found after the video to help you better understand.

If you like videos like this, it helps to tell Google+ with a click here

Code from the Video

The whole package is here ADT 24 Census App

Contact.java

package com.newthinktank.censusapp;

import java.util.Calendar;
import java.util.Date;
import java.util.UUID;

import android.util.Log;

public class Contact {
	
	private String name;
	private String phoneNumber;
	private String streetAddress;
	private String city;
	
	private UUID idNumber;
	private boolean contacted = false;
	
	// NEW
	private Date dateOfBirth;
	
	public Contact(){
		
		// Generates a type 4 pseudo randomly generated UUID
		// Wiki : 1 billion UUIDs every second for the next 
		// 100 years, the probability of creating just one 
		// duplicate would be about 50%
		
		idNumber = UUID.randomUUID();
		
		// NEW Define a default dateOfBirth
		
		dateOfBirth = new Date();
		
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		
		Log.e("CENSUS", "NAME CHANGED TO " + name);
		
		this.name = name;
	}

	public String getPhoneNumber() {
		return phoneNumber;
	}

	public void setPhoneNumber(String phoneNumber) {
		
		Log.e("CENSUS", "PHONE CHANGED TO " + phoneNumber);
		
		this.phoneNumber = phoneNumber;
	}


	public String getStreetAddress() {
		return streetAddress;
	}

	public void setStreetAddress(String streetAddress) {
		
		Log.e("CENSUS", "STREET CHANGED TO " + streetAddress);
		
		this.streetAddress = streetAddress;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		
		Log.e("CENSUS", "CITY CHANGED TO " + city);
		
		this.city = city;
	}
	
	public boolean getContacted() {
		return contacted;
	}

	public void setContacted(boolean contacted) {
		
		Log.e("CENSUS", "CONTACTED CHANGED TO " + contacted);
		
		this.contacted = contacted;
	}

	public UUID getIdNumber() {
		return idNumber;
	}

	// NEW
	
	public Date getDateOfBirth() {
		return dateOfBirth;
	}

	public void setDateOfBirth(Date dateOfBirth) {
		this.dateOfBirth = dateOfBirth;
	}

	// Used to return date in the format mm/dd/yyyy
	// as a string
	
	public String getDateString(){
		
		Calendar calendar = Calendar.getInstance();
		
		calendar.setTime(dateOfBirth);
		
		int year = calendar.get(Calendar.YEAR);
		
		// Must add 1 because January is 0
		
		int month = calendar.get(Calendar.MONTH) + 1;
		int day = calendar.get(Calendar.DAY_OF_MONTH);
		
		return month + "/" + day + "/" + year;
		
	}
	
	// END OF NEW
	
}

dialog_date.xml

<?xml version="1.0" encoding="utf-8"?>
<DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/birthday_picker"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" 
    android:calendarViewShown="false" />

<!-- We get rid of the calendar with
android:calendarViewShown="false" 
because otherwise 2 date inputs show up
and confuse everything -->
    

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Census App</string>
    <string name="action_settings">Settings</string>
    <string name="contact_name_hint">Enter Name</string>
    <string name="contact_phone_hint">Enter Phone Number</string>
    
    <string name="contact_street_hint">Enter Street</string>
    <string name="contact_city_hint">Enter City</string>

    <string name="contact_contacted_checkbox">Contacted</string>
    <string name="fragment_contact_list_title">Contacts</string>
    <string name="list_item_contact_name">Name</string>
    <string name="list_item_contact_street">Street</string>
    
    <string name="contact_birthday">Enter Birthday</string>
    

</resources>

DateDialogFragment.java

package com.newthinktank.censusapp;

// 100% NEW

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.View;
import android.widget.DatePicker;
import android.widget.DatePicker.OnDateChangedListener;

// A DialogFragment is used to display a dialog window
// that floats over the current Activity

public class DateDialogFragment extends DialogFragment{
	
	// Key for the birthday that is passed between activities
	
	public static final String CONTACT_BIRTHDAY = "com.newthinktank.censusapp.contact_birthday";
	
	private Date contactBirthday;

	// Called to set up the DialogFragment
	
	@Override
	public Dialog onCreateDialog(Bundle savedInstanceState) {
		
		// Get the birthdate that was passed to this Activity
		
		contactBirthday = (Date) getArguments().getSerializable(CONTACT_BIRTHDAY);
		
		// Used to work with the date
		
		Calendar calendar = Calendar.getInstance();
		
		calendar.setTime(contactBirthday);
		
		// Convert date to month, day and year
		
		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH);
		int day = calendar.get(Calendar.DAY_OF_MONTH);
		
		// Inflate the Dialog
		
		View theView = getActivity().getLayoutInflater()
				.inflate(R.layout.dialog_date, null);
		
		// Access the DatePicker in dialog_date.xml
		
		DatePicker datePicker = (DatePicker) theView.findViewById(R.id.birthday_picker);
		
		// If the birth date changes prepare to pass the new value
		
		datePicker.init(year, month, day, new OnDateChangedListener(){

			@Override
			public void onDateChanged(DatePicker theView, int year, int month,
					int day) {
				
				contactBirthday = new GregorianCalendar(year, month, day).getTime();   
				
				// Put the new value in the Bundle for passing
				// to other activities
				
				getArguments().putSerializable(CONTACT_BIRTHDAY, contactBirthday);
				
			}
			
		});
		
		// Set up the dialog box by setting the custom view to hold the 
		// contents of the dialog. Also set title, the type of button,
		// what to do when it is clicked and then the creation command
		
		return new AlertDialog.Builder(getActivity())
			.setView(theView)
			.setTitle(R.string.contact_birthday)
			.setPositiveButton(android.R.string.ok,
					new DialogInterface.OnClickListener() {

						@Override
						public void onClick(DialogInterface dialog, int which) {
							
							// Notify of a change to the birth date
							
							sendResult(Activity.RESULT_OK);
							
						}
				
			})
			.create();
	}
	
	public static DateDialogFragment newInstance(Date date){
		
		// Bundle holds the value for birth date
		
		Bundle dataPassed = new Bundle(); 
		
		// Put the key value pair in the Bundle
		
		dataPassed.putSerializable(CONTACT_BIRTHDAY, date);
		
		// Create the DateDialogFragment and attach the birth date
		
		DateDialogFragment dateFragment = new DateDialogFragment();
		
		dateFragment.setArguments(dataPassed);
		
		return dateFragment;
		
	}
	
	private void sendResult(int resultCode){
		
		// Check that the target Fragment was set up in ContactFragment
		
		if(getTargetFragment() == null) return;
		
		// If we have the target set up are intention to provide
		// it with the birthdate 
		
		Intent theIntent = new Intent();
		
		theIntent.putExtra(CONTACT_BIRTHDAY, contactBirthday);
		
		// Get the target and have it receive the data
		
		getTargetFragment()
			.onActivityResult(getTargetRequestCode(), resultCode, theIntent);
		
	}
	
}

ContactFragment.java

package com.newthinktank.censusapp;

import java.util.Date;
import java.util.UUID;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;

public class ContactFragment extends Fragment {
	
	// Store the ID NUMBER for the current Contact
	// This is the key of the key / value pair that will
	// store the Contacts Id Number
	
	public static final String CONTACT_ID =
			"com.newthinktank.censusapp.contact_id";

	private Contact contact;
	private EditText contactNameEditText;

	private EditText contactStreetEditText;
	private EditText contactCityEditText;
	private EditText contactPhoneEditText;
	
	private CheckBox contactedCheckBox;
	
	// NEW
	
	// The key that represents the value stored for date of birth
	
	private static final String DATE_OF_BIRTH = "Date of Birth";
	
	private EditText contactBirthdayEditText;
	
	// Used to track when we are performing an action on date of birth
	
	private static final int REQUEST_DATE = 0;
	
	// END OF NEW
	
	
	public static ContactFragment newContactFragment(UUID contactId){
		
		// A Bundle is used to pass data between Activitys
		
		Bundle passedData = new Bundle();
		
		// Put the Contacts ID in the Bundle and return the
		// ContactFragment
		
		passedData.putSerializable(CONTACT_ID, contactId);
		
		ContactFragment contactFragment = new ContactFragment();
		
		contactFragment.setArguments(passedData);
		
		return contactFragment;
		
	}

	// Generate this with Right Click - Source - Override/Implement methods
	// This method is called when the Fragment is called for.
	// We initialize everything here.

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);

		// Replace contact = new Contact();
		// Get the value from CONTACT_ID that was passed in
		
		UUID contactId = (UUID) getArguments().getSerializable(CONTACT_ID);
		
		// Get the Contact with the matching ID
		
		contact = AllContacts.get(getActivity()).getContact(contactId);
		
	}

	// Used to inflate the Fragment, or show it on the screen

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		// Pass in the layout to inflate, the views parent and whether
		// to add the inflated view to the parent.
		// We mark this false because the Activity will add the view.

		View theView = inflater.inflate(R.layout.fragment_contact, container, false);

		// Get a reference to the EditText

		contactNameEditText = (EditText) theView.findViewById(R.id.contactNameEditText);

		// If text in the EditText box is edited it will change the
		// name.
		
		contactStreetEditText = (EditText) theView.findViewById(R.id.contactStreetEditText);
		contactCityEditText = (EditText) theView.findViewById(R.id.contactCityEditText);
		contactPhoneEditText = (EditText) theView.findViewById(R.id.contactPhoneEditText);
		
		// All the above EditText components will use just one TextWatcher
		// which auto updates Contact.java

		TextWatcher editTextWatcher = new TextWatcher() {

			@Override
			public void onTextChanged(CharSequence arg0, int arg1, int arg2,
					int arg3) {
				
				// There is an error in that name is getting the wrong information
				// Removing request focus from fragment_contact.xml fixed that

				Log.e("TEXT WAS CHANGED", arg0.toString());

				if (contactNameEditText.hasFocus() == true){

					contact.setName(arg0.toString());

				} else if (contactStreetEditText.hasFocus() == true){
					
					contact.setStreetAddress(arg0.toString());
					
				} else if (contactCityEditText.hasFocus() == true){
					
					contact.setCity(arg0.toString());
					
				} else if (contactPhoneEditText.hasFocus() == true){
					
					contact.setPhoneNumber(arg0.toString());
					
				}

			}

			@Override
			public void afterTextChanged(Editable s) {
				// TODO Auto-generated method stub

			}

			@Override
			public void beforeTextChanged(CharSequence s, int start,
					int count, int after) {
				// TODO Auto-generated method stub

			}
		};

		
		contactStreetEditText.addTextChangedListener(editTextWatcher);
		contactCityEditText.addTextChangedListener(editTextWatcher);
		contactPhoneEditText.addTextChangedListener(editTextWatcher);

		contactNameEditText.addTextChangedListener(editTextWatcher);

		// Create CheckBox Listener
		
		contactedCheckBox = (CheckBox) theView.findViewById(R.id.contactedCheckBox);
		
		contactedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){

			@Override
			public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
				
				// Change the value of the checkbox
				
				contact.setContacted(arg1);
				
			}
			
		});
		
		// NEW 
		
		// Setup the Birth Day EditText box and insert the data
		
		contactBirthdayEditText = (EditText) theView.findViewById(R.id.contactBirthdayEditText);
		
		contactBirthdayEditText.setText(contact.getDateString());
		
		contactBirthdayEditText.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				
				// getSupportFragmentManager() returns the FragmentManager
				// that is used to interact with Fragments in this Activity
				
				FragmentManager fragManager = getActivity()
						.getSupportFragmentManager();
				
				DateDialogFragment dateDialog = DateDialogFragment
						.newInstance(contact.getDateOfBirth());
				
				dateDialog.setTargetFragment(ContactFragment.this, REQUEST_DATE);
				
				dateDialog.show(fragManager, DATE_OF_BIRTH);
				
			}
			
		});
		
		// END OF NEW

		// Get the values for the current Contact and put them in
		// the right Components
		
		contactNameEditText.setText(contact.getName());
		contactStreetEditText.setText(contact.getStreetAddress());;
		contactCityEditText.setText(contact.getCity());
		contactPhoneEditText.setText(contact.getPhoneNumber());
		contactedCheckBox.setChecked(contact.getContacted());


		// Pass in the layout to inflate, the views parent and whether
		// to add the inflated view to the parent.
		// We mark this false because the Activity will add the view.

		return theView;

	}

	// NEW
	
	// Called when the OK button is clicked on the date dialog box
	
	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		
		// If this wasn't called by the date dialog exit
		
		if(resultCode != Activity.RESULT_OK) return;
		
		// If it was get the date and save it to the Contact and
		// update the date EditText box
		
		if(requestCode == REQUEST_DATE){
			
			Date birthdate = (Date) data
					.getSerializableExtra(DateDialogFragment.CONTACT_BIRTHDAY);
			
			contact.setDateOfBirth(birthdate);
			
			contactBirthdayEditText.setText(contact.getDateString());
			
		}
		
	}

}

26 Responses to “Android Development 24”

  1. Travel IO says:

    Thanks for the great Tutorials Derek! I have recommended them to friends and colleagues.
    Cheers!

  2. Ron Sivan says:

    Hey Derek,

    If I wanted to save the changes I make to the contacts, how would I do that?

    Everytime I run the app it restores to the default values.

    I guess my question is how to save the data and restore.

    Thanks.

  3. Hi Derek. This is regarding a file in the download zip.

    in CensusApp.java

    line 30: ContactFragment()
    line 31: return ContactFragment.newContactFragment(contactIdNumber);

    I’m having some trouble figuring out the reason for line 30. Is that a typo?

    Thanks

    • Derek Banas says:

      Hi Gerard,

      I’m not sure what line you are referring to. I just looked at the code. i think you may be referring to something that was part of a commented out line. Sorry about the confusion.

  4. Sarah says:

    Hi, Derek

    I execute the sample code, and it jump out error MSG “Unforturnlly, Census app has stopped” when I click on a personal data.

    Catlog warm showup:
    01-03 07:01:00.004: E/AndroidRuntime(3205): FATAL EXCEPTION: main
    01-03 07:01:00.004: E/AndroidRuntime(3205): java.lang.NullPointerException
    01-03 07:01:00.004: E/AndroidRuntime(3205): at com.newthinktank.censusapp.ContactFragment.onCreateView(ContactFragment.java:178)
    01-03 07:01:00.004: E/AndroidRuntime(3205): at android.support.v4.app.Fragment.performCreateView(Fragment.java:1478)

    • Derek Banas says:

      Hi Sarah,

      There is a typo some place. Try downloading and running the package for this app. It is available under the video. I hope that helps ๐Ÿ™‚

      • Sarah says:

        Sorry about not saying clearly, I do use package now, and I did try replace ContactFragment.java code from web also can’t work.

    • I have the same problem. I use a Samsung Galaxy 10.1 tablet running Android 3.1 but my project has Android 4.3. That may be significant and it may ressemble to the situation of yopur setup Sarah.

    • After some more investigations I’ve found that the same problem occur when I set it to 2.3.3 and 3.1 in the eclipse project. The error message is

      W/ResourceType(10609): Failure getting entry for 0x7f06000b (t=5 e=11) in package 0 (error -75)
      W/Resources(10609): Converting to string: TypedValue{t=0x1/d=0x7f06000b a=-1 r=0x7f06000b}
      D/AndroidRuntime(10609): Shutting down VM
      W/dalvikvm(10609): threadid=1: thread exiting with uncaught exception (group=0x402b1760)
      E/AndroidRuntime(10609): FATAL EXCEPTION: main
      E/AndroidRuntime(10609): java.lang.NullPointerException
      E/AndroidRuntime(10609): at com.newthinktank.censusapp.ContactFragment.onCreateView(ContactFragment.java:176)
      E/AndroidRuntime(10609): at android.support.v4.app.Fragment.performCreateView(Fragment.java:1500)
      E/AndroidRuntime(10609): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927)

      The class R is indeed regenerated. I removed it and rebuilt it. The R.string inner class has the following

      public static final int contact_phone_number_hint=0x7f06000b;

      • Derek Banas says:

        Any time you get R file errors it is best to delete the R file and then Project -> Clean. You may also want to try and Help -> Check for Updates. I hope that helps.

        • Jaco Gericke says:

          Hi Derek ๐Ÿ™‚

          I received the same java.lang.NullPointerException while running CensusApp (from the package) on my device and I found that it occurred only when device is in landscape mode and the ContactFragment fragment is used.
          After trying for a while to find the problem, I saw that you never added the new EditText and Checkbox to the landscape XML layout. I fixed it and the app worked perfectly ๐Ÿ˜€

          Thanks a lot for all your great tutorials! I am an electronic engineering student from South Africa and I have to do a final year project next semester where in I am required to write an Android app to communicate with another device that I have to design and build. Your tutorials have really helped me a lot, thanks for all your effort!!

          • Derek Banas says:

            Hi Jaco,

            Thank you for posting your fix. I’m very happy that you enjoy the videos. You’re very welcome ๐Ÿ™‚ I wish you all the best on your final project.

  5. Bigal says:

    Hi Derek,

    Just wondering if you are going to finish this excellent tutorial? In part 18 of the Android Development you said you would take us step by step through the creation of a Big App that included adding people to a database, taking pictures and using fragment in a bunch of different ways. The app looked awesome and was the exact thing I’ve been looking for all over the internet to get me started on an app I’ve been thinking of doing for months.

    Please don’t think I’m criticising your tutorial. I am most certainly not as it’s taught me so much and been delivered in a highly professional and easily understood manner, it’s just it promised so much but has just stopped mid app.

    I understand that other projects and interests may have taken over your time but it would be great to see how to finish this app off.

    Thanks in any case for your time and efforts as they are hugely appreciated.

    Thanks in advance.

  6. Matt says:

    ๐Ÿ™‚ awwwwww, no more vidss.

    I’ll subscribe (YT) in hope you find the time to get back to this. AWESOME tuts, seriously. It was incredibly helpful to go through a multi-part app like this. Thanks Derek!

    (Gobbled tuts 18-24 all in one sitting xD, simply outstanding!)

    • Matt says:

      (oh and just FYI – Bug Report:

      There is a bug with main view (ContactViewPager.java) not updating edited record information until the app is reopened… Data points are recorded correctly, just not updating list_item_contacts listed in main view.

      Cheers,)

      Thanks, thanks, thanks, and thanks again for the tuts!

      : )

    • Derek Banas says:

      Thank you very much ๐Ÿ™‚ I will get back to regular Android java development. I decided it would be helpful to take a look at App Inventor though because so much can be done with it. Then I’ll come back and dive into Java again.

  7. ariz says:

    how will you add a new person

  8. Oskar says:

    Hi Derek,
    Im not yet in this video, but in a couple of days i will be. So i wonder, where to go from here?

Leave a Reply

Your email address will not be published.

Google+