The second screenshot shows a
ListView
.
Click on an item to pop up a piece of
Toast
.
The position numbers are the zero-based positions of each item in the
ListView
;
Curly is number 0 since he is at the top of the list in alphabetical order.
The id numbers are the one-based is numbers of each item in the database;
Curly is number 3 since I created Moe and Larry before him.
Before I ran this app, I used the People app to create contacts named Moe, Larry, Curly, Shemp, in that order. To keep it simple, I gave each contact no information besides its name. There are no addresses, phone numbers, etc.
We saw a
Cursor
here.
We saw a
ListView
whose adapter is a
ArrayAdapter<String>
here.
The next step is a
ListView
whose adapter is a
SimpleCursorAdapter
containing a
Cursor
.
This will probably be how you display records from an SQLite database.
A
Cursor
in an adapter must read the field named
_id
.
That means that the
projection
array must contain this field.
The fourth and fifth arguments of the
SimpleCursorAdapter
’s
constructor must be arrays of the same length.
CursorAdapter
is an abstract class;
we would have to define many of its methods.
SimpleCursorAdapter
is a concrete (non-abstract) class;
all of its methods are already defined with stubs.
The
position
argument of
onListItemClick
is the position of the item in the
ListView
.
The
id
argument is the position of the item in the adapter,
which in this case is the
_id
field of the SQLite database that holds the contacts.
They are different because the
position
s
start at 0 while the
_id
s
start at 1.
They are also different
because of the
sortOrder
passed to the constructor of the
CursorLoader
.
Since the data in this list comes from a database,
getItemAtPosition
returns a record in the database.
It does this by returning a
Cursor
with which the record can be read.
MainActivity.java
R.java
activity_main.xml
.
The
RelativeLayout
contains a
ListView
.
AndroidManifest.xml
has
android.permission.READ_CONTACTS
build.gradle
(Module: app).
Each line of the
ListView
is displayed as the
TextView
in the following file
~/Library/Android/sdk/platforms/android-22/data/res/layout/simple_list_item_1.xml
(shown without its
<?xml>
tag and copyright notice).
The identifying number of this file is
android.R.layout.simple_list_item_1
This id number starts with
android.R
because the file is on your hard disk.
A file that is part of your app
(e.g.,
activity_main.xml
)
has an id number that starts with
R
(e.g.,
R.layout.activity_main
).
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceListItemSmall" android:gravity="center_vertical" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:minHeight="?android:attr/listPreferredItemHeightSmall" />
See
Using
a Loader
for another example of not using a
Cursor
until a load is completed.
ApiDemos/src/com/example/android/apis/app/LoaderCursor.java
SearchView
.)
The
Activity
creates a
Fragment
,
specifically a
ListFragment
whose user interface is a built-in
ListView
.
The
ListFragment
creates a
SimpleCursorAdapter
without a
Cursor
.
Later,
when
onCreateLoader
is called,
it creates a
CursorLoader
.
Still later,
when
onLoadFinished
is called,
it creates a
Cursor
that is
swapped
into
the
SimpleCursorAdapter
.
~/Library/Android/sdk/platforms/android-22/data/res/layout/simple_list_item_2.xml
TwoLineListItem
.
The second
TextView
can have
android:layout_below
and
android:layout_alignStart
because
TwoLineListItem
is a subclass of
relativeLayout
.
<?xml>
and copyright omitted.
The
android:id
attributes need no plus signs because the values
android.R.id.text1
and
android.R.id.text2
are built into the SDK.
<TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?attr/listPreferredItemHeight" android:mode="twoLine" android:paddingStart="?attr/listPreferredItemPaddingStart" android:paddingEnd="?attr/listPreferredItemPaddingEnd"> <TextView android:id="@id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:textAppearance="?attr/textAppearanceListItem" /> <TextView android:id="@id/text2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/text1" android:layout_alignStart="@id/text1" android:textAppearance="?attr/textAppearanceListItemSecondary" /> </TwoLineListItem>
SimpleCursorAdapter adapter = new SimpleCursorAdapter( this, android.R.layout.simple_list_item_2, null, new String[] {ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts._ID}, new int[] {android.R.id.text1, android.R.id.text2}, 0 //flags );
Since class
TwoLineListItem
is now deprecated,
you could create the following file
simple_list_item_2.xml
containing a
LinearLayout
in place of the
TwoLineListItem
.
I had to change some of the attributes in the above file
simple_list_item_2.xml
to get them to compile for older versions of Android.
In the Android Studio
project
view,
select the folder
app/res/layout
and pull down
File → New… → Layout resource file
New Resource File
File name: simple_list_item_2
Root element: LinearLayout
OK
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:minHeight="?attr/listPreferredItemHeight" android:paddingLeft="?attr/listPreferredItemPaddingLeft" android:paddingRight="?attr/listPreferredItemPaddingRight"> <TextView android:id="@+id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:textAppearance="?attr/textAppearanceListItem"/> <TextView android:id="@+id/text2" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?attr/textAppearanceListItemSmall"/> </LinearLayout>
SimpleCursorAdapter adapter = new SimpleCursorAdapter( this, R.layout.simple_list_item_2, null, new String[] {ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts._ID}, new int[] {R.id.text1, R.id.text2}, 0 //flags );
SimpleCursorAdapter
has called
setText
to set the text of each
TextView
.
But now we will call the
setText
ourselves
for the
TextView
s
in the display name column.
Insert the following code after creating the
SimpleCursorAdapter
.
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() { //This method will be called for every column of every row. //Most of the time it will do nothing and just return false. //But for each TextView belonging to the display name column, //it will set the text of the TextView and return true. @Override public boolean setViewValue(View view, Cursor cursor, int columnIndex) { int displayNameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); if (columnIndex == displayNameIndex) { String displayName = cursor.getString(displayNameIndex); ((TextView)view).setText(displayName.toUpperCase()); return true; //Indicates that this method has written the text into the TextView. } return false; //Indicates that the text still needs to be written into the TextView. } });