Contact List using ListView and SimpleCusrorAdapter with Contact Photos and Filter/Search
I had been looking for a simpler solution and my final solution is quite closer to the one Daniel mentioned here so I thought I should share mine here. I am using Fragment to show Device Contacts as a list of names with their pictures. Result is pretty similar to that of Daniel's but is showing only names. More information can be shown very easily once you understand the code.
In my case I was fetching names and pictures from ContactsContract using PHOTO_URI so I didn't have to extend SimpleCursorAdapter as Daniel had to.
My example also includes filtering the list of contacts as user types in SearchView to find a contact
I have a Fragment called FragmentContacts and two Layout files, first the main Layout frag_contacts.xml and second for each contact row list_row_contact.
frag_contacts.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:padding="8dip">
        <android.support.v7.widget.SearchView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white"
            android:id="@+id/searchView"/>
    </FrameLayout>
    <LinearLayout
        android:id="@+id/ll_contactList"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="9"
        android:orientation="vertical" >
        <ListView
            android:id="@+id/lv_ContactList"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:divider="#aaaaaa"
            android:dividerHeight="1dp" >
        </ListView>
    </LinearLayout>
</LinearLayout>
list_row_contact.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:descendantFocusability="blocksDescendants">
    <FrameLayout
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:gravity="center">
        <ImageView
            android:id="@+id/imgContact"
            android:layout_width="35dip"
            android:layout_height="35dip"
            android:layout_gravity="center"
            android:layout_margin="5dip" />
    </FrameLayout>
    <TextView
        android:id="@+id/contact_name"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:layout_weight="6"
        android:gravity="center_vertical"
        android:textSize="18sp"
        android:paddingLeft="10dip">
    </TextView>
</LinearLayout>
FragmentContacts.java
public class FragmentContacts extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>{
    private ListView lv_ContactList;
    private SearchView searchView;
    private SimpleCursorAdapter mCursorAdapter;
    private static final String DISPLAY_NAME = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME;
    private static final String[] contactsColumns = {    Contacts._ID, Contacts.LOOKUP_KEY, DISPLAY_NAME, Contacts.PHOTO_URI    };
    private final String contactsFilter = "(" +Contacts.HAS_PHONE_NUMBER+ "='1') AND (" + Contacts.IN_VISIBLE_GROUP + "='1')";
    private final String contactsSortOrder = DISPLAY_NAME + " COLLATE LOCALIZED ASC";
    private final static String[] listDisplayColumns = { DISPLAY_NAME, Contacts.PHOTO_URI   };
    private final static int[] listDataViewIDs = { R.id.contact_name, R.id.imgContact };
    String[] mSelectionArgs;
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.frag_contacts, null);
        lv_ContactList = (ListView)view.findViewById(R.id.lv_ContactList);
        searchView = (SearchView)view.findViewById( R.id.searchView);
        return view;
    }
    @Override
    public void onResume(){
        super.onResume();
        mCursorAdapter= new SimpleCursorAdapter( getActivity(), R.layout.list_row_contact, null, listDisplayColumns, listDataViewIDs, 0);
        lv_ContactList.setAdapter(mCursorAdapter);
        getLoaderManager().initLoader(0, null, this);
        searchView.setOnQueryTextListener( new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit( String query ) {return false;}
            @Override
            public boolean onQueryTextChange( String newText ) {
                if( newText.isEmpty() ) mSelectionArgs = null;
                else mSelectionArgs = new String[]{ "%"+newText.trim()+"%"};
                getLoaderManager().restartLoader( 0, null, FragmentContacts.this );
                return false;
            }
        } );
    }
    @Override
    public Loader<Cursor> onCreateLoader( int id, Bundle args ) {
        if(mSelectionArgs == null)
            return new CursorLoader( getActivity(), Contacts.CONTENT_URI, contactsColumns, contactsFilter, null, contactsSortOrder );
        else
            return new CursorLoader( getActivity(), Contacts.CONTENT_URI, contactsColumns, contactsFilter + " AND (" + DISPLAY_NAME+" LIKE ?)", mSelectionArgs, contactsSortOrder );
    }
    @Override
    public void onLoadFinished( Loader<Cursor> loader, Cursor data ) {
        mCursorAdapter.swapCursor(data);
    }
    @Override
    public void onLoaderReset( Loader<Cursor> loader ) {
        mCursorAdapter.swapCursor(null);
    }
}