I am developing an extremely simple list app to save items to an SQLite DB, and populate a ListView with it's contents.
This can be done using a SimpleCursorAdapter, which I do like this:
mySQLiteAdapter.openToRead();
cursor = mySQLiteAdapter.queueAll();
String[] from = new String[]{mySQLiteAdapter.getKeyContent()};
int[] to = new int[]{R.id.normal};
cursorAdapter = new SimpleCursorAdapter(this, R.layout.row, cursor, from, to, 0);
listView.setAdapter(cursorAdapter);
mySQLiteAdapter.close();
Now however I would like to implement the ability to either strike out text on a row by click or long click, or change the row to a different layout (which could also strike the text)
At first I simply set the onClickListener for the listview and changed the paint flags to strike the text. This works fine until there are enough items in the list to scroll the view, or until the activity is reloaded. In the latter the strike is gone (since nothing was persistent), and in the former other rows are striked, and the intended ones are not. Then is changes as you scroll around. See Custom ListView adapter, strange ImageView behavior for a similar situation to my own.
From this I have found that I will need use a custom adapter to do what I want. So I have created a CustomCursorAdapter which extends SimpleCursorAdapter and overriden some methods to attempt to inflate a seperate layout with a background colour. I am not having much luck.
Here is what I have so far:
// Creating a new instance of the custom adapter and assigning it to the listview
mySQLiteAdapter.openToRead();
cursor = mySQLiteAdapter.queueAll();
String[] from = new String[]{mySQLiteAdapter.getKeyContent()};
int[] to = new int[]{R.id.normal};
cursorAdapter = new CustomCursorAdapter(this, R.layout.row, cursor, from, to, 0);
listView.setAdapter(cursorAdapter);
mySQLiteAdapter.close();
and
// CustomCursorAdapter class
private class MyCursorAdapter extends SimpleCursorAdapter {
private LayoutInflater inflater;
public MyCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get reference to the row
View view = super.getView(position, convertView, parent);
//View view;
if (getItemViewType(position) == 0) {
view = inflater.inflate(R.layout.row, null);
}
else {
view = inflater.inflate(R.layout.rowstrike, null);
}
return view;
}
@Override
public int getItemViewType(int position) {
int row;
if (cursor.getInt(cursor.getColumnIndex(mySQLiteAdapter.getKeyStrike())) == 1) {
Log.d("DEBUG", "Row " + position + " is STRIKED");
row = 1;
}
else {
Log.d("DEBUG", "Row " + position + " is normal");
row = 0;
}
return row;
}
}
The implementation of the CustomCursorAdapter works and shows the correct amount of rows. The logic to determine if the row should contain striked text by querying the db is correct, however the returned inflated views are completely blank. I think it may be to do with the way my XML files are arranged and which ones I pass to the adapter but all my testing and tinkering to try to get this working have failed spectacularly so far.
Here are my XML files for the layouts
// activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<EditText
android:layout_width="fill_parent"
android:layout_height="35dp"
android:id="@+id/editText"
android:hint="Press here to add an item"
android:maxLines="1"
android:imeOptions="actionDone"
android:inputType="textAutoCorrect"/>
<ListView
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="@+id/listView"
android:layout_below="@id/editText"/>
</RelativeLayout>
.
// row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp" >
<TextView
android:id="@+id/normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"/>
</RelativeLayout>
.
//rowstrike.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp" >
<TextView
android:id="@+id/striked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#F05"/>
</RelativeLayout>
I have been searching on this for days and days, each time getting closer but nothing seems to work, or the explanations are not beginner friendly for someone like myself.
The closest post I have found to what I am after is ListView view recycling with CustomAdapter
However I think I need more code snippets as I must be doing something wrong elsewhere in my app? There is a mention of overriding the getViewTypeCount method but I am unsure of how this is done...
There is also How would I use a different row layout in custom CursorAdapter based on Cursor data? However I am not quite sure where to go from this post...
EDIT: Solution based on Luksprogs post.
mySQLiteAdapter.openToRead();
cursor = mySQLiteAdapter.queueAll();
String[] from = new String[]{mySQLiteAdapter.getKeyContent()};
int[] to = new int[]{R.id.normal};
cursorAdapter = new SimpleCursorAdapter(this, R.layout.row, cursor, from, to, 0);
cursorAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (view.getId() == R.id.normal) {
TextView tv = (TextView)view.findViewById(R.id.normal);
if (cursor.getInt(cursor.getColumnIndex(mySQLiteAdapter.getKeyStrike())) == 1) {
tv.setPaintFlags(tv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
else {
tv.setPaintFlags(tv.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
}
}
return false;
}
});
listView.setAdapter(cursorAdapter);
mySQLiteAdapter.close();
and I have an OnItemClickListener as follows to strike and unstrike rows
private ListView.OnItemClickListener listViewOnItemClickListener
=new ListView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final int rowID = cursor.getInt(cursor.getColumnIndex(mySQLiteAdapter.getKeyID()));
mySQLiteAdapter.openToWrite();
if (cursor.getInt(cursor.getColumnIndex(mySQLiteAdapter.getKeyStrike())) == 0) {
mySQLiteAdapter.setKeyStrike(rowID, 1);
}
else {
mySQLiteAdapter.setKeyStrike(rowID, 0);
}
cursor = mySQLiteAdapter.queueAll();
cursorAdapter.swapCursor(cursor);
mySQLiteAdapter.close();
};