Limit number of items in SimpleCursorAdapter when using custom content provider - android

I'm trying to get used to using custom content providers. I've successfully managed to write a very simple application that on the press of a button adds a String to an sqlite database using a custom content provider. These database entries are then displayed in a ListView in the same activity.
I am trying to limit the number of items in the ListView to 5, but due to my lack in experience, I have no idea how to proceed.
This is what I'm using to fill the ListView
private void fillData() {
String[] from = new String[]{commentsTable.COLUMN_COMMENT, commentsTable.COLUMN_ID};
int[] to = new int[]{android.R.id.text1, android.R.id.text2};
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, android.R.layout.two_line_list_item, null, from, to, 0);
setListAdapter(adapter);
}
I followed this tutorial http://www.vogella.com/tutorials/AndroidSQLite/article.html#tutorial-sqlite-custom-contentprovider-and-loader to write an app similar to the app in the tutorial.
I have tried using a Cursor instead of null but I'm getting a java.lang.NullPointerException error because of the method .getWritableDatabase().

After some research, I have managed to solve this and thought I would post an answer for anyone who stumbles across this question.
In my customContentProvider class I added the variables:
public static final String QUERY_PARAMETER_LIMIT = "limit";
public static final String QUERY_PARAMETER_OFFSET = "offset";
and then edited the query method to include:
public Cursor query(Uri uri, ...) {
String limit = uri.getQueryParameter(QUERY_PARAMETER_LIMIT);
String offset = uri.getQueryParameter(QUERY_PARAMETER_OFFSET);
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// ...
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder, limitString);
//...
return c;
}
Then when getting a cursor simply use the Uri
Uri CONTENT_URI = customContentProvider.CONTENT_URI.buildUpon()
.appendQueryParameter(customContentProvider.QUERY_PARAMETER_LIMIT,
String.valueOf(limit))
.appendQueryParameter(customContentProvider.QUERY_PARAMETER_OFFSET,
String.valueOf(offset))
.build();
Reference:
https://stackoverflow.com/a/24055457/6735035

CursorAdapter has method getCount(). You could implement it in your class like this.
public int getCount() {
int superCount = super.getCount();
return Math.min(superCount, 5);
}
So you need to inherit SimpleCursorAdapter and redefine method getCursor().
Crate this class
public class CustomCursorAdapter extends SimpleCursorAdapter {
public CustomCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
#Override
public int getCount() {
int superCount = super.getCount();
return Math.min(superCount, 5);
}
}
And in your code change SimpleCursorAdapter to CustomCursorAdapter
private void fillData() {
String[] from = new String[]{commentsTable.COLUMN_COMMENT, commentsTable.COLUMN_ID};
int[] to = new int[]{android.R.id.text1, android.R.id.text2};
getLoaderManager().initLoader(0, null, this);
adapter = new CustomCursorAdapter(this, android.R.layout.two_line_list_item, null, from, to, 0);
setListAdapter(adapter);
}

Related

How to display more than one column data from the same table on ListView?

My ListView currently is able to display only one column data. I want it to display two column data.
Here is the code for my ListView:
public class ProjectExplorer extends ListActivity {
private projectdatabase database;
protected Cursor cursor;
protected ListAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
database = new projectdatabase(ProjectExplorer.this);
getinfo();
}
private void getinfo() {
database.open();
cursor = database.getDataforDisplay();
adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cursor, new String[] {"project_name"}, new int[] { android.R.id.text1 }, 0);
setListAdapter(adapter);
}
#Override
public void onListItemClick(ListView parent, View view, int position, long id) {
super.onListItemClick(parent, view, position, id);
Cursor c = ((SimpleCursorAdapter)parent.getAdapter()).getCursor();
c.moveToPosition(position);
// get project name here
String str_projectname= c.getString(c.getColumnIndex("project_name"));
Toast.makeText(this, str_projectname, Toast.LENGTH_LONG).show();
}
Here is the method in database class which return the cursor:
public Cursor getDataforDisplay () {
String[] columns = new String[] {KEY_ROWID, PROJECT_NAME, PROJECT_FINISH_DATE, PROJECT_DIFFICULTY, PROJECT_STATUS};
Cursor c = projectDatabase.query(DATABASE_TABLE, columns, null, null, null, null, null);
projectDatabase.query(DATABASE_TABLE, columns, null, null, null, null, null);
c.moveToFirst();
return c;
}
I also want to display KEY_ROWID which is defined as _id
You can use android.R.layout.simple_list_item_2 specifying column no.2 from table from where data can be retrieved and specifies location with help of it can be displayed on Activity - android.R.id.text2
adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, cursor, new String[] {"project_name","column_no2"}, new int[] { android.R.id.text1, andriod.R.id.text2 }, 0);
You can also create your on custom Adapter specifying your own Listview and creating it from scratch and customized everything.
The best way to achieve this at least in my opinion is to create custom Adapter for your ListView in that case you can set your own design how single listview element should look like . Just populate some arrays using your Cursor and set them to your custom adapter.

Skip null values in ListActivity with SQLite data

I can show the id, title and borrowed values in a TextView. My problem is that I would like to check the "borrowed" values, and if they are not null, display a simple image. If the value is null, I don't want to display anything.
Might work with a ViewBinder, but I don't know how to solve this.
private static String[] FROM = { _ID, TITLE, BORROWED };
private static String[] TO = { R.id.id, R.id.title, R.id.borrowed };
private Cursor getMovies() {
SQLiteDatabase db = movies.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, FROM, null, null, null, null, ORDER_BY);
startManagingCursor(cursor);
return cursor;
}
private void showTitles(Cursor cursor) {
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item, cursor, FROM, TO);
setListAdapter(adapter);
}
Extend your SimpleCursorAdapter and check for null of the BORROWED val in your overrided bindView() method. Something like this:
private class mySimpleCursorAdapter extends SimpleCursorAdapter{
#Override
public void bindView(View view, Context context, Cursor cursor) {
if ( cursor.isNull( cursor.getColumnIndex(BORROWED) ) ){
// Handle NULL scenario
} else {
// Handle not NULL scenario
}
}
}
This is not tested and there's other code you'll need to implement, but this is the idea.
You can apply where clause in db.query for non null values so that your cursor only has not null rows..

onListItemClick, what statement do I need to get the value?

i can add to a db and list as a listview.
When I click a list item using onListItemClick, what statement do I
need to get the value?
Thanks in advance.
public class Main extends ListActivity {
private static String[] FROM = { _ID, DESCRIPTION, UN };
private static String ORDER_BY = DESCRIPTION + " ASC";
private static int[] TO = {R.id.itemrowid, R.id.itemdescription, R.id.itemun };
private EventsData events;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
events = new EventsData(this);
try {
Cursor cursor = getEvents();
showEvents(cursor);
} finally {
events.close();
}
**public void onListItemClick(ListView parent, View v, int position, long id) {
// What statement to put here to get the value of _ID,DESCRIPTION, UN
// selected**?
}
private Cursor getEvents() {
// Perform a managed query. The Activity will handle closing
// and re-querying the cursor when needed.
SQLiteDatabase db = events.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, FROM, null, null, null,
null, ORDER_BY);
startManagingCursor(cursor);
return cursor;
}
private void showEvents(Cursor cursor) {
// Set up data binding
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.item, cursor, FROM, TO);
setListAdapter(adapter);
}
First, you need to obtain item at clicked position:
Cursor cursor = getListAdapter().getItem(position);
Then you can get data from this cursor (I don't know what types do you use, so just for example it will be int and String):
int id = cursor.getInt(cursor.getColumnIndex(_ID));
String description = cursor.getString(cursor.getColumnIndex(DESCRIPTION));
See documentation for Cursor's available methods.
Adding to sergey answer you can also directly see what View was clicked and use findViewById() to find the components, therefore, the values you want.
This is just an alternative being the method Sergey much less 'patchy'

Android ListView (not ListActivity) connecting to SQLite DB using CustomCursorAdapter

I am trying to create Custom Cursor adapter and attach it to my listview.
Though I am not yet there to create textboxes and set values inside my adapter,
my code currently throws a runtime exception when I try to call super(ctx, c);
What could be wrong? Searched all over the web & couldn't get it. Thanks in advance!
Custom Cursor adapter:
public class CustomCursorAdaptor extends CursorAdapter {
private Context context;
private int layout;
public CustomCursorAdaptor (Context ctx, int layout, Cursor c, String[] from, int[] to) {
super(ctx, c);
this.context = ctx;
this.layout = layout;
}
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return(new View(context));
}
#Override
public void bindView(View v, Context context, Cursor c) {
}
}
and my Activity:
public class DynamicScrollView extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context ctx=getBaseContext();
RelativeLayout newLayout=new RelativeLayout(ctx);
ListView lv=new ListView(ctx);
SQLiteDatabase db;
db = ctx.openOrCreateDatabase("TShow.db", SQLiteDatabase.OPEN_READONLY, null);
db.setVersion(1);
db.setLocale(Locale.getDefault());
db.setLockingEnabled(true);
Cursor cur = db.query("control", new String[] {"id"}, "parent_id=2", null, null, null, null);
CustomCursorAdaptor adapter = new CustomCursorAdaptor(ctx, lv.getId(), cur, new String[] {"id"}, new int[] {2});
lv.setAdapter(adapter);
newLayout.addView(lv);
setContentView(newLayout);
}
}
I really don't know much about the base context, which you are passing to your CursorAdaptor here, but I understand that usually you should rather be using your Activity as the context. Activity is a subclass of Context, in case you did not know.
So, in stead of:
new CustomCursorAdaptor(ctx, lv.getId(), cur, new String[] {"id"}, new int[] {2});
... you could try:
new CustomCursorAdaptor(this, lv.getId(), cur, new String[] {"id"}, new int[] {2});
Cursor adapter expects to have a column _id which it uses as index. I added another column as _id in the select and it worked! Updated code:
Cursor cur = db.query("control", new String[] {"id as _id", "id"}, "parent_id=2", null, null, null, null);
I traced it back from the exception description which said: column '_id' does not exist
Thank you for the golden tip about the _id column which is needed for the ListView to work.
#dmg: you must not name your _id column with the (native)SQL 'AS' statement. Just add the '_id' column without changing it.

Strange behaviour with custom cursor adapter android

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.

Categories

Resources