I'm new to this. After scrolling my ListView, items in "same position" as items I selected before get auto selected. (Same position I mean position in the screen not in the database.) I had this prob before, because I was selecting items by their indexes of ListView in OnItemClickListener. However i face this prob again although I'm doing it right way I think.
When I click on item in ListView, I get its unique ID and based on that I change SELECTED value of this item (of this row in the database) to 0 or 1 (depending on this if it was clicked or not). After that I switch backgrnd color to grey (or back to white). This is handled in CursorAdapter which distinguishes SELECTED property.
Here's my code.
OnCreate in MainActivity.kt
val dbHelper = DBHelper(this)
val db = dbHelper.writableDatabase
val myCursor = db.rawQuery("SELECT * FROM ${ContractClass.FeedReaderContract.TABLE_NAME}", null)
val myAdapter = CursorAdapter(this, myCursor)
myListView.adapter = myAdapter
myListView.setOnItemClickListener { _, view, _, _ ->
val text = view.txtName.text
val select = "${ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD} MATCH ?"
val selectCursor = db.query(
ContractClass.FeedReaderContract.TABLE_NAME, // The table to query
null, // The array of columns to return (pass null to get all)
select, // The columns for the WHERE clause
arrayOf("$text"), // The values for the WHERE clause
null, // don't group the rows
null, // don't filter by row groups
null // The sort order
)
with(selectCursor) {
while (moveToNext()) {
val itemSel = getInt(getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED))
if (itemSel == 1){
val values = ContentValues().apply {
put(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED, 0)
}
val count = db.update(
ContractClass.FeedReaderContract.TABLE_NAME, values, select, arrayOf("$text"))
}else{
val values = ContentValues().apply {
put(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED, 1)
}
val count = db.update(
ContractClass.FeedReaderContract.TABLE_NAME, values, select, arrayOf("$text"))
}
}
}
}
CursorAdapter.kt
class CursorAdapter(context: Context, cursor: Cursor) : CursorAdapter(context, cursor, 0) {
// The newView method is used to inflate a new view and return it,
// you don't bind any data to the view at this point.
override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
if (cursor.getInt(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED)) == 0){
return LayoutInflater.from(context).inflate(R.layout.row_list_row, parent, false)
}else{
return LayoutInflater.from(context).inflate(R.layout.user_list_row_selected, parent, false)
}
}
// The bindView method is used to bind all data to a given view
// such as setting the text on a TextView.
override fun bindView(view: View, context: Context, cursor: Cursor) {
// Find fields to populate in inflated template
val tvBody = view.findViewById<View>(R.id.txtName) as TextView
val tvPriority = view.findViewById<View>(R.id.txtComment) as TextView
val tvPriority2 = view.findViewById<View>(R.id.txtThird) as TextView
val tvPriority3 = view.findViewById<View>(R.id.txtThi) as TextView
// Extract properties from cursor
val body = cursor.getString(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD))
val priority = cursor.getString(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_DEFN))
val priority2 = cursor.getInt(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract._id))
val priority3 = cursor.getInt(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED))
// Populate fields with extracted properties
tvBody.text = body
tvPriority.text = priority.toString()
tvPriority2.text = priority2.toString()
tvPriority3.text = priority3.toString()
}
}
Database Table Create
private val SQL_CREATE_ENTRIES =
"CREATE VIRTUAL TABLE ${ContractClass.FeedReaderContract.TABLE_NAME} USING fts4(" +
"${ContractClass.FeedReaderContract._id}," +
"${ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD} TEXT," +
"${ContractClass.FeedReaderContract.COLUMN_NAME_DEFN} TEXT," +
"${ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED} INTEGER)"
I found similar post here: List view with simple cursor adapter items checked are un-checked during scroll but I think, they suggest what I've already done.
Thanks for any help.
You don't appear to be changing the ListView's Cursor after you have made changes to/updated the underlying data.
Try, after making changes to the underlying data, updating the ListView's Cursor by
swapCursor(myCursor)
(or the notiftDatasetChanged()
method)Here's an equivalent of your App but in Java rather than Kotlin (not having any luck converting as I hardly ever use Kotlin).
This I believe does what you want. That is,
If you select an unselected row then all rows that contain the enword are selected and greyed out and the selected column value is set to 1.
If you select a selected (grey) row then those rows that contain the enword are de-selected and are change to white with the selected column value being changed back to 0
Note that rather than create an FTS table I've mimicked the FTS and used LIKE instead of MATCH.
Are toggled if they are selected the background is grey else white. e.g. initially it is :-
If cat is clicked (2nd row) then all other cat rows are also toggled and greyed as per :-
and so on.
public class MainActivity extends AppCompatActivity {
DBHelper dbhelper;
ListView myListView;
MyCursorAdapter myAdapter;
Cursor mCursor;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myListView = this.findViewById(R.id.myListView);
dbhelper = new DBHelper(this);
addSomeTestingData();
manageListView();
}
private void manageListView() {
mCursor = dbhelper.getAllRows();
if (myAdapter == null) {
myAdapter = new MyCursorAdapter(this,mCursor);
myListView.setAdapter(myAdapter);
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
dbhelper.updateSelected(mCursor.getString(mCursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD)));
manageListView();
}
});
} else {
myAdapter.swapCursor(mCursor);
}
}
private void addSomeTestingData() {
if (DatabaseUtils.queryNumEntries(dbhelper.getWritableDatabase(),ContractClass.FeedReaderContract.TABLENAME) > 1) return;
for (int i=0; i < 10; i++) {
dbhelper.addRow("Apple", "Thing that falls from trees");
dbhelper.addRow("Cat", "Something that is furry and sits on mats");
dbhelper.addRow("Bear", "Something that is furry that eats honey but doesn't ssit on a mat");
dbhelper.addRow("Dog", "Something is furry and friendly");
dbhelper.addRow("Echida", "An upside down hedgehog");
dbhelper.addRow("Ferret", "Something that is furry and found up trouser legs");
dbhelper.addRow("Giraffe", "Something that has 5 legs one pointing up");
dbhelper.addRow("Hippo", "An animal that loves mud and water but not humans");
dbhelper.addRow("Ibis", "A white feathered flying thing");
dbhelper.addRow("Jaguar", "A car or a large black cat");
dbhelper.addRow("Kangaroo", "A marsupial that boxes, skips and has a puch for shopping trips");
dbhelper.addRow("Lizard", "A rock dweller");
dbhelper.addRow("Mammoth", "A big hairy elephant now extinct");
dbhelper.addRow("Nuthatch", "A small bird that does not customise nuts so they have hatches.");
dbhelper.addRow("Ostrich", "A l argefast running bird that does not fly");
dbhelper.addRow("Panther", "A skinny pink cat that walks on only two of it's four lehs");
dbhelper.addRow("Queen", "A female rule of a country");
dbhelper.addRow("Rhinocerous", "A Hippo like animal that has a name that is hard to spell");
dbhelper.addRow("Tiger", "A live verion of Winnie the Pooh's friend Tigger");
dbhelper.addRow("Stork", "A skinny ostrich that flies and delivers children through Dream World.");
}
}
}
Obviously the addSomeTestingData method is just for that.
Note that there's hardly any other code. The DB access has all been moved to the DBHelper class.
The crux of the matter is the manageListView method.
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(@Nullable Context context) {
super(context, ContractClass.DBNAME,null,ContractClass.DBVERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(ContractClass.FeedReaderContract.CRTSQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public Cursor getAllRows() {
SQLiteDatabase db = this.getWritableDatabase();
return db.query(ContractClass.FeedReaderContract.TABLENAME,null,null,null,null,null,null);
}
public void updateSelected(String selected) {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("UPDATE "
+ ContractClass.FeedReaderContract.TABLENAME
+ " SET " + ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED
+ "= CASE " + ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED +
" WHEN 1 THEN 0 ELSE 1 END " +
" WHERE " + ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD + " LIKE ?",
new String[]{selected}
);
}
public long addRow(String enWord, String definition) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD,enWord);
cv.put(ContractClass.FeedReaderContract.COLUMN_NAME_DEFN,definition);
return db.insert(ContractClass.FeedReaderContract.TABLENAME,null,cv);
}
}
public class MyCursorAdapter extends CursorAdapter {
public MyCursorAdapter(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_list_row,parent,false);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (cursor.getInt(cursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED)) < 1) {
view.setBackgroundColor(0xFFF9F9F9);
} else {
view.setBackgroundColor(0xFFD9D9D9);
}
TextView tvBody = view.findViewById(R.id.txtName);
TextView tvPriority = view.findViewById(R.id.txtComment);
TextView tvPriority2 = view.findViewById(R.id.txtThird);
TextView tvPriority3 = view.findViewById(R.id.txtThi);
tvBody.setText(cursor.getString(cursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD)));
tvPriority.setText(cursor.getString(cursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_DEFN)));
tvPriority2.setText(cursor.getString(cursor.getColumnIndex(ContractClass.FeedReaderContract._id)));
tvPriority3.setText(cursor.getString(cursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED)));
}
}