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 [googleplusone]
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; } }
list_item_contact.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <CheckBox android:id="@+id/contact_contacted_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:layout_alignParentRight="true" android:enabled="false" android:focusable="false" android:padding="4dp" /> <TextView android:id="@+id/contact_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_toLeftOf="@id/contact_contacted_checkbox" android:padding="4dp" android:text="@string/list_item_contact_name" android:textStyle="bold" /> <TextView android:id="@+id/contact_street" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/contact_name" android:layout_toLeftOf="@id/contact_contacted_checkbox" android:padding="4dp" android:text="@string/list_item_contact_street" /> </RelativeLayout>
Hello Derek, the tutorial series is excellent!
Im not sure where to post requests so here it goes:
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.
best regards
Richard
Hello Richard,
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.
Hi Derek, will you be able to provide code to retrieve contact images (either from gallery or camera) and store them as part of the contact?
Yes I’m going to store images and video
Hey Derek,
I really love your tutorials! Helped me a lot to develope my own application 🙂
Is it possible to make a tutorial video about the Navigation Drawer and ActionBars?
Best regards!
Marcel
That is coming very soon. I’ll be doing a ton of interface stuff in the next few tutorials.
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
Hello,
I’d be happy to upload them, but I don’t know where to upload. Youku for example wouldn’t allow me to upload all the videos for some reason. Some where ok and other were not.
If you give me a list of sites you can use I’ll see what I can do.
Thank you
Derek
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
Thanks
Asalam
Hi Asalam,
I have a few problems using these sites, but 1 of them may work.
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
Thanks
Derek
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.
Thanks
Sorry it took so long to respond. I have to approve comments on my own because my site gets attacked all of the time. It looks like DailyMotion might work. I check it out.
This is copy from Android programming Big nerd ranch
Yes I liked it so much I decided to use it for the next few videos. I changed a few things here and there.
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.
Thank you for posting the error. One problem with posting videos fast is that I don’t have time to optimize the code. I’ll eventually start making complete apps that are optimized.
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?
sorry my fault, I used editText instead of textview.. Sorry about that!
I’m glad you fixed it. Sorry i couldn’t get to you quicker
Hi Derek,
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.
The error in logcat is in here: http://pastebin.com/NJ2hYgXk
Thanks in advance,
José
Hi Jose, I have the whole package here. Tell me if you have any trouble with it.