I am reading data from an SQLite database using a cursor and using an adapter to display it in a listView. This works fine but I now want to reduce the amount of data that I show in the listView. At the moment it displays the following:
John Smith, 25, Moday, Consultation, Dr. Harley
Jane Doe, 41, Wednesday, Surgery, Dr. Pope
What I want it to display is:
John Smith, 25, Mo, Con, Harley
Jane Doe, 41, We, Sur, Pope
Basically I want to parse 3 of the strings. The problem is the cursor adapter takes the columns of the database as a string array in its constructor so I don't know where to perform the parsing operation on it. I've tried a number of different options and am getting unrecognised column id errors and other runtime errors. Can anyone point me in the right direction?
The method where the adapter is created:
private void fillList() {
Cursor c = db.getApts();
startManagingCursor(c);
String[] from = new String[] {ModuleDB.KEY_AptCode,
ModuleDB.KEY_AptName,
ModuleDB.KEY_AptAge,
ModuleDB.KEY_AptDay,
ModuleDB.KEY_AptType,
ModuleDB.KEY_AptDoc};
int[] to = new int[] {R.id.Aptcode_entry,
R.id.AptName_entry,
R.id.AptAge_entry,
R.id.Aptday_entry,
R.id.Apttype_entry,
R.id.Aptdoc_entry};
SimpleCursorAdapter aptAdapter =
new SimpleCursorAdapter(this, R.layout.apt_entry, c, from, to);
setListAdapter(aptAdapter);
}
1.) Let your activity implement - ViewBinder
2.) Match your column and use substring
public class YourActivity extends Activity
implements SimpleCursorAdapter.ViewBinder {
adapter.setViewBinder(this); //Put this line after your list creation and setlistAdapter
#Override
public boolean setViewValue(View view, Cursor cursor, int column) {
//Showing for day, similarly for others
int dayColumn = cursor.getColumnIndex(your day column name in quotes);
if (column == dayColumn ) {
String dayString = cursor.getString(dayColumn );
((TextView)view).setText(bodyString.subString(0, 3));
return true; // Return true to show you've handled this column
}
return false;
}
}
Also - #Simon is Right - Using a Custom Adapter that extends a Cursor Adapter is always better because you get a lot more freedom to modify it later if your requirements evolve further. Off the top of my head here is an example of how you can use custom adapter and build a nice list- http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/
Not sure what adapter you use, but assuming all the data is shown as single row then I'd extend that one and override toString() method of it.
Don't think you can override toString() on a simple adapter but perhaps this will help?
SimpleCursorAdapter aptAdapter= new SimpleCursorAdapter(this, R.layout.apt_entry, c, from, to);
CursorToStringConverter stringConverter = new CursorToStringConverter() {
#Override
public CharSequence convertToString(Cursor cursor) {
return "Hello listview"; // whatever string you want to build using cursor.getString() etc
}
};
aptAdapter.setCursorToStringConverter(stringConverter);
[EDIT] Just checked the docs and SimpleCursorAdapter does not have a toString() method, nor do any of it's super classes.
Related
I'm using SimpleCursorAdaptor and a ListView to display the values in my SQLite database rows. One of the values in my row is a date (column 'date'). Rather than display this date I need to run this through a method that will return another string based on what the date is. This is the value I want displayed in my list rather than the actual value taken straight from the Database.
In short I wish to display all values from my database table row except for one, where I need to change it before displaying it.
Here is my code:
public class BinCollectionDayListActivity extends ListActivity{
//
private static final String fields[] = { "name", "date", BaseColumns._ID };
//
public void onCreate(Bundle savedInstanceState) {
//
super.onCreate(savedInstanceState);
//
DatabaseHelper helper = new DatabaseHelper(this);
SQLiteDatabase database = helper.getWritableDatabase();
Cursor data = database.query("names", fields, null, null, null, null, null);
CursorAdapter dataSource = new SimpleCursorAdapter(this, R.layout.binrow, data, fields, new int[] { R.id.name, R.id.date });
dataSource.getCursor().requery();
//
ListView view = getListView();
view.setHeaderDividersEnabled(true);
setListAdapter(dataSource);
//
helper.close();
database.close();
}
}
As you can tell I am pretty new to Android development and would love to know what the best approach would be to achieving the desired result.
Thanks in advance,
Tony
Two options that I've used before:
Array Adapter (Preferred):
Create an ArrayAdapter and populate the Cursor data into your ArrayAdapter.
http://anujarosha.wordpress.com/2011/11/17/how-to-create-a-listview-using-arrayadapter-in-android/
ViewBinder: On your Cursor, you can setup/specify a ViewBinder where you can check the data that's about to be mapped, perform some logic on it, and spit out a different result if desired. This is probably exactly what you're looking for, but do consider the ArrayAdapter as it tends to give you better control and it's a pain to switch these things later on.
Changing values from Cursor using SimpleCursorAdapter
I have a ListView and I can reorder items, but every time I exit from the app and start it again the order goes back to default. I'd appreciate if someone can give me answer and maybe a little snippet of how I can do that.
Example: I have two items and two links. Google and Yahoo. I use drag and drop and ArrayList with that. Everything is fine except I don't know how to save the new order.
I don't know if there is some other way to reorder those items.
I use CWAC-TOUCHLIST to do this.
Here is how far I have got:
private static String[] items={"Google", "Yahoo"};
private static String[] links={"http://google.com", "http://yahoo.com"};
private IconicAdapter adapter=null;
private IconicAdapter1 adapter2=null;
ArrayList<String> array= new ArrayList<String>(Arrays.asList(items));
private ArrayList<String> array2=
new ArrayList<String>(Arrays.asList(links));
/** Called when the activity is first created. **/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TouchListView tlv=(TouchListView)getListView();
adapter=new IconicAdapter();
adapter2=new IconicAdapter1();
setListAdapter(adapter);
setListAdapter(adapter2);
tlv.setOnItemClickListener(itemclick);
tlv.setDropListener(onDrop);
}
private TouchListView.DropListener onDrop =
new TouchListView.DropListener() {
#Override
public void drop(int from, int to) {
String item=(String) adapter.getItem(from);
adapter.remove(item);
adapter.insert(item, to);
String link=(String) adapter2.getItem(from);
adapter2.remove(link);
adapter2.insert(link, to);
}
};
private TouchListView.OnItemClickListener itemclick =
new TouchListView.OnItemClickListener(){
public void onItemClick(AdapterView<?> parent, View view,
int from, long id) {
String item = adapter.getItem(from);
Toast.makeText(getBaseContext(), "you pick: "+item,
Toast.LENGTH_SHORT).show();
String link = adapter2.getItem(from);
Intent showContent = new Intent(getApplicationContext(),
Web.class);
showContent.setData(Uri.parse(link));
startActivity(showContent);
}
};
Is there a smarter way to do it?
You have to save the lists with their order either to a file or a DB.
When the application starts you have to initialize the ListViews from the saved lists.
When you drag & drop an item, you have to save the changes.
The method you need to change :
Your drop method should, in addition to what it already does, store the changes in a file or a DB.
Instead of the arrays that you pass in the IconicAdapter and IconicAdapter2 constructors (array & array2), you should pass the saved arrays.
You can read in many places how to store data to a file or a SQLite DB. For example, you can find a good tutorial for SQLite here - http://www.vogella.com/articles/AndroidSQLite/article.html#android_requisites (only the first four sections are relevant, since you don't have to use a ContentProvider).
If you choose to use a DB, you can create a table that contains the following columns : item, link & order. In the drop method you'll have to update the order column of all the rows whose order is between from and to (or if the lists are not too big, you can simply update all the rows and set the order to be the index in the array that holds the list).
When you load the table from the DB you should order it by the order column.
i'm making select statement and it works fine
the problem is selecting more than thousands of records ate one and this cause the program too slow.
is there a possibility to select fifty by fifty and when select the first fifty record show them then add the next fifty record to them.
how can i do that .
thanks in advance ...
Use LIMIT/OFFSET Clauses is selection statement
I haven't worked on that but can give some idea reagarding that. You can use AsynTask here. In the doingInbackground() you can get the records and then you can call publishProgress() when 50 records are fetched and update the UI.
UPDATE:
You can use the LIMIT/OFFSET clause that Kiran said to get the limit of the record fetched and can update the UI using AsyncTask.
Here is the code you need to back your AutoCompleteTextView with a cursor adapter.
#Override
protected void onResume() {
super.onResume();
text = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView1);
final AdapterHelper h = new AdapterHelper(this);
Cursor c = h.getAllResults();
startManagingCursor(c);
String[] from = new String[] { "val" };
int[] to = new int[] { android.R.id.text1 };
CursorAdapter adapter = new MyCursorAdapter(this,
android.R.layout.simple_dropdown_item_1line, c,
from, to);
adapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
if (constraint == null) {
return h.getAllResults();
}
String s = '%' + constraint.toString() + '%';
return h.getAllResults(s);
}
});
text.setAdapter(adapter);
}
class MyCursorAdapter extends SimpleCursorAdapter {
public MyCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
}
public CharSequence convertToString(Cursor cursor) {
return cursor.getString(cursor.getColumnIndex("val"));
}
}
The database that I am using has 3k rows of data in it and the Autocomplete works fine.
The things to note are that you need make sure that the your adapter puts the correct value in the text box once a user selects it. Do this with the convertToString method (at the end of the snippet above). You get to this method by extending SimpleCursorAdapter and overriding the method as shown.
Then you need to provide a FilterQueryProvider to your adapter. This allows your query to be run with the where clause of your typed text. If you have a huge dataset, then setting the threshold large enough (either programatically, or in xml) will prevent the filter query running until it will return a suitably sized resultset.
Hope this is useful.
Anthony Nolan
Here's the ListAdapter that I am trying to bind my ListView with.
ListAdapter adapter = new SimpleCursorAdapter(this, R.layout.my_item,c,new String[] {"title","body"},
new int[] { R.id.TextView_Title,R.id.TextView_Body});
setListAdapter(adapter);
This works. But I am stuck at what I guess should be very simple. What I want to do is to display the title completely while I display a sub string of the body (say, first 10/15 chars). How do I do this in this case? Can I manipulate the cursor directly in a way so that it returns a substring at the first place or do I have to do it after the cursor has returned the values (in that case, how?). Thanks for the help.
EDIT: OK, sorry I thought you wanted to 'get' the substring of the body column but you want to 'set' it as a substring???
Implement SimpleCursorAdapter.ViewBinder on your activity and override setViewValue().
At some point in your code you'll need to use...
adapter.setViewBinder(this); // Put this on onCreate() perhaps
...and the code would look similar to this...
public class MyActivity extends Activity
implements SimpleCursorAdapter.ViewBinder {
#Override
public boolean setViewValue(View view, Cursor cursor, int column) {
int bodyColumn = cursor.getColumnIndex("body");
if (column == bodyColumn) {
String bodyString = cursor.getString(bodyColumn);
((TextView)view).setText(bodyString.subString(0, 10));
return true; // Return true to show you've handled this column
}
return false;
}
}
Skip to the bottom if you just want to see the question without context
The android app I'm building has a simple table with three columns:
_id INTEGER PRIMARY KEY..., name TEXT, color INT
This table is called categories. I load my categories from the database and feed them into a SimpleCursorAdapter for use with a Spinner like so:
String[] from = new String[] {
ListDbAdapter.KEY_CATEGORY_NAME,
ListDbAdapter.KEY_CATEGORY_COLOR };
int[] to = new int[] { R.id.categorySpinnerItem };
mCategorySpinnerAdapter = new SimpleCursorAdapter(this,
R.layout.category_spinner_item, categoryCursor, from, to);
mCategorySpinnerAdapter
.setViewBinder(new CategorySpinnerViewBinder());
mCategorySpinner.setAdapter(mCategorySpinnerAdapter);
I set a custom ViewBinder because I want the category name to be the text of the spinner item, and the color to be the background color. My ViewBinder looks like this:
private static final int NAME_COLUMN = 1;
private static final int COLOR_COLUMN = 2;
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
TextView textView = (TextView) view;
String name = cursor.getString(NAME_COLUMN);
int color = cursor.getInt(COLOR_COLUMN);
textView.setText(name);
textView.setBackgroundColor(color);
return true;
}
Here is my question (finally)
In the setViewValue method, what is columnIndex supposed to be doing? The documentation says "the column at which the data can be found in the cursor" but when I debug through setViewValue I hit it three times and columnIndex is always 1.
I was expecting the debugger to get into setViewValue once for each entry in the from array, with a columnIndex first of 1 and then 2. Or maybe once for each column in the query results.
The above code works, I can get the desired functionality but only because of my NAME_COLUMN and COLOR_COLUMN constants. I'd be really interested to hear an explanation of setViewValue and its parameters from someone more experienced with custom ViewBinders.
In the source of SimpleCursorAdapter, the setViewValue is called in bindView :
bound = binder.setViewValue(v, cursor, from[i]);
where the third param from[i], which is the interesting one, is an iteration over an int[], which represents the column indexes used. However the index for [i] for the iteration itself comes from the int[] to which is passed to the constructor of the adapter, and in your case, it has only 1 item - R.id.categorySpinnerItem
EDIT: In two words, the String[] and the int[] should be equivalent, same size and in same order - for each column name you need an int R.id... first view id will be connected to the first column id with from[0], second with from[1] and so on, and if you pass 10 columns, but you have only 3 R.id-s, you will get only to from[2] :)
I think that you are confused because you missed the part with the return value - true if you bind the data, false otherwise (and the adapter attempts to handle the binding on its own). I think that the idea is like with the OnTouchEvent- giving you the option to consume it or not. So, you are always returning true at index 1, and you are never offered the index 2, because you have binded the view already, that's the only explanation I can think of about having always only 1 in the columnIndex param.
However, I almost haven't used cursor adapters - I find them not-OO, it is way better to create a POJO somewhere else, initializing it however you want using the db columns, and when you have a shaped list of objects just sending them to a "normal" adapter, it sounds more MVC. For example, if at some point you decide that KEY_CATEGORY_NAME will be in the format "cat_name##cat_description" (for example) you have to change the Adapter. Sounds more reasonable to change your class Category, so the getName() will return just "cat_name", and the adapter is the same.
So, because I almost haven't used CursorAdapters , if I am right about the columnIndex, please DO tell me about it, because I am curious but I don't want to create a CursorAdapter and check it myself :)