1

I have an activity that needs to display Android phone contacts into a ListView which is a fragment inside my AddressBook.java activity. I was able to get my list to display the names, but nothing else. I feel like I am close, but having a hard time with getting the views to work. In my XML files (I have 4), I have the activity XML (only has the fragment), the ListView XML (contact_list_view.xml), and then a typical row for the ListView which I call my Item XML (contacts_list_item.xml). But how do I connect all those views into my activity view? I think I have the fragment inside of the activity OK, since the list of names works, but not sure how to get a photo and email to display. I know I'm missing something in my main code too, ContactFragments.java.

One issue, is I had to filter my contacts by name, otherwise every email in every application on my phone would post, which is not what I want. I only want the contacts in my Android Contacts app to display, so I hope that doesn't inhibit the other details of the contacts from showing.

Below is my code, I don't show my AddressBook.java activity, because I only use the XML view to display my fragment in it, using the fragment tags (which works fine, because I do at least get the contact names).

I have searched far and wide for answers to this online, but there are so many parts, I could not find how to address my specific situation. I hope to use my code I already have and edit it minimally (versus implementing a whole new way to achieve this with other classes, etc). If you have any ideas, I'd be very thankful.

ContactsFragment.java

package org.azurespot.practiceapp.addressbook;

import android.app.ListFragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ContentUris;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

import com.example.practiceapp.R;

/*
 * Taken from http://stackoverflow.com/questions/18199359/how-to-display-contacts-in-a-listview-in-android-for-android-api-11
 */

public class ContactsFragment extends ListFragment implements 
                                LoaderCallbacks<Cursor>{

    private CursorAdapter mAdapter;
    public ListView listView;
    public Cursor cursor;
    // Name should be displayed in the text1 TextView in item layout
    public static final String[] FROM = { ContactsContract.Contacts
                                                .DISPLAY_NAME_PRIMARY };
    private static final int[] TO = { android.R.id.text1 };
    private android.content.Context context;
    public View view;
    public static Uri uri;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // delete list if already there (old version)
        if (!(listView == null)){
            listView.setAdapter(null);
        }

        // create adapter once
        context = getActivity();
        int layout = android.R.layout.simple_list_item_1;
        Cursor c = null; // there is no cursor yet
        int flags = 0; // no auto-requery! Loader requeries.

        // put List in adapter
        mAdapter = new SimpleCursorAdapter(context, layout, c, FROM, TO, flags);


    } // end onCreate


    // columns requested from the database
    private static final String[] PROJECTION = {
            Contacts._ID,
            ContactsContract.Contacts.PHOTO_THUMBNAIL_URI,
            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
            ContactsContract.Contacts.HAS_PHONE_NUMBER,
            ContactsContract.Contacts.IN_VISIBLE_GROUP
    };

    // this goes in the CursorLoader parameter list, it filters
    // out only those contacts who have a phone number
    private static final String SELECTION = 
            ContactsContract.Contacts.HAS_PHONE_NUMBER + "='1'";

    // Empty public constructor, required by the system
    public ContactsFragment() {}

    // A UI Fragment must inflate its View (all fragments must override onCreateView)
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // Inflate the fragment layout
        View view =  inflater.inflate(R.layout.contact_list_view,
            container, false);
        listView = (ListView) view.findViewById(R.id.list); 

        // set Contact photo to ImageView
        ContactsFragment frag = new ContactsFragment();
        ImageView thumb = (ImageView)view.findViewById(R.id.contact_thumbnail);
        frag.getPhotoUriFromID(Contacts._ID);
        thumb.setImageURI(uri);
        return view;
    }

    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // and tell loader manager to start loading
        getLoaderManager().initLoader(0, null, this);

        listView.setAdapter(mAdapter);
        listView.setFastScrollEnabled(true);

    }

    // a CursorLoader does a query in the background
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // load from the "Contacts table"
        Uri contentUri = Contacts.CONTENT_URI;

        // no sub-selection, no sort order, simply every row
        // projection says we want just the _id and the name column
        return new CursorLoader(getActivity(),
                contentUri,
                PROJECTION,
                SELECTION,
                null,
                ContactsContract.Contacts.DISPLAY_NAME
        );
    }

    private Uri getPhotoUriFromID(String id) {
        try {
            Cursor cur = context.getContentResolver()
                    .query(ContactsContract.Data.CONTENT_URI,
                            null,
                            ContactsContract.Data.CONTACT_ID
                                    + "="
                                    + id
                                    + " AND "
                                    + ContactsContract.Data.MIMETYPE
                                    + "='"
                                    + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE
                                    + "'", null, null);
            if (cur != null) {
                if (!cur.moveToFirst()) {
                    return null; // no photo
                }
            } else {
                return null; // error in cursor process
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        Uri person = ContentUris.withAppendedId(
                ContactsContract.Contacts.CONTENT_URI, Long.parseLong(id));
         uri = Uri.withAppendedPath(person,
                ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
         return uri;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
         // Once cursor is loaded, give it to adapter
        mAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // Delete the reference to the existing Cursor,
        // so it can recycle it
        mAdapter.swapCursor(null);
    }
}

contact_list_view.xml

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

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
</ListView>

contacts_list_item

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

        <ImageView
        android:id="@+id/contact_thumbnail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/thumbnail" />

        <TextView 
          android:id='@+id/text_name'
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:clickable="true"/>
</LinearLayout>
Azurespot
  • 3,066
  • 3
  • 45
  • 73

2 Answers2

0

You should override the getView() method of adapter and create a custom list view to display image, name and email.

Refer to this link for more details http://www.androidhive.info/2014/07/android-custom-listview-with-image-and-text-using-volley/

arjun
  • 3,514
  • 4
  • 27
  • 48
  • Thanks Arjun, however I'm trying to pull from my `Contacts`, which is a whole other beast than just a `ListView` alone. Some tutorials say that `CursorLoader` has its own view, so I don't need to provide one. Then others say to get the photo/email from a query using my `Cursor`, accessing the `ContactContracts.Data` class table. Still researching, but if I find out more, I will note it here. Thanks for the idea though. – Azurespot Dec 24 '14 at 06:09
0

Assuming your cursor is returning all the appropriate data, it looks like you just need to adjust some of the starting values when initializing the adapter. Try updating the following:

public static final String[] FROM = { ContactsContract.Contacts.DISPLAY_NAME_PRIMARY, ContactsContract.Contacts.PHOTO_THUMBNAIL_URI };
private static final int[] TO = { R.id.text_name, R.id.contact_thumbnail };

int layout = R.layout.contact_list_item;

That should get your adapter showing both the image and the display name. Overriding the getView() method as suggested by Arjun Sna is not required since the SimpleCursorAdapter supports displaying both TextView and ImageView. If you want to add the email, you need to update your XML to include another TextView, then add it's associating info to the FROM and TO arrays.

Ifrit
  • 6,791
  • 8
  • 50
  • 79