Android: How to set List item checked? - android

I have an android.R.layout.simple_list_item_multiple_choice with checkboxes an want so initiate some of them.
How can I do that?
I have the following code:
private void fillList() {
Cursor NotesCursor = mDbHelper.fetchAllNotes();
startManagingCursor(NotesCursor);
String[] from = new String[] { NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY, NotesDbAdapter.KEY_CHECKED };
int[] to = new int[] {
android.R.id.text1,
android.R.id.text2,
//How set checked or not checked?
};
SimpleCursorAdapter notes = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_multiple_choice, NotesCursor,
from, to);
setListAdapter(notes);
}

Put the resource id of your checkbox in your row layout into the to array, corresponding to the NotesDbAdapter.KEY_CHECKED cursor in from array.
Implement a SimpleCursorAdapter.ViewBinder.
Have the ViewBinder.setViewValue() method check for when its called for the NotesDbAdapter.KEY_CHECKED column.
When it is not the KEY_CHECKED column, have it return false the the adapter will do what it normally does.
When it is the KEY_CHECKED column, have it set the CheckBox view (cast required) to checked or not as you wish and then return trueso that adapter won't attempt to bind it itself. The cursor and corresponding column id is available to access query data to determine whether to check the checkbox or not.
Set your ViewBinder in your SimpleCursorAdapter via setViewBinder()
Here's one of my ViewBinder implementations. Its not for checboxes, rather its for doing some fancy formatting of a text view, but it should give you some idea for the approach:
private final SimpleCursorAdapter.ViewBinder mViewBinder =
new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(
final View view,
final Cursor cursor,
final int columnIndex) {
final int latitudeColumnIndex =
cursor.getColumnIndexOrThrow(
LocationDbAdapter.KEY_LATITUDE);
final int addressStreet1ColumnIndex =
cursor.getColumnIndexOrThrow(
LocationDbAdapter.KEY_ADDRESS_STREET1);
if (columnIndex == latitudeColumnIndex) {
final String text = formatCoordinates(cursor);
((TextView) view).setText(text);
return true;
} else if (columnIndex == addressStreet1ColumnIndex) {
final String text = formatAddress(cursor);
((TextView) view).setText(text);
return true;
}
return false;
}
};

Related

How to update cursor at setViewValue?

Is there any way to manually update cursor's item value from the setViewValue code? Currently I just fetch all records again and then changeCursor and notifyDataSetChanged, but it is very slow:
dbHelper = new DbAdapter(this);
dbHelper.open();
recordsCursor = dbHelper.fetchAllRecords();
startManagingCursor(recordsCursor);
String[] from = new String[]{DbAdapter.KEY_1, DbAdapter.KEY_2};
int[] to = new int[]{R.id.text1, R.id.background};
adapter = new SimpleCursorAdapter(this, R.layout.row, recordsCursor, from, to);
if (adapter != null) {
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
int viewId = view.getId();
switch(viewId) {
case R.id.text1:
final Object item = adapter.getItem(cursor.getPosition());
...
// some logic is here, so the code below is run only when item is displayed for 5 seconds
dbHelper.open();
Boolean result = dbHelper.markRecordAsRead(text); // update the database
...
// the code below works, but since I read the whole database, it is SLOW
dbHelper.open();
recordsCursor = dbHelper.fetchAllRecords();
adapter.changeCursor(recordsCursor);
adapter.notifyDataSetChanged();
// is there any way just to update the cursor's item value?

Invisible text color instead of white in a listview displaying entries from database

Im trying to change text color in a listview, which is displaying my entries from database. The problem is that it displays invisible text ( or it displays nothing at all. only the lines which are separating my entries on this list) but after clicking on entry it shows me details from this entries. Displaying works good without trying to use ViewBinder.
// map each name to a TextView
String[] from = new String[] { "event" };
int[] to = new int[] { R.id.countryTextView };
conAdapter = new SimpleCursorAdapter(Clock.this, R.layout.day_plan, null, from, to);
SimpleCursorAdapter.ViewBinder binder = new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex){
int getIndex = cursor.getColumnIndex("colour");
String empname = cursor.getString(getIndex);
tv = (TextView)findViewById(R.id.countryTextView);
tv.setTextColor(Color.WHITE);
if(empname.equals("Green"))
{
tv.setTextColor(Color.WHITE);
return true;
}
return false;
}
};
setListAdapter(conAdapter); // set adapter
((SimpleCursorAdapter) conAdapter).setViewBinder(binder);
How can I change it to work ok ?
You have to add the text to the textview:
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex)
{
((TextView) view).setTextColor(Colors.RED);
((TextView) view).setText(cursor.getString(cursor.getColumnIndex("YOUR-COLUMN-NAME")));
return false;
}
change line
tv = (TextView)view.findViewById(R.id.countryTextView); // <-- add 'view.'
call
conAdapter.setViewBinder(SimpleCursorAdapter.ViewBinder)
setListAdapter(conAdapter); // set adapter

How to bind CheckBox in multipleChoice ListView to boolean value with SimpleCursorAdapter?

I have a ListView with android:choiceMode="multipleChoice". I fill this ListView from a Cursor through a SimpleCursorAdapter. Is there really no way to directly bind the "CheckBox" of the ListView's CheckedTextView layout to a boolean value from the cursor?
Currently I loop through the cursor calling ListView.setItemChecked() if the value is true:
private void showMyData(long myId) {
// fill the list
String[] fromColumns = { "myTextColumn" };
int[] toViews = { android.R.id.text1 };
Cursor myCursor = _myData.readData(myId);
CursorAdapter myAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_multiple_choice,
myCursor, fromColumns, toViews);
ListView myListView = (ListView) findViewById(R.id.myListView);
myListView.setAdapter(myAdapter);
// mark items that include the object specified by myId
int myBooleanColumnPosition = myCursor
.getColumnIndex("myBooleanColumn");
for (int i = 0; i < myCursor.getCount(); i++) {
myCursor.moveToPosition(i);
if (myCursor.getInt(myBooleanColumnPosition ) == 1) {
myListView.setItemChecked(i, true);
}
}
}
That does the job. But I would like to have code like this:
String[] fromColumns = { "myTextColumn", "myBooleanColumn" };
int[] toViews = { android.R.id.text1, android.R.id.Xyz };
and have no loop. Am I missing something here or is it Android?
EDIT:
I tried this as suggested by Luksprog:
public boolean setViewValue(View view, Cursor cursor,
int columnIndex) {
CheckedTextView ctv = (CheckedTextView) view;
ctv.setText(cursor.getString(cursor
.getColumnIndex("myTextColumn")));
if (cursor.getInt(cursor.getColumnIndex("myBooleanColumn")) == 1) {
ctv.setChecked(true);
Log.d("MY_TAG", "CheckBox checked");
}
return true;
}
That logged checking the CheckBox but didn't actually do it. Maybe that's a bug on my side. And while it's even more complicated than the initial loop at least it feels like one is using the framework, not working against it. So thank you Luksprog for the answer.
But to sum it up: Android is actually missing the straight forward approach.
Use a SimpleCursorAdapter.ViewBinder on your adapter. Make sure your Cursor has the boolean values column in it and then:
String[] fromColumns = { "myTextColumn" };
int[] toViews = { android.R.id.text1 };
Cursor myCursor = _myData.readData(myId);
CursorAdapter myAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_multiple_choice, myCursor, fromColumns, toViews);
myAdapter.setViewBinder(new ViewBinder() {
public boolean setViewValue (View view, Cursor cursor, int columnIndex) {
// set the text of the list row, the view parameter (simple use cursor.getString(columnIndex))
// set the CheckBox status(the layout used in the adapter is a CheckedTextView)
return true;
}
});

CheckedTextViews will randomly appear checked in a list if I click one further up the list

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
}
}

Changing values from Cursor using SimpleCursorAdapter

I have database table with the columns {Name, Time (UTC format) , Latitude, Longitude}
I display the table using a ListActivity with a SimpleCursorAdapter.
I would like that the column Time show the time in a human readable format (13-07-2010 10:40) rather than in UTC format (18190109089).
How can I specify that the values from column Time need some filtering/adaptation?
POSSIBLE SOLUTION (with a problem):
SimpleCursorAdapter offers the method:
setCursorToStringConverter(SimpleCursorAdapter.CursorToStringConverter cursorToStringConverter);
to specify how a class that is able to convert a Cursor to CharSequence (convertToString(Cursor cursor).
Anyway I don't know in which format should be the return CharSequence paramater!
The simplest way to format a cursor value is to use SimpleCursorAdapter.setViewBinder(..):
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.list, cursor,
new String[] { Definition.Item.TITLE, Definition.Item.CREATE_DATE }, new int[] { R.id.title, R.id.createDate});
adapter.setViewBinder(new ViewBinder() {
public boolean setViewValue(View aView, Cursor aCursor, int aColumnIndex) {
if (aColumnIndex == 2) {
String createDate = aCursor.getString(aColumnIndex);
TextView textView = (TextView) aView;
textView.setText("Create date: " + MyFormatterHelper.formatDate(getApplicationContext(), createDate));
return true;
}
return false;
}
});
i also had the same problem after long struggle finally i found answer :) ( see below )
use setViewText (TextView v, String text)
for example
SimpleCursorAdapter shows = new SimpleCursorAdapter(this, R.layout.somelayout, accountCursor, from, to)
{
#Override
public void setViewText(TextView v, String text) {
super.setViewText(v, convText(v, text));
}
};
private String convText(TextView v, String text)
{
switch (v.getId())
{
case R.id.date:
String formatedText = text;
//do format
return formatedText;
}
return text;
}
You can use setViewBinder(), or subclass SimpleCursorAdapter and override bindView().
You can use SQLite syntax on that column to format the date.
Something like this will do it
SELECT strftime('%d-%m-%Y %H:%M',1092941466,'unixepoch');
SELECT strftime('%d-%m-%Y %H:%M',timecol,'unixepoch');
Going thru this old post, noticed I have done something similar that might help:
public class FormatCursorAdapter extends SimpleCursorAdapter {
protected int[] mFormats;
public static final int FORMAT_TEXT=0;
public static final int FORMAT_CURRENCY=1;
public static final int FORMAT_DATE=2;
public FormatCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int[] formats, int flags) {
super(context, layout, c, from, to, flags);
mFormats = formats;
ViewBinder viewBinder = new ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
int formatType = mFormats[columnIndex-1];
switch (formatType) {
case FORMAT_CURRENCY:
NumberFormat nf = NumberFormat.getCurrencyInstance();
nf.setMaximumFractionDigits(2);
((TextView)view).setText(nf.format(cursor.getDouble(columnIndex)));
return true;
case FORMAT_DATE:
DateFormat df = SimpleDateFormat.getDateTimeInstance();
((TextView)view).setText(df.format(new Date(cursor.getLong(columnIndex))));
return true;
}
return false;
}
};
setViewBinder(viewBinder);
}
}
Usage:
// For the cursor adapter, specify which columns go into which views with which format
String[] fromColumns = {
Table.COLUMN_TITLE,
Table.COLUMN_AMOUNT,
Table.COLUMN_DATE};
int[] toViews = {
R.id.tvTitle,
R.id.tvAmount,
R.id.tvDate};
int[] formatViews = {
FormatCursorAdapter.FORMAT_TEXT,
FormatCursorAdapter.FORMAT_CURRENCY,
FormatCursorAdapter.FORMAT_DATE};
mAdapter=new FormatCursorAdapter(getContext(),R.layout.item_operation,cursor,
fromOpsColumns,toOpsViews,formatViews,0);
mListView.setAdapter(mOpsAdapter);
Hope this helps anyone out there !

Categories

Resources