In 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()); } } }
Thanks for the great Tutorials Derek! I have recommended them to friends and colleagues.
Cheers!
Thank you ๐ I greatly appreciate the referrals.
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.
You could save them in a database like I covered in a past part of the Android tutorial. I’ll also talk about saving to files.
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
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.
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)
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 ๐
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.
You want to compile for the version of Android on your device or that will cause problems. Most of these apps work on Android 3.
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;
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.
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!!
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.
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.
Yes I will be finishing everything up. I just got side tracked by the Samsung stuff and i want to finish up Inkscape.
Great news Derek, thanks again for your time and efforts.
You’re very welcome ๐
๐ 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!)
(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!
: )
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.
how will you add a new person
It all happens automatically
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?
Hi Oskar,
I explore how to use a new API being the Samsung mobile sdk. I will start a new Android tutorial in the next few weeks.