原文地址:http://thinkandroid.wordpress.com/2010/01/11/custom-cursoradapters/
Hey everyone,
Now that we have the SimpleCursorAdapter figured out, when writing your Custom Cursor Adapter you’ll see where some of the parameters come into play. Here’s an example of a Custom Cursor Adapter that I built:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
public
class
ContactListCursorAdapter
extends
SimpleCursorAdapter
implements
Filterable {
private
Context context;
private
int
layout;
public
ContactListCursorAdapter (Context context,
int
layout, Cursor c, String[] from,
int
[] to) {
super
(context, layout, c, from, to);
this
.context = context;
this
.layout = layout;
}
@Override
public
View newView(Context context, Cursor cursor, ViewGroup parent) {
Cursor c = getCursor();
final
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent,
false
);
int
nameCol = c.getColumnIndex(People.NAME);
String name = c.getString(nameCol);
/**
* Next set the name of the entry.
*/
TextView name_text = (TextView) v.findViewById(R.id.name_entry);
if
(name_text !=
null
) {
name_text.setText(name);
}
return
v;
}
@Override
public
void
bindView(View v, Context context, Cursor c) {
int
nameCol = c.getColumnIndex(People.NAME);
String name = c.getString(nameCol);
/**
* Next set the name of the entry.
*/
TextView name_text = (TextView) v.findViewById(R.id.name_entry);
if
(name_text !=
null
) {
name_text.setText(name);
}
}
@Override
public
Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if
(getFilterQueryProvider() !=
null
) {
return
getFilterQueryProvider().runQuery(constraint); }
StringBuilder buffer =
null
;
String[] args =
null
;
if
(constraint !=
null
) {
buffer =
new
StringBuilder();
buffer.append(
"UPPER("
);
buffer.append(People.NAME);
buffer.append(
") GLOB ?"
);
args =
new
String[] { constraint.toString().toUpperCase() +
"*"
};
}
return
context.getContentResolver().query(People.CONTENT_URI,
null
,
buffer ==
null
?
null
: buffer.toString(), args, People.NAME +
" ASC"
);
}
}
|
The Custom Cursor Adapter created above extends the SimpleCursorAdapter and also implements the Filterable class (which I’ll get to later). Here you can see the importance of passing in the layout of the list row entry as in the cursor adapter when the actual views in the list are being built (via newView and bindView) and both methods use this layout to inflate the view, which you can then use to retrieve the TextView / ImageView / etc that are custom designed in your list entry row XML file.
In my example, when a view is created for the first time (via newView), you first inflate the view and retrieve the cursor WHICH YOU PASSED IN. Remember this as depending on which columns you told your cursor to return, those are the columns that you can retrieve information from in your Custom Cursor Adapter. In other words, if my cursor looked like:
1
|
Cursor c = getContentResolver().query(People.CONTENT_URI,
new
String[] { People._ID, People.NAME },
null
,
null
,
null
);
|
And I tried to retrieve the People.NUMBER column from my cursor, it will return an SQL exception.
The rest should be pretty self explanatory – simply grab the data you want and do what you want with the data (i.e. calculations, grabbing images, etc) and then put them into your inflated views.
One more thing to note is that YOU MUST place something into each view (even if it is NULL). For instance, had my adapter looked like:
1
2
3
4
5
6
|
String name = c.getString(nameCol);
TextView name_text = (TextView) v.findViewById(R.id.name_entry);
if
(name_text !=
null
&& name !=
null
) {
name_text.setText(name);
}
|
Then you’ll notice some weird behavior – namely, that you’ll see the names start shifting as you scroll up and down the list. What happens is that if you don’t instantiate and place something into your TextView (basically to act as a place holder) then in your bindView method nothing gets bound to some of the TextViews and thus the shifting. So basically, if you see stuff shifting around in your lists, then that’s a big flag for make sure you are binding things to all of your views in both your newView and bindView methods.
Finally, a little on the Filterable implementation. If you want a list that filters as the user starts to type then this is what you need to do. Once you implement the Filterable class, you’ll need to override the runQueryOnBackgroundThread() method and this code snippet:
1
2
3
4
5
6
7
8
9
|
StringBuilder buffer =
null
;
String[] args =
null
;
if
(constraint !=
null
) {
buffer =
new
StringBuilder();
buffer.append(
"UPPER("
);
buffer.append(People.NAME);
buffer.append(
") GLOB ?"
);
args =
new
String[] { constraint.toString().toUpperCase() +
"*"
};
}
|
And basically the StringBuilder is just building the constraint that the cursor will run. If you want to mess with this, then you’ll need to keep:
1
2
3
4
|
buffer =
new
StringBuilder();
buffer.append(
"UPPER("
);
// THIS YOU CAN CHANGE
buffer.append(
") GLOB ?"
);
|
But you can customize the rest of the constraint, for instance:
1
2
3
4
|
buffer =
new
StringBuilder();
buffer.append(
"UPPER("
);
buffer.append(People.NAME +
" IS NOT NULL"
+
" AND "
+ People.NUMBER_KEY +
" LIKE '630%'"
);
buffer.append(
") GLOB ?"
);
|
And that’s it! Next post will be on BaseAdapters, and I’ll also talk a little on the comparison between these two adapters.
To see all posts on CursorAdapters, please visithttp://thinkandroid.wordpress.com/category/android-tutorials/cursoradapter-tutorials/
Happy coding.
- jwei