Well, I have read almost 50 links related to this question, but my code still not working.
I have a Custom adapter which extends SimpleCursorAdapter class, and I use that adapter to fill the ListView on onCreate method
private void populateListView()
{
String[] from = new String[] { SchemaHelper.TASK_DESCRIPTION, SchemaHelper.TASK_CREATED_ON, SchemaHelper.TASK_ID };
int[] to = new int[] {R.id.lv_row_description, R.id.lv_row_created_on};
tasksCursor = schemaHelper.getTasks();
startManagingCursor(tasksCursor);
tasksAdapter = new TasksAdapter(this, R.layout.tasks_listview_row, tasksCursor, from, to);
setListAdapter(tasksAdapter);
}
The App is a simple task manager, I want to update the ListView contents when the user submits a new task without calling setListAdapter() again.
I have tried notifyDataSetChanged (running on ui thread), invalidate, requery(deprecated)... almost everything.
I'm doing something wrong?
EDIT:
This is the method where I add a new task to the database
private void addTask(String description)
{
String message = "";
schemaHelper.open();
if(schemaHelper.isAlreadyInDatabase(description))
{
message = getString(R.string.task_already_exists);
}
else
{
message = getString(R.string.task_succesfully_added);
schemaHelper.insertTask(description);
populateListView();
newTask.setText("");
}
schemaHelper.close();
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
ADAPTER CLASS:
private class TasksAdapter extends SimpleCursorAdapter
{
private LayoutInflater layoutInflater;
private Cursor cursor;
public TasksAdapter(Context context, int layout, Cursor c, String[] from, int[] to)
{
super(context, layout, c, from, to);
cursor = c;
cursor.moveToFirst();
layoutInflater = LayoutInflater.from(context);
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if(cursor.getPosition() < 0)
{
cursor.moveToFirst();
}
else
{
cursor.moveToPosition(position); // Here throws the error
}
View row = layoutInflater.inflate(R.layout.tasks_listview_row, null);
TextView description = (TextView) row.findViewById(R.id.lv_row_description);
TextView createdOn = (TextView) row.findViewById(R.id.lv_row_created_on);
description.setText(cursor.getString(cursor.getColumnIndexOrThrow(SchemaHelper.TASK_DESCRIPTION)));
createdOn.setText(getString(R.string.added_on) + " " + TaskHelper.formatDateWithSuffix(cursor.getString(cursor.getColumnIndexOrThrow(SchemaHelper.TASK_CREATED_ON))));
return row;
}
}
i dont know much of the taskCursor and taskAdapter but i used ArrayAdapter i guess, well have a look in my code and take your own conclusions.
//LISTVIEW database CONTATO
ListView user = (ListView) findViewById(R.id.lvShowContatos);
//String = simple value ||| String[] = multiple values/columns
String[] campos = new String[] {"nome", "telefone"};
list = new ArrayList<String>();
Cursor c = db.query( "contatos", campos, null, null, null, null, "nome" + " ASC ");
c.moveToFirst();
String lista = "";
if(c.getCount() > 0) {
while(true) {
list.add(c.getString(c.getColumnIndex("nome")).toString());
if(!c.moveToNext()) break;
}
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, list);
user.setAdapter(adapter);
If you don't want to use requery() you can simply pass a new Cursor with the same query:
tasksCursor.close();
tasksCursor = schemaHelper.getTasks();
startManagingCursor(tasksCursor);
tasksAdapter.changeCursor(tasksCursor);
I assume that when you call addTask() you have already called populateListView() once. Try changing addTask() to this:
private void addTask(String description)
{
String message = "";
schemaHelper.open();
if(schemaHelper.isAlreadyInDatabase(description))
{
message = getString(R.string.task_already_exists);
}
else
{
message = getString(R.string.task_succesfully_added);
schemaHelper.insertTask(description);
// Remove call to populateListView(), just update the Cursor
tasksCursor.close();
tasksCursor = schemaHelper.getTasks();
startManagingCursor(tasksCursor);
tasksAdapter.changeCursor(tasksCursor);
newTask.setText("");
}
schemaHelper.close();
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
If this "doesn't work", please be more specific. Is it throwing an error, if so what kind?
You are doing a little too much work in your adapter. Please watch Android's Romain Guy at Google Talks discuss adapters and getView(). However since you only want to pass one special string to your createdOn TextView, let's do something very different and override setViewText():
Try this:
public class TasksAdapter extends SimpleCursorAdapter {
String prefix;
public TasksAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to) {
super(context, layout, cursor, from, to);
// This is constant so set it once and consider adding the space to the end of the String in strings.xml
prefix = getString(R.string.added_on) + " ";
}
#Override
public void setViewText(TextView v, String text) {
if(v.getId() == R.id.lv_row_created_on)
v.setText(prefix + TaskHelper.formatDateWithSuffix(text));
else
super.setViewText(v, text);
}
}
The rest of the data is taken care of with SimpleCursorAdapter's existing methods.
Related
My list contains elements with name and status. The name is a String and the status is an int.
I want to have a ListView where each element shows the name and a drop-down for selecting the status. Also, the background color will change based on the status int.
I have the Cursor:
String[] ItemsData= { "id", "name", "status" };
Cursor ItemsCursor = ItemsDatabase.query(
Database.ITEMS_TABLE, ItemsData, null, null, null, null, null
);
ItemsCursor.moveToFirst();
and I have created the list (programmatically is how I must do it):
ListView ItemsListView = new ListView(this);
Now, I know that I can use and Adapter (like SimpleCursorAdapter) but I don't know how to do it, taking into account that the name is a String inside a TextView and the status is a Spinner with predefined values saved as int in the database, with the default value being the one corresponding to the status. Also, the status changes the background color, too.
Maybe I can't use an Adapter. Also, I don't know how to include a Spinner inside each list element, if possible.
Here is how i do it :
listV = (ListView) findViewById(R.id.viewTest);
ArrayList<HashMap<String, String>> listItem = new ArrayList<HashMap<String, String>>();
int i = 1;
String value = null;
HashMap<String, String> map;
while ((value = database.getStuff(i)) != null) {
map = new HashMap<String, String>();
map.put("title", value);
map.put("desc", "RANDOM TEXT");
map.put("img", String.valueOf(R.drawable.images));
listItem.add(map);
i++;
}
SimpleAdapter s = new SimpleAdapter (this.getBaseContext(), listItem, R.layout.list_content,
new String[] {"img", "title", "desc"}, new int[] {R.id.img, R.id.stuff, R.id.stuff});
listV.setAdapter(s);
And here is where i deal with the cursors :
public String getStuff(int id) {
Cursor c = db.rawQuery("SELECT value FROM test WHERE _id = " +id, null);
return cursorToString(c);
}
private String cursorToString(Cursor c) {
if (c.getCount() == 0)
return null;
c.moveToFirst();
String s = c.getString(0);
c.close();
return s;
}
The "R.layout.list_content" is the .xml file used to setup the shape of each item of your list, you can add images TextView and everything else you want !
Just tell me if you want more infos about the .xml stuff
You can use a CursorAdapter to build the rows from any layout you want.
public class CustomAdapter extends CursorAdapter {
public CustomAdapter(Context context, Cursor c) {
super(context, c, false);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.row, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// Populate view with the cursor, add listeners...
}
}
I'm writing a simple music app for Android 2.1 and stumbled upon such a problem:
In my Activity I have a ListView, which has album cover, album title, artist and number of songs in each row. I query the ContentProvider for all those values to put in the ListView. But some albums on SDCARD have no album cover and the query returns NULL I think. In result in my ListView I have albums where only few have covers.
What I would like to do, is to put some argument when querying ContentProvider or in some other place, to put my default icon as album cover if the query returns null cover. I mean, if the ContentProvider returns nothing as a cover, I would like to put my default icon in this place.
Maybe I should use some other query, not by ManagedCursor?
I saw solution to similar db problem using the IFNULL clause (SELECT IFNULL(title, "------") AS title FROM books;). I thought it could help in some way, but there is no place to put such a clause in ContentProvider query.
Does anyone know how to do it? I put mu code below:
private void getAllAlbumsFromSDCARD()
{
String[] cols = { MediaStore.Audio.Albums.ALBUM, MediaStore.Audio.Albums.ALBUM_ART, MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media._ID, MediaStore.Audio.Albums.NUMBER_OF_SONGS};
Uri allAlbumsUri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
albumsCursor = this.managedQuery(allAlbumsUri, cols, null, null, MediaStore.Audio.Media.ALBUM);
// SELECT IFNULL(title, "------") AS title FROM books;
Log.d(TAG, "" +albumsCursor.getCount());
if(albumsCursor!=null)
{
Log.d(TAG, "Managing cursor");
startManagingCursor(albumsCursor);
//
String[] from = new String[] { MediaStore.Audio.Albums.ALBUM_ART, MediaStore.Audio.Albums.ALBUM, MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Albums.NUMBER_OF_SONGS };
int[] to = new int[] { R.id.cover, R.id.album, R.id.artist, R.id.count };
ListAdapter albums = new SimpleCursorAdapter(this, R.layout.album_row, albumsCursor, from, to);
this.setListAdapter(albums);
}
}
I found that solution for my problem is custom CursorAdapter. I paste a code that makes exactly what I wanted:
public class MyCursorAdapter extends SimpleCursorAdapter{
private MainActivity mainActivity;
private int layout;
public MyCursorAdapter(MainActivity context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
this.mainActivity = context;
this.layout = layout;
}
public View getView(int position, View convertView, ViewGroup parent) {
// Cursor to current item
Cursor cursor = getCursor();
cursor.moveToPosition(position);
View v = mainActivity.getLayoutInflater().inflate(layout, parent, false);
TextView albumTV = (TextView) v.findViewById(R.id.album);
if (albumTV != null)
{
int index = cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM);
String album = cursor.getString(index);
albumTV.setText(album);
}
TextView artistTV = (TextView)v.findViewById(R.id.artist);
if (artistTV != null)
{
int index = cursor.getColumnIndex(MediaStore.Audio.Albums.ARTIST);
String artist = cursor.getString(index);
artistTV.setText(artist);
}
TextView countTV = (TextView)v.findViewById(R.id.count);
if (countTV != null)
{
int index = cursor.getColumnIndex(MediaStore.Audio.Albums.NUMBER_OF_SONGS);
String count = cursor.getString(index);
countTV.setText(count);
}
ImageView cover = (ImageView) v.findViewById(R.id.cover);
if (cover != null)
{
int index = cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM_ART);
String path = cursor.getString(index);
Log.d("MCA", ""+path);
if(path == null)
{
cover.setImageResource(R.drawable.no_cover_64);
cover.setScaleType(ImageView.ScaleType.FIT_CENTER);
}
else
{
BitmapDrawable icon = new BitmapDrawable(path);
cover.setImageDrawable(icon);
}
}
return v;
}
}
Ok, so this has been somewhat addressed alot on this site, however I do not believe the exact problem with what my code uses. I am filling a listView with CheckedTextViews which works completely. However when I click on an item it gets checked but when I scroll up and down random rows are also checked. I realize it must have something to do with how the ListView keeps track of the items. I am running into some errors at the moment. I attempted to fill a hashmap with the list of the rows so I can keep track which one is set to true and which are false. However I am not positive where to implement the map and try to fill it.
Here is my OnCreate
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewmenu);
//Get table name of menu clicked.
Bundle extras = getIntent().getExtras();
tableName = extras.getString("table");
// map each contact's name to a TextView in the ListView layout
String[] from = new String[] { "name" };
int[] to = new int[] { R.id.toppingCheckedTextView };
for(int i=0; i< from.length; i++){
map.put(i, false);
}
contactAdapter = new SimpleCursorAdapter(
ViewToppingListing.this, R.layout.toppings_list_item, null, from, to);
setListAdapter(contactAdapter); // set contactView's adapter
}
I attempt to place the map in the onCreate to fill it however it complains about a nullpointer.
Here is where I tried using the OnListItemClick method
#Override
protected void onListItemClick(ListView arg0, View arg1, int arg2, long arg3){
final int index = arg2 - arg0.getFirstVisiblePosition();
View v = arg0.getChildAt(index);
CheckedTextView ctv = (CheckedTextView) v.findViewById(R.id.toppingCheckedTextView);
if((Boolean)map.get(index) == true){
ctv.setChecked(true);
ctv.setVisibility(View.VISIBLE);
} else{
ctv.setVisibility(View.GONE);
}
}
I have read alot on this, and it seems that alot of solutions involves using getView(), however I don't know if that applies to my situation. Any help would be greatly appreciated!
First of all do you need a SimpleCursorAdapter? You set the adapter with a null cursor:
contactAdapter = new SimpleCursorAdapter(
ViewToppingListing.this, R.layout.toppings_list_item, null, from, to); // the third parameter is the cursor and you set it to null!
The behavior you see it's because of the ListView is recycling views and yes you'll have to implement your own adapter and override bindView(). The code bellow is based on another answer to a similar question maybe you'll want to look at it( Getting the selected View from ListView ). Here is an example:
public class TestCursorAdapter extends ListActivity {
MySimpleAdapter adapter;
private HashMap<Long, Boolean> positionHide = new HashMap<Long, Boolean>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] columns = new String[] { "_id", "name" };
MatrixCursor mc = new MatrixCursor(columns); // cursor for testing
for (int i = 1; i < 35; i++) {
long id = i;
mc.addRow(new Object[] { id, "Name" + i });
}
String[] from = new String[] { "name" };
int[] to = new int[] { R.id.checked_text };
adapter = new MySimpleAdapter(this,
R.layout.adapter_mysimpleadapter_row, mc, from, to);
setListAdapter(adapter);
}
private class MySimpleAdapter extends SimpleCursorAdapter {
public MySimpleAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
super.bindView(view, context, cursor);
CheckedTextView ctv = (CheckedTextView) view
.findViewById(R.id.checked_text);
long pos = cursor.getLong(0); // the id from the cursor
if (positionHide.get(pos) == null) {
ctv.setChecked(false);
// we don't have this id in the hashmap so the value is by
// default false, the TextView is GONE
} else {
// we have the value in the Hashmap so see what it is and set
// the textview visibility from this value
Boolean tmp = positionHide.get(pos);
if (tmp.booleanValue()) {
ctv.setChecked(true);
} else {
ctv.setChecked(false);
}
}
}
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Boolean tmp = positionHide.get(id);
if (tmp == null) {
// if null we don't have this key in the hashmap so
// we add it with the value true
positionHide.put(id, true);
} else {
positionHide.put(id, !tmp.booleanValue());
// if the value exists in the map then inverse it's value
}
adapter.notifyDataSetChanged(); // notify the adapter that something has
// changed
}
}
I have a list of activities on clicking one of which i will get a ListView.
On this ListView here, I can apply the preferences using a menu button. But this preferences is not applied right away to the ListView.
I have to go back and navigate my way through the parent list of activities and when i click then only I get the preferences that I want is applied.
cant we get this preferences/settings applied right away after we apply it?
But the view I am applying is through a cursoradapter. When I put this cursor inside OnSharedPreferenceChangeListener it gives me some constructor error "The constructor MyCountriesActivity.MySimpleCursorAdapter(new SharedPreferences.OnSharedPreferenceChangeListener(){}, int, Cursor, String[], int[]) is undefined". I tried to adjust the constructor in MySimpleCursorAdapter accordingly but I am not being able to do that. What is the solution?
OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// Implementation
int sort = prefs.getInt("sort_pref", -1); // -1 will be the result if no preference was set before
if(sort == 1)
sortOrder = "year";//sort on year
else if (sort == 2)
sortOrder = "country";//sort on country
ContentResolver contentResolver = getContentResolver();
Cursor c = contentResolver.query(CONTENT_URI, null, null, null, sortOrder);
String[] from = new String[] { "year", "country" };
int[] to = new int[] { R.id.year, R.id.country };
SimpleCursorAdapter sca = new MySimpleCursorAdapter(this, R.layout.country_row,
c, from, to);
setListAdapter(sca);
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
}
class MySimpleCursorAdapter extends SimpleCursorAdapter{
public MySimpleCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
// TODO Auto-generated constructor stub
}
#Override // Called when updating the ListView
public View getView(int position, View convertView, ViewGroup parent) {
/* Reuse super handling ==> A TextView from R.layout.list_item */
View v = super.getView(position,convertView,parent);
TextView tYear = (TextView) v.findViewById(R.id.year);
TextView tCountry = (TextView) v.findViewById(R.id.country);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
boolean font_size = prefs.getBoolean("fontSize", false);
boolean italic_font = prefs.getBoolean("fontItalic", false);
String listpref = prefs.getString("bgColor", "#ffffff80");
//System.out.println(listpref);
tYear.setBackgroundColor(Color.parseColor(listpref));
tCountry.setBackgroundColor(Color.parseColor(listpref));
if (font_size){
tYear.setTextSize(25);
tCountry.setTextSize(25);
}
if (italic_font){
tYear.setTypeface(null, Typeface.ITALIC);
tCountry.setTypeface(null, Typeface.ITALIC);
}
return v;
}
}
You can add OnSharedPreferenceChangeListener to your Activity:
public class MyList extends ListActivity implements OnSharedPreferenceChangeListener {
and define the onSharedPreferenceChange() method to manually alter settings immediately. You'll have to use SharedPreferences.registerOnSharedPreferenceChangeListener(this) as well.
OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
int sort = prefs.getInt("sort_pref", -1); // -1 will be the result if no preference was set before
if(sort == 1)
sortOrder = "year";//sort on year
else if (sort == 2)
sortOrder = "country";//sort on country
ContentResolver contentResolver = getContentResolver();
Cursor c = contentResolver.query(CONTENT_URI, null, null, null, sortOrder);
String[] from = new String[] { "year", "country" };
int[] to = new int[] { R.id.year, R.id.country };
SimpleCursorAdapter sca = new MySimpleCursorAdapter(getBaseContext(), R.layout.country_row,
c, from, to);
setListAdapter(sca);
//System.out.println("sjfs");
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
i have been having this issue for some time now, and have not gotten an answer for it yet. i have this custom Cursor adapter which i use to populate a list view from an sqlite database. Now my issue is that i want to populate the listview based on certain conditions.An example is if the condition is important, the listview should display only data that fits into that criteria and so on. I already have working methods that query the database accordingly.
now my problem is that, i can't seem to populate the listviews based on those methods and conditions without:
1) creating a copy of the exact same custom cursor adapter and just changing the names variables.
2) creating a copy of the exact xml layout and changing the id's.
As i say, its working this way, but i feel am having unnecessary classes and xml layout since its exactly the same thing. I know am doing something wrong, i just don't know what. Please any help and explanation would be appreciated. here is the necessary part of the code Code for the CustomCursorAdapter:
public class ViewItems extends ListActivity implements OnItemClickListener{
DBAdapter adapter;
Cursor cursor;
ListView list;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_list);
adapter = new DBAdapter(this);
adapter.open();
fillData();
list = (ListView)findViewById(android.R.id.list); // default android listView id
list.setOnItemClickListener(this);
}
// Different method calls
protected void fillImportantData() {
Cursor cursor = adapter.retrieveImportant();
startManagingCursor(cursor);
String[] from = new String[]{DBAdapter.NAME, DBAdapter.DATE, DBAdapter.TIME, DBAdapter.PRIORITY};
int[] to = new int[]{R.id.viewNameId, R.id.viewDateId, R.id.viewTimeId};
customCursorAdapter items = new customCursorAdapter(this, R.layout.view_items, cursor, from, to);
setListAdapter(items);
}
public class customCursorAdapter extends SimpleCursorAdapter {
private int layout;
Context context;
public customCursorAdapter(Context context, int layout, Cursor cursor, String[]from, int[] to) {
super(context, layout, cursor, from, to);
this.layout = layout;
this.context = context;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder;
if(view != null){
holder = new ViewHolder();
holder.viewName = (TextView)view.findViewById(R.id.viewNameId);
holder.viewStartDate = (TextView)view.findViewById(R.id.viewDateId);
holder.viewStartTime = (TextView)view.findViewById(R.id.viewTimeId);
view.setTag(holder);
}else{
holder = (ViewHolder)view.getTag();
}
int namecol = cursor.getColumnIndex(DBAdapter.NAME);
String name = cursor.getString(namecol);
if(holder.viewName != null){
holder.viewName.setText(name);
holder.viewName.setTextColor(Color.RED);
}
String startDate = cursor.getString(cursor.getColumnIndex(DBAdapter.DATE));
holder.viewStartDate.setText(startDate);
String startTime = cursor.getString(cursor.getColumnIndex(DBAdapter.TIME));
holder.viewStartTime.setText(startTime);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(layout, parent, false);
return view;
}
#Override
public long getItemId(int id){
return id;
}
#Override
public Object getItem(int position){
return position;
}
}
static class ViewHolder{
TextView viewName;
TextView viewStartDate;
TextView viewStartTime;
}
}
// methods in database
public Cursor retrieveAll(){
String[] resultColumns = new String[] {KEY_ID, NAME DATE, TIME, PRIORITY};
Cursor cursor = db.query(DATABASE_TABLE, resultColumns, null, , null, null, null);
return cursor;
}
public Cursor retrieveImportant(){
String[] resultColumns = new String[] {KEY_ID, NAME DATE, TIME, PRIORITY};
String[] condition = {"important"};
Cursor cursor = db.query(DATABASE_TABLE, resultColumns, PRIORITY + "=" + "?", condition, null, null, null);
return cursor;
}
If you change the data you wish to display, you will need to run a fresh query on the database and get a Cursor back that reflects that changed data. Depending on the nature of the changes, this may require a fresh CursorAdapter or merely a call to changeCursor(). If the new query returns the same columns and you want them displayed the same way, changeCursor() is probably sufficient. Otherwise, you will need to create a new CursorAdapter and call setAdapter() on your ListView to switch over to it.
You only need a different row layout if you are truly changing the row layout. You do not need to change IDs just for grins. Since you are not doing this in the code you have shown above, I am unclear what specifically you are worried about.