In this tutorial I will connect the ListFragment to the Census App, so that when you click on an item we’ll be able update the Contacts. This tutorial began at part 18, so if you missed that video start there.

All the Contacts are updated and we also cover how to store and pass contact IDs between activities. We also streamline the code by separating duplicate code out into another class and much more.

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

Code From the Video

CensusApp.java

package com.newthinktank.censusapp;
//We will use the android.support.v4.app.Fragment
//support library so our app runs on older versions
//of Android
import java.util.UUID;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
// Change Activity to FragmentActivity
// The FragmentManager ads Fragments to an Activity's view
// NEW Change to extend FragmentActivityBuilder so that
// that class can handle the work FragmentManager must do
public class CensusApp extends FragmentActivityBuilder {
/* This work is now handled by FragmentActivityBuilder
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_census_app);
FragmentManager fragManager = getSupportFragmentManager();
// Check if the FragmentManager knows about the Fragment
// id we refer to
Fragment theFragment = fragManager.findFragmentById(R.id.fragmentContainer);
// Check if the Fragment was found
if(theFragment == null){
// If the Fragment wasn't found then we must create it
theFragment = new ContactFragment();
// Creates and commits the Fragment transaction
// Fragment transactions add, attach, detach, replace
// and remove Fragments.
// add() gets the location to place the Fragment into and
// the Fragment itself.
fragManager.beginTransaction()
.add(R.id.fragmentContainer, theFragment)
.commit();
}
}
*/
// NEW : Call for FragmentActivityBuilder to have the FragmentManager
// add the right Fragment to the Activity
@Override
protected Fragment createFragment() {
// Get the Contact ID that was passed over
UUID contactIdNumber = (UUID) getIntent()
.getSerializableExtra(ContactFragment.CONTACT_ID);
// Create an instance of ContactFragment and pass in
// the ID so the proper Contact data is displayed
return new ContactFragment().newContactFragment(contactIdNumber);
}
// END OF NEW
}

ContactListActivity.java

package com.newthinktank.censusapp;
//We will use the android.support.v4.app.Fragment
//support library so our app runs on older versions
//of Android
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
//Change Activity to FragmentActivity
// The FragmentManager ads Fragments to an Activity's view
//NEW Change to extend FragmentActivityBuilder so that
//that class can handle the work FragmentManager must do
public class ContactListActivity extends FragmentActivityBuilder {
/* This work is now handled by FragmentActivityBuilder
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_census_app);
FragmentManager fragManager = getSupportFragmentManager();
// Check if the FragmentManager knows about the Fragment
// id we refer to
Fragment theFragment = fragManager.findFragmentById(R.id.fragmentContainer);
// Check if the Fragment was found
if(theFragment == null){
// If the Fragment wasn't found then we must create it
// We change this from ContactFragment, which we used
// in CensusApp
theFragment = new FragmentContactList();
// Creates and commits the Fragment transaction
// Fragment transactions add, attach, detach, replace
// and remove Fragments.
// add() gets the location to place the Fragment into and
// the Fragment itself.
fragManager.beginTransaction()
.add(R.id.fragmentContainer, theFragment)
.commit();
}
}
*/
// NEW : Call for FragmentActivityBuilder to have the FragmentManager
// add the right Fragment to the Activity
@Override
protected Fragment createFragment() {
return new FragmentContactList();
}
}

FragmentActivityBuilder.java

package com.newthinktank.censusapp;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
// NEW : This class will use the FragmentManager to ad
// Fragments to multiple Activity views
public abstract class FragmentActivityBuilder extends FragmentActivity{
// This method must be implemented so that the right
// type of Fragment can be returned.
// CensusApp gets ContactFragment()
// ContactListActivity gets FragmentContactList()
protected abstract Fragment createFragment();
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.activity_census_app);
FragmentManager fragManager = getSupportFragmentManager();
// Check if the FragmentManager knows about the Fragment
// id we refer to
Fragment theFragment = fragManager.findFragmentById(R.id.fragmentContainer);
// Check if the Fragment was found
if(theFragment == null){
// If the Fragment wasn't found then we must create it
// NEW We can generate many types of Fragments by having
// CreateFragment define the type. So
// theFragment = new ContactFragment();
// is replaced by
theFragment = createFragment();
// Creates and commits the Fragment transaction
// Fragment transactions add, attach, detach, replace
// and remove Fragments.
// add() gets the location to place the Fragment into and
// the Fragment itself.
fragManager.beginTransaction()
.add(R.id.fragmentContainer, theFragment)
.commit();
}
}
}

FragmentContactList.java

package com.newthinktank.censusapp;
import java.util.ArrayList;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
// The ListFragment displays a list of items in a
// ListView, by binding to our ArrayList using an
// ArrayAdapter in this situation.
public class FragmentContactList extends ListFragment {
// Stores the list of Contacts
private ArrayList<Contact> contactList;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Change the title for the current Activity
getActivity().setTitle(R.string.fragment_contact_list_title);
// Get the ArrayList from AllContacts
contactList = AllContacts.get(getActivity()).getContactList();
ContactAdapter contactAdapter = new ContactAdapter(contactList);
// Provides the data for the ListView by setting the Adapter
setListAdapter(contactAdapter);
}
// NEW
// Handle what happens when an item in the ListFragment is clicked
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Log.e("CENSUS APP", "LIST ITEM CLICKED");
Contact clickedContact = ((ContactAdapter) getListAdapter()).getItem(position);
// Pass the Context with getActivity and the Activity to
// start being ContactActivity
Intent newIntent = new Intent(getActivity(), CensusApp.class);
// Add the IdNumber when calling for the Activity to display
// so that the right data is loaded
newIntent.putExtra(ContactFragment.CONTACT_ID,
clickedContact.getIdNumber());
startActivityForResult(newIntent, 0);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
((ContactAdapter)getListAdapter()).notifyDataSetChanged();
}
// END OF NEW
private class ContactAdapter extends ArrayAdapter<Contact> {
public ContactAdapter(ArrayList<Contact> contacts) {
// An Adapter acts as a bridge between an AdapterView and the
// data for that view. The Adapter also makes a View for each
// item in the data set. (Each list item in our ListView)
// The constructor gets a Context so it so it can use the
// resource being the simple_list_item and the ArrayList
// android.R.layout.simple_list_item_1 is a predefined
// layout provided by Android that stands in as a default
super(getActivity(), android.R.layout.simple_list_item_1, contacts);
}
// getView is called each time it needs to display a new list item
// on the screen because of scrolling for example.
// The Adapter is asked for the new list row and getView provides
// it.
// position represents the position in the Array from which we will
// be pulling data.
// convertView is a pre-created list item that will be reconfigured
// in the code that follows.
// ViewGroup is our ListView
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Check if this is a recycled list item and if not we inflate it
if(convertView == null){
convertView = getActivity().getLayoutInflater()
.inflate(R.layout.list_item_contact, null);
}
// Find the right data to put in the list item
Contact theContact = getItem(position);
// Put the right data into the right components
TextView contactNameTextView =
(TextView)convertView.findViewById(R.id.contact_name);
contactNameTextView.setText(theContact.getName());
TextView streetTextView =
(TextView)convertView.findViewById(R.id.contact_street);
streetTextView.setText(theContact.getStreetAddress());
CheckBox contactedCheckBox =
(CheckBox)convertView.findViewById(R.id.contact_contacted_checkbox);
contactedCheckBox.setChecked(theContact.getContacted());
// Return the finished list item for display
return convertView;
}
}
}

ContactFragment.java

package com.newthinktank.censusapp;
import java.util.UUID;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
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 {
// NEW 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";
// END OF NEW
private Contact contact;
private EditText contactNameEditText;
private EditText contactStreetEditText;
private EditText contactCityEditText;
private EditText contactPhoneEditText;
private CheckBox contactedCheckBox;
// 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
passedData.putSerializable(CONTACT_ID, contactId);
ContactFragment contactFragment = new ContactFragment();
contactFragment.setArguments(passedData);
return contactFragment;
}
// END OF NEW
// 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);
// NEW 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);
// END OF NEW
}
// 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 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) {
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) {
// NEW Change the value of checked to the opposite
// of what it was if clicked
contact.setContacted(!contact.getContacted());
// END OF NEW
}
});
// 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());
// END OF NEW
// 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;
}
}

23 Responses to “Android Development 22”

I would like to make Reference apps based on html documents; for example I have a homebrew RPG website with basic html links to rules, and spell lists which link to hundreds of spells.

Is there an easy way to integrate all the html content with minimal retyping/reformating etc? (like copying the html pages in a project folder and having an app shell to access and navigate with options like home, back, next, bookmark?)
Having the content integrated in the app, so that you can get it from a Play Store and consult/read it offline.

If thats not possible, a tutorial for a reader app with internal links between pages would be great.

I have a couple of tutorials on parsing xml and json in this tutorial series. You could also pull in the page and then pull out the information you need with regular expressions. You may also find this useful TagSoup.

I’ll do my best to make a tutorial on how to parse regular html in Android.

Hello Derek,
Is it possible for you to upload the tutorials in some other video sharing website in addition to youtube. I’m from a country where unfortunately, youtube is banned 🙁 I will really appreciate if you can share your videos in facebook or other sharing sites.

Thanks Derek for replying. Though I myself have never done much video sharing. But I think you can create a facebook page and upload videos there. In addition couple of other video sharing sites that I’m aware of are
a) metacafe.com
b) dailymotion.com

1. Facebook randomly won’t allow me to upload certain videos. That is why I no longer use Facebook
2. Metacafe only allows videos to be 10 minutes or less. They have also blocked some of my videos
3. Dailymotion might work. I’ll give it a try

Hi Derek,
My previous comment has been in moderation. May be because i used website addresses. Anyways, my suggestion for video sharing were
a) You can use facebook page and share videos
b) metacafe and dailymotion work similar to youtube and can be used for video sharing.

Hey Derek! Thanks for your very helpful set of tutorials, they are really clear and well prepared. 🙂

However, I’m wondering why in the android, when people use fragments, it seems that you (and everyone else) is using setArguments/getArguments to pass data around, where in your factory method (newContactFragment) you could have used a directement assignement: contactFragment.contactId = passedData.

Is it something linked to fragment life management? (but you are only reusing the argument in onCreate(), so only at creation time, not after a pause/resume).
Thanks for shedding some lights on this!

It is probably because we all learned from the same tutorials and that just stuck. I do my best to keep up on deprecated functions with Android. Thankfully they aren’t fully deprecating code. Since I make apps all of the time I have less and less time to look back at improved new options. I hope that makes sense.

A bug in class ContactFragment, function onCreateView
Step to reproduce
1 On contact list, click the first person
2 On contact page, do nothing just hit back button
3 On the contact list, the name disappear

The reason is these code
contactNameEditText.setText(contact.getName());
contactStreetEditText.setText(contact.getStreetAddress());
contactCityEditText.setText(contact.getCity());
contactPhoneEditText.setText(contact.getPhoneNumber());
is after your set the listener, these code will invoke the listener as well.
The bad news is the focus in always on the name text field. so the name
field will be overwritten by phoneNumber.
You can log the contact before you return the view.
So these setText codes should be put before set the listener.

Hey Derek,
Great tutorials! My question to you and everyone that reading this is, dont I need something that make my onListItemClick to work? I have made a copy of your application but with products instead of contacts and the only thing I do not get to work is when I press in the list the details should show. Only thing I get when I press in my textview is “TextView does not support text selection. Action mode cancelled.” I have tried to add and mix this stuff in the xml file:
android:enabled=”false”
android:clickable=”true”
android:focusable=”false”
android:focusableInTouchMode=”false”
but non of them changed anything more than I can’t modify in the list (which was good but in the end did not help me). Please could someone help me with my problem?

I have a question, on my code I get a java.lang.NullPointerException everytime I click one of the contacts, which seems to be caused by the listeners on ContactFragment. I also copied your code entirely (maintaning the package name to match mine) and still get the same error

Since I’m so new to this, I would appreciate if you could help me figure out what seems to be the problem. I commented all the setName() from ContactFragment.java and then the contact info opens (with all fields empty), and as soon as I put a letter in one of the EditText the app crashes again with the null pointer exception.