I'm having an issue that I just can't seem to trace down the cause of. This may be simply due to my lack of familiarity with loaders...
I have a custom adapter I use to set a prompt for a spinner in the "closed" position (single line display) until the first selection is made. I've used this before with great success, but this time it's not working. The only difference is that now I'm using loaders.
When the adapter tries to access the cursor in my overridden getView method, the cursor is null and of course I get a force close with a null pointer exception. I don't understand how the cursor can be null there as obviously the getDropDownView method (which I don't override) has access to the cursor as the dropdown is populated...
All I can think is that for some reason the getView method is retaining the original null that is passed in as a placeholder while the loader does it's work. I would think that when I use changeCursor that reference would change as well, but it seems that it isn't.
main activity adapter call
adapter = new CursorAdapter_SpinnerPrompt(this,
R.layout.rowlayout_black, null, new String[] { ThreadMillDB.THREADMILL_THREAD }, new int[] { R.id.ListItem });
mThreadChooser.setAdapter(adapter);
relevant loader code
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.changeCursor(cursor);
}
adapter code
public class CursorAdapter_SpinnerPrompt extends SimpleCursorAdapter {
private Context context;
private int layout;
private Cursor c;
private String[] from;
private int[] to;
public static boolean spinnerFlag = false;
public CursorAdapter_SpinnerPrompt(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
this.context = context;
this.layout = layout;
this.c = c;
this.from = from;
this.to = to;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = View.inflate(context, layout, null);
if (spinnerFlag != false) {
c.moveToPosition(position); // cursor is null here... why? How to fix?
TextView tv = (TextView) convertView;
tv.setText(c.getString(c.getColumnIndex(ThreadMillDB.THREADMILL_THREAD)));
} else {
TextView tv = (TextView) convertView;
tv.setText("Choose a thread");
tv.setTextColor(Color.parseColor("#778899"));
}
return convertView;
}
}
UPDATE
And of course in typing this out I figured out a solution. I moved the creation of the adapter out of my activity onCreate and into onLoadFinished and put the cursor into the call. However, this seems messier than the way I was trying to do it. Is there any modification I can make to my original methods (shown above) to make it work without having to move the adapter instantiation to the onLoadFinished method?
your suspicious is correct.
The cursor reference you remembered on your constructor is not the same that you passed on chageCursor(Cursor c).
The proper way to get the cursor on a CursorAdapter is calling getCursor() than you'll get the reference to the cursor you passed on changeCursor(Cursor c).
Related
This problem has been stuck for a while in my head.
What I need to do:
Show a listview with alternating resources for the items in the listView.
What is my problem:
So far I can alternate resources and show no data, or show the data but not alternate resources. The first item works well every time, but not form there onwards. I think I'm very close but I just can't think what is going wrong...
What have I done:
I have used a custom simple cursor adapter.
Where is the code:
public class DialogCursor extends SimpleCursorAdapter {
private LinearLayout wrapper;
private TextView burbuja;
public DialogCursor(Context context, int layout, Cursor c, String[] from,
int[] to, int flags) {
super(context, layout, c, from, to, flags);
// TODO Auto-generated constructor stub
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
Context context = parent.getContext();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.dialogo_row, parent, false);
}
burbuja = (TextView) row.findViewById(R.id.idiomaselec);
wrapper = (LinearLayout) row.findViewById(R.id.wrapper);
//get reference to the row
View view = super.getView(position, convertView, parent);
Log.d("Dialogo","enters getview");
Log.d("Dialogo",Integer.toString(position));
//check for odd or even to set alternate colors to the row background
if(position % 2 == 0){
Log.d("Dialogo","Even");
burbuja.setBackgroundResource(R.drawable.bubble_green);
wrapper.setGravity(Gravity.LEFT);
}
else {
Log.d("Dialogo","not even");
burbuja.setBackgroundResource(R.drawable.bubble_yellow);
wrapper.setGravity(Gravity.RIGHT);
}
return row;
}
}
The cursor adapter is called from this other class (just showing relevant part)
String[] from = new String[] { DialogoTable.TABLE_DIALOGO + "." + columna };
// Fields on the UI to which we map
final int[] to = new int[] { R.id.idiomaselec};
Log.d("Dialogo","entra en fillData2");
getLoaderManager().initLoader(0, null, this);
if (bot) {
Log.d("Dialogo","entra en fillData2.5");
getLoaderManager().restartLoader(0, null, this);
}
adapter2 = new DialogCursor(this, R.layout.dialogo_row, null, from, to, 0);
setListAdapter(adapter2);
And the output:
If I return row (last line of code)
I get the background resources in the right place but with no data
If I return view (last line of code)
I get the data but only the first item has the right background resources.
One last note:
I have followed this example
http://adilsoomro.blogspot.com/2012/12/android-listview-with-speech-bubble.html
but I dont want to create a class message since I wnat the data from my DB.
Thank you for your help :)
In a similar case I was able to have a custom cursorAdapter alternate resources based on the cursor position. I put the following code in my bindView where entryView is the passed in view. I am overriding getView at all.
if(cursor.getPosition()%2 == 1){
entryView.findViewById(R.id.title_relative).setBackgroundColor(getResources().getColor(R.color.orange));
}else{
entryView.findViewById(R.id.title_relative).setBackgroundColor(getResources().getColor(R.color.blue));
}
I'm trying to populate a listview with images, whose URI is returned from a cursor.
I'm not sure if I should use a viewbinder with my simplecursoradapter, or to create a custom simplecursoradapter that somehow does the same job, and I also don't know how to implement either of those options.
My adapter has the following:
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.albumitem, albumCursor, displayFields, displayViews);
String[] displayFields = new String[] { AudioColumns.ALBUM,
AudioColumns.ARTIST, AlbumColumns.NUMBER_OF_SONGS };
int[] displayViews = new int[] { R.id.albumTitle, R.id.artistTitle,
R.id.totalSongs};
But I'd like to add an image to R.id.albumView as well.
I can obtain the image normally (outside of an adapter) by retrieving the AlbumColumns.ALBUM_ID from the cursor, and then using the following code:
currentAlbumId = idList.get(currentSongIndex);
currentAlbumIdLong = Integer.parseInt(currentAlbumId);
artworkUri = Uri.parse("content://media/external/audio/albumart");
currentSongUri = ContentUris.withAppendedId(artworkUri, currentAlbumIdLong);
albumArt.setImageURI(currentSongUri);
My problem is, I have no idea how to perform a similar task inside the adapter. Anyhow, my best guess is to use a viewBinder. Could somebody kindly show me how to implement this?
Thank you for your help.
--Edit--
Two great answers. Thank you both.
private class YourCustomAdatper extends CursorAdapter{
private final LayoutInflater mInflater;
public YourCustomAdatper(Context context, Cursor cursor) {
super(context, cursor, true);
mInflater = LayoutInflater.from(context);
}
#Override
public void bindView(View view, Context context, final Cursor cursor)
{
ImageView imageview=view.findViewById(yourImageview_id);
//do rest of task
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final View view =mInflater.inflate(yourLayout, parent, false);
return view;
}
}
You can use a custom CursorAdpater. Pass the arguments you need to the constructor of the Custom class. In the newView inflate the layout and in bindView set the values. Refer http://blog.cluepusher.dk/2009/11/16/creating-a-custom-cursoradapter-for-android/
I'm trying to display information about an object, concretely a customer. I mean name, last name, telephone number, adress, .... that is the result by a query on my database.
I want to display this on a ListView, so better if my activity inherit of a ListActivity because ListView is self-contained by default.
I do the next: I have a class called Customer that saves all information by the query (Cursor) that only return one record(customer) and I don't know how to pass this object ,I don't find the most appropriate adapter.
Can I do this by this way or I must convert this object into an ArrayList that contains all information and, in fact, use ArrayAdapter for my adapter?
Anyone knows how to do this??
Thanks.
Once you have your cursor queried you should do something like this
String[] from = new String[]{"FirstName"};
int[] to = new int[]{R.id.row};
SimpleCursorAdapter sca = new SimpleCursorAdapter(this, R.layout.row_item, cur, from, to);
setListAdapter(sca);
check here the documentation for SimpleCursorAdapter.
If you need to do something more complicated with your views you should implement your own custom Cursor adapter:
public class ExampleCursorAdapter extends CursorAdapter {
public ExampleCursorAdapter(Context context, Cursor c) {
super(context, c);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView summary = (TextView)view.findViewById(R.id.summary);
summary.setText(cursor.getString(
cursor.getColumnIndex(ExampleDB.KEY_EXAMPLE_SUMMARY)));
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.item, parent, false);
bindView(v, context, cursor);
return v;
}
}
And finally, I can find the solution it would be. I'm going to tell you how I did.
I wanted to display details customer by this way:
row 1-> Name: name_value
row 2-> Last Name: lastname_value
row 3-> Phone: phone_value
...
N. n row -> n_field: n_value
Fields that describe what value is going to show, i mean "Name:", "Last Name:", "Phone:", ...
I put all this values on a string-array . I captured that by a String [] .
Values, show proper value next the correct field. I put all this inside a ArrayList
and next only have to write the proper adapter:
This Activity inherit ListActivity, ListView by default.
SimpleCursorAdapter is not proper because we have to pass the cursor as argument of this adapter but my Cursor only returns only one result, only one record .
And CursorAdapter is refused by the same reason. What can I do next? ArrayAdapter :
public class DetalleClienteAdapter extends ArrayAdapter<String>
{
private String[] campos_cliente;
private ArrayList<String> detalles_cliente;
public DetalleClienteAdapter(Context contexto, int layout, ArrayList<String> detalles)
{
super(contexto,layout,detalles);
//mInflater = LayoutInflater.from(contexto);
// LOS VALORES DE LOS CAMPOS:
detalles_cliente = detalles;
// CAMPOS DE DETALLES: nombre, apellidos, etc...
android.content.res.Resources res = getResources();
campos_cliente = res.getStringArray(R.array.array_detalle_cliente);
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
// TODO Auto-generated method stub
//return super.getView(position, convertView, parent);
LayoutInflater inflater= getLayoutInflater();
View fila = inflater.inflate(R.layout.detalles_cliente, parent, false);
TextView campo =(TextView)fila.findViewById(R.id.detalles_campo);
TextView valor =(TextView)fila.findViewById(R.id.detalles_valor);
campo.setText(campos_cliente[position]);
valor.setText(detalles_cliente.get(position));
return fila;
}
}
Now I'm going to write a more efficient adapter, with ViewHolder. What is your opinion about that?
Thanks.
Hi I want to achieve the following result in my ListActivity:
First item, descritpion
Second item, description
...
where Name and description come from
Cursor c = db.rawQuery(...);
I used a SimpleCursorAdapter and I had no problems. Then I decided to make some modification to data that I have in cursor before displaying it. I wanted to have a internal counter that would increment every time cursor adapter dislays a new view. That's why I decided to implement my own CustomCursorAdapter extending SimpleCursorAdapter:
public class TrainingsListCursorAdapter extends SimpleCursorAdapter{
private Context context;
private int layout;
int list_item_order_number = 1;
public TrainingsListCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
this.context = context;
this.layout = layout;
}//contructor
#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);
String durationCol = c.getString(c.getColumnIndex("duration"));
if(durationCol != null){
durationCol = minutesForSeconds(durationCol);
}
TextView textViewDuration = (TextView) v.findViewById(R.id.duration);
TextView order_number = (TextView) v.findViewById(R.id.list_item_num);
if(order_number != null){
order_number.setText(String.valueOf(list_item_order_number));
}
if(textViewDuration != null){
textViewDuration.setText(durationCol);
}
list_item_order_number++;
return v;
}
I have a member variable _list_item_order_number_ that is incremented by one every time a view/row is drawn (by calling newView() method). This number is displayed in every view/list item and it corresponds to list item order...
The problem I have is that when I'm rotating the screen this number doesn't correspond to list items anymore. For example my 3rd item has value 1 and so on...
I guess this has something to do with the order Android is drawing the views. If I scroll the list to, let's say, the middle and then rotate, these numbers are all mixed like this:
First Item
Second item
Third item
I have a simple cursor adapter set on a list in my application as follows:
private static final String fields[] = {"GenreLabel", "Colour", BaseColumns._ID};
datasource = new SimpleCursorAdapter(this, R.layout.row, data, fields, new int[]{R.id.genreBox, R.id.colourBox});
R.layout.row consists of two TextViews (genreBox and colourBox). Rather than setting the content of the TextView to the value of "Colour" , I would like to set its background colour to that value.
What would I need to do to achieve this?
Check out SimpleCursorAdapter.ViewBinder.
setViewValue is basically your chance to do whatever you wish with the data in your Cursor, including setting the background color of your views.
For example, something like:
SimpleCursorAdapter.ViewBinder binder = new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
String name = cursor.getColumnName(columnIndex);
if ("Colour".equals(name)) {
int color = cursor.getInt(columnIndex);
view.setBackgroundColor(color);
return true;
}
return false;
}
}
datasource.setViewBinder(binder);
Update - if you're using a custom adapter (extending CursorAdaptor) then the code doesn't change a whole lot. You'd be overriding getView and bindView:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView != null) {
return convertView;
}
/* context is the outer activity or a context saved in the constructor */
return LayoutInflater.from(context).inflate(R.id.my_row);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
int color = cursor.getInt(cursor.getColumnIndex("Colour"));
view.setBackgroundColor(color);
String label = cursor.getString(cursor.getColumnIndex("GenreLabel"));
TextView text = (TextView) findViewById(R.id.genre_label);
text.setText(label);
}
You're doing a bit more manually, but it's more or less the same idea. Note that in all of these examples you might save on performance by caching the column indices instead of looking them up via strings.
What you're looking for requires a custom cursor adapter. You can subclass SimpleCursorAdapter. This basically give access to the view as its created (although you'll be creating it yourself).
See this blog post on custom CursorAdapters for a complete example. Particularly, I think you'll need to override bindView.