SimpleCursorAdapter remove values - java

I have a ListView on each row i have a LinearLayout with some objects in it (mostly some TextViews).
This ListView i fill it dynamically from a cursor. In this cursor i have one value true or false.
I want to hide or make non clickable the lines with value false. I try this code but doesn't work
public void contentProviderInitialized(final Cursor cursor) {
SimpleCursorAdapter commonTickets = new SimpleCursorAdapter(MyClass.this,
R.layout.row_ticketing, cursor, new String[] {"price", "productName", "stopName" },
new int[] { R.id.ticketing_price, R.id.ticketing_product, R.id.ticketing_stop_name }
) {
#Override
public void bindView(View view, Context context, Cursor cursor) {
String enabledStr = cursor.getString(cursor.getColumnIndex("enabled"));
String product = cursor.getString(cursor.getColumnIndex("productName"));
boolean enabled = Boolean.parseBoolean(enabledStr);
LinearLayout ticketingRow = (LinearLayout) view.findViewById(R.id.ticketing_row);
if (enabled) {
ticketingRow.setEnabled(true);
} else {
ticketingRow.setEnabled(false);
}
super.bindView(view, context, cursor);
};
MyClass.this.ticketing_list_view.setAdapter(commonTickets);
}
}

Override isEnabled on the adapter
http://developer.android.com/reference/android/widget/BaseAdapter.html#isEnabled(int)
This answer seems to hint at it. Use movetoposition on the cursor. It sounds like the performance would be bad with that, though, so you might want to do some caching of true/false values based on numeric position? Try it out. See how it goes. The caching might be a waste.

This was a great help to try another aproach:
Android - how to delete item from a cursor?
Add the positions you want to see to a new MatrixCursor and swap your Cursor to the new Matrix Cursor

Related

Populating a ListView with SQLite - ListView appends rows but doesn't show text

I'm trying to populate a ListView with SQLite items. After reading about this here and in other tutorials/examples I tried the following code, but I can't spot where exactly is the issue with it, since it's not working as intended, ListView isn't showing any items. My database has only one type of data, a string called sentence. Any help that could at least point me a direction in solving the issue is appreciated.
First I made a FavoritesDataBaseCore.java:
package com.easyprojects.artgames;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* Created by Vinicius on 19/05/2017.
*/
public class FavoritesDataBaseCore extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "Favorites.db";
public FavoritesDataBaseCore(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table favorite(_id integer primary key autoincrement, sentence text not null)");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists favorite");
onCreate(db);
}
}
The sentence is supposed to be added through a button click in CharacterActivity.java, relevant code:
ImageButton setFavoriteBtn;
private SQLiteDatabase database;
FavoritesDataBaseCore helper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_character);
setFavoriteBtn = (ImageButton) findViewById(R.id.setFavoriteBtnId);
setFavoriteBtn.setOnClickListener(this);
// Database stuff
helper = new FavoritesDataBaseCore(this);
database = helper.getWritableDatabase();
}
// Code below is inside public void onClick(View view)
case R.id.setFavoriteBtnId:
ContentValues values = new ContentValues();
values.put("sentence", characterTV.getText().toString());
database.insert("favorite", null, values);
// A Toast here showing values.toString() works well
break;
And finally the Activity that contains the ListView and creates the Cursor, I suspect the problem is here, since the Cursor is a major responsable for setting a correct adapter to the ListView, FavoritesActivity.java, relevant code:
private SQLiteDatabase database;
FavoritesDataBaseCore helper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_favorites);
helper = new FavoritesDataBaseCore(this);
database = helper.getReadableDatabase();
String[] field = {"sentence"};
int[] to = new int[]{R.id.SentenceAd};
Cursor cursor = getData();
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cursor, field, to, 0);
final ListView FavoritesListView = (ListView) findViewById(R.id.listviewID); // ListView
FavoritesListView.setAdapter(adapter);
}
public Cursor getData(){
Cursor cursor;
String[] field = {"sentence"};
cursor = database.query("favorite",null,null,null,null,null,null);
return cursor;
}
My database_adapter.xml looks like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/SentenceAd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
Edit: After adapting the code with #MikeT's suggestions I've come to the conclusion my ListView is appending rows each time I add a value into the database, but sentence is never shown in those rows.
There are a number of issues. The first few are related to the creation of the Cursor in preparation for it's use by the SimpleCursorAdapter. Then there is an issue with how the SimpleCursorAdpater has been instantiated/ssetup.
The Cursor.
Cursor adpaters require that a column named _id exists. To overcome this issue the cursor should include both columns. The easiest way to accomplish this is to use null as the 2nd parameter to the query method.
Change
cursor = database.query("favorite",field,null,null,null,null,null);
to
cursor = database.query("favorite",null,null,null,null,null,null);
A Cursor returned from the query method will not be null (cusror.getCount() is how you would check for no rows), so there is no need or use checking if it is null. Additionally the Cursor Adpater (simple or custom) will do the necessary cursor navigation. There is no need to move the cursor. The original has been edited accordingly so no change is required.
Last in regard to the Cursor, closing the Database would close the Cursor (again already edited).
The SimpleCursorAdapter
The issue is that you are using a supplied layout as in android.R.layout.simple_list_item_1 (the 2nd parameter) this expects you to signify which Cursor column (you have signified sentence) will be displayed in it's specific TextView but you have provided a TextView in your own layout.
Changing
int[] to = new int[]{R.id.SentenceAd};
to
int[] to = new int[]{android.R.id.text1};
Is one way to resolve the problem, but it's a waste your time and effort in creating you own layout.
To utilise your own layout then don't make the above change (as you will want the id of the TextView in your layout) but instead change
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cursor, field, to, 0);
to use your layout
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.database_adapter, cursor, field, to, 0);
In short the 2nd (The layout to be used) and the 4th (The Column from the Cursor, by name) and 5th (The ID of the TextView within the layout where the data from the column will be placed) parameters are interdependent/reliant upon each other.

Android - Wanting to alternate icon images in a listview using cursorLoader

I have an application that displays a listView of contacts sorted by Last, then first names. Beside each contact is an image (icon). There are 3 kinds of contacts for which I'd like to display 3 different images (customers/suppliers/other) I have a default image now that is set to customer. I'm wondering if there's a way using the cusorLoader shown below to alternate images on the fly, or whether it would just be best to add a method involving a cursor in my onResume. (onResume is called each time I need to display the images). I believe simpleCursorAdapter can only take textViews as args, so if it's possible, maybe a compound textview/image would work. My icons are not stored in the database, just in the drawables.
Thanks in advance for any replies.
#Override
protected void onResume() {
super.onResume();
//Starts a new or restarts an existing Loader in this manager
getLoaderManager().restartLoader(0, null, this);
}
/*
* The fillData method binds the simpleCursorAadapter to the listView.
*/
private void fillData() {
String[] from = new String[] { ContactsDB.COLUMN_LAST_NAME, ContactsDB.COLUMN_FIRST_NAME };
//The XML views that the data will be bound to:
int[] to = new int[] {R.id.label2, R.id.label};
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.contact_row, null, from,
to, 0);
setListAdapter(adapter);
}
// Sort the names by last name, then by first name
String orderBy = ContactsDB.COLUMN_LAST_NAME + " COLLATE NOCASE ASC"
+ "," + ContactsDB.COLUMN_FIRST_NAME + " COLLATE NOCASE ASC" ;
// Creates a new loader after the initLoader () call
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { ContactsDB.ROW_ID, ContactsDB.COLUMN_LAST_NAME, ContactsDB.COLUMN_FIRST_NAME };
CursorLoader cursorLoader = new CursorLoader(this,
SomeContentProvider.CONTENT_URI, projection, null, null, orderBy);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in.
// (The framework will take care of closing the old cursor once we return.)
adapter.swapCursor(data); //Call requires Min API 11
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed.
// Data is no longer available, delete the reference
adapter.swapCursor(null);
}
}
Here is the code I use to dynamically show a drawable on a ListView, you have to use the function setViewBinder on your adapter:
mAdapter.setViewBinder(new ViewBinder() {
public boolean setViewValue(View aView, Cursor aCursor, int aColumnIndex) {
//Modification of the icon to display in the list
if (aColumnIndex == aCursor.getColumnIndex(DatabaseHandler.RATE_EMOTION)) {
int emotionID = aCursor.getInt(aColumnIndex);
Drawable emotionDrawable = resources.getDrawable(R.drawable.ic_unknown_rate);
//if emotion is set
if(emotionID > 0){
String emotionDrawablePath = "ic_smi" + emotionID;
int emotionDrawableID = resources.getIdentifier(emotionDrawablePath,"drawable", getPackageName());
//if a drawable is found
if(emotionDrawableID > 0){
emotionDrawable = resources.getDrawable(emotionDrawableID);
}
}
ImageView emotionImage = (ImageView) aView;
emotionImage.setImageDrawable(emotionDrawable);
return true;
}
return false;
}
});
You can see in this example that I change the drawable according to the data I get from the cursor for every row.

Android SimpleCursorAdapter.ViewBinder not updating bound TextView

I have a SimpleCursorAdapter and I'm attempting to bind a SimpleCursorAdapter.ViewBinder to it. Below is the code that I am using.
// Setup Cursor and SimpleCursorAdapter, called in Activity onResume()
Cursor userCursor = getUserProfile(userEmail);
mAdapter = new SimpleCursorAdapter(this,
R.layout.user_profile,
userCursor,
new String[] {
StackOverFlow.Users.FULL_NAME,
StackOverFlow.Users.EMAIL },
new int[] {
R.id.txtvwProfileUserFullName,
R.id.txtvwProfileOtherUserInfo } );
mAdapter.setViewBinder(new UserProfileViewBinder());
My UserProfileViewBinder class:
private class UserProfileViewBinder implements SimpleCursorAdapter.ViewBinder {
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
TextView tv;
switch (columnIndex) {
case 1: // TODO: Magic Numbers, bad!
tv = (TextView) view.findViewById(R.id.txtvwProfileUserFullName);
String userFullName = cursor.getString(columnIndex);
tv.setText(userFullName);
break;
case 2: // TODO: Magic Numbers, bad!
tv = (TextView) view.findViewById(R.id.txtvwProfileOtherUserInfo);
String userEmail = cursor.getString(columnIndex);
tv.setText(userEmail);
break;
}
return true;
}
}
Issue
When I load the activity, the TextView's do not get updated with the data from the userCursor. I can confirm that userCursor is in fact pointing to a valid row in the backing SQLite database, and has data, but the setViewValue method in UserProfileViewBinder never appears to be executing. I'm not sure what I'm doing wrong, or neglecting to include.
As an alternative, I'm doing the following to set the TextView's text:
if (mAdapter.getCursor().moveToFirst()) {
mUserFullName.setText(mAdapter.getCursor().getString(
StackOverFlow.USER_FULL_NAME));
mUserOtherInfo.setText(mAdapter.getCursor().getString(
StackOverFlow.USER_EMAIL));
}
But I don't think this will automatically update the TextView's when the cursor data changes. when the ContentProvider calls setNotificationUri after executing a query, and the REST server call returns and the thread updates the row the cursor is pointing at.
Thanks in advance.
Are you planing to do other thing besides setting the text on those two TextViews? If the answer is no then you have no reason to use the ViewBinder as the SimpleCursorAdapter, by default, will set the text for you.
Anyway, I don't understand why you used the columnIndex parameter to identify the views and not the id of the view that is passed in. See if this helps:
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (view.getId() == R.id.txtvwProfileUserFullName) {
String userFullName = cursor.getString(columnIndex);
((TextView) view).setText(userFullName);
return true;
} else if (view.getId() == R.id.txtvwProfileOtherUserInfo) {
String userEmail = cursor.getString(columnIndex);
((TextView) view).setText(userEmail);
return true;
} else {
return false;
}
}

How can I change the Data that I got from my Database?

I get my data out of my db with the following code:
private void fillData() {
cursor = mDbAdapter.fetchAllSubjects();
startManagingCursor(cursor);
String[] from = new String[] { DatabaseAdapter.KEY_TITLE, DatabaseAdapter.KEY_LECTURER, DatabaseAdapter.KEY_BEGIN };
int[] to = new int[] { R.id.title, R.id.lecturer, R.id.time };
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter subjects = new SimpleCursorAdapter(this, R.layout.subject_row, cursor, from, to);
setListAdapter(subjects);
}
Now my problem is, that I want to add 3 other columns from my db and want to get the following:
"("+DatabaseAdapter.KEY_TYPE+") "+DatabaseAdapter.KEY_TITLE
DatabaseAdapter.KEY_LECTURER
new Date(DatabaseAdapter.KEY_BEGIN)
new Date(DatabaseAdapter.KEY_END)
--> these two should be in one TextView in the way dd.MM. HH:mm (this is from BEGIN) - HH:mm (this is from END)
I don't know how I'm able to do that - please help me :)
Ok I finally figured out what you really wanted.
Instead of using "SimpleCursorAdapter" directly, you can create your own Cursor adapter, inside which you can mainipulate the data as you want.
Create a new Adapter "SubjectsAdapter.java". In this Adapter you will override the "bindView" and "newView". This allows us to apply a view to the cursor. But before doing so, gives us the opportunity to change the data from the cursor.
This will give you an idea what has to be done.
private void fillData()
{
cursor = mDbAdapter.fetchAllSubjects();
startManagingCursor(cursor);
SubjectsAdapter subjectsAdapter = new SubjectsAdapter(this, cursor);
setListAdapter(subjectsAdapter);
}
//SubjectsAdapter.java - make changes to fix bugs/compilation errors. This is untested.
public class SubjectsAdapter extends ResourceCursorAdapter
{
public SubjectsAdapter(Context context, Cursor cur) {
super(context, R.layout.subject_row, cur);
}
#Override
public View newView(Context context, Cursor cur, ViewGroup parent)
{
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return li.inflate(R.layout.subject_row, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor)
{
TextView titleText = (TextView)view.findViewById(R.id.title);
titleText.setText(cursor.getString(cursor.getColumnIndex(DatabaseAdapter.KEY_TITLE)));
//You can add code to retrieve other columns here.
//This is where you retrieve the date in long format from cursor, convert it to a required format, and then using it.
TextView beginTimeText = (TextView)view.findViewById(R.id.time);
Long lBeginDate = cursor.getLong(cursor.getColumnIndex(DatabaseAdapter.KEY_BEGIN));
String sBeginDate = getFormattedDate(lBeginDate);
beginTimeText.setText(sBeginDate);
}
private String getFormattedDate(Long lDate)
{
SimpleDateFormat smdf = new SimpleDateFormat("MM/dd/yyyy h:mm:ss a");
String sDate = smdf.format( lDate ));
return sDate;
}
}

How do I implement autocomplete with cursoradapter

I have an SQLite database containing 2 tables 4000+ rows each used for autocomplete. I saw very simple examples that use an array of strings to provide autocomplete or they use the list of contacts to do the same. Obviously none of these work in my case. How do I use my own SQLite database with my own autocomplete data, for the autocomplete. Do I have to create content providers? How? Please give me some examples because I couldn't find any. I have managed to override SQLiteOpenHelper to copy the database from the assets folder to the /data/data/MY_PACKAGE/databases/ folder on the android. I have created a custom CursorAdapter that uses my custom SQLiteOpenHelper and returns a cursor from runQueryOnBackgroundThread. I get strange errors about some _id column missing. I have added the _id column to my tables. I also don't understand what is the Filterable interface doing and when does my data get filtered. What methods/classes do I need to override? Thanks.
It works.
You need the SQLiteOpenHelper from here. You basically have to copy your database into a specific folder from your assets folder. Then you need a custom CursorAdapter that uses your custom SQLiteOpenHelper.
Here is the onCreate method for my activity.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search);
KeywordsCursorAdapter kwadapter = new KeywordsCursorAdapter(this, null);
txtKeyword = (AutoCompleteTextView)this.findViewById(R.id.txtKeyword);
txtKeyword.setAdapter(kwadapter);
txtCity = (AutoCompleteTextView)this.findViewById(R.id.txtCity);
btnSearch = (Button)this.findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(this);
}
Here is the cursoradapter. You can pass null for cursor when constructing.
public class KeywordsCursorAdapter extends CursorAdapter {
private Context context;
public KeywordsCursorAdapter(Context context, Cursor c) {
super(context, c);
this.context = context;
}
//I store the autocomplete text view in a layout xml.
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.keyword_autocomplete, null);
return v;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
String keyword = cursor.getString(cursor.getColumnIndex("keyword"));
TextView tv = (TextView)view.findViewById(R.id.txtAutocomplete);
tv.setText(keyword);
}
//you need to override this to return the string value when
//selecting an item from the autocomplete suggestions
//just do cursor.getstring(whatevercolumn);
#Override
public CharSequence convertToString(Cursor cursor) {
//return super.convertToString(cursor);
String value = "";
switch (type) {
case Keywords:
value = cursor.getString(DatabaseHelper.KEYWORD_COLUMN);
break;
case Cities:
value = cursor.getString(DatabaseHelper.CITY_COLUMN);
break;
}
return value;
}
#Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
//return super.runQueryOnBackgroundThread(constraint);
String filter = "";
if (constraint == null) filter = "";
else
filter = constraint.toString();
//I have 2 DB-s and the one I use depends on user preference
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
//String selectedCountryCode = prefs.getString("selectedCountry", "GB");
String selectedCountryCode = prefs.getString(context.getString(R.string.settings_selected_country), "GB");
selectedCountryCode += "";
//Here i have a static SQLiteOpenHelper instance that returns a cursor.
Cursor cursor = MyApplication.getDbHelpers().get(selectedCountryCode.toLowerCase()).getKeywordsCursor(filter);
return cursor;
}
}
Here is the part that returns the cursor: it's just a select with a like condition.
public class DatabaseHelper extends SQLiteOpenHelper {
...
public synchronized Cursor getKeywordsCursor (String prefix) {
if (database == null) database = this.getReadableDatabase();
String[] columns = {"_id", "keyword"};
String[] args = {prefix};
Cursor cursor;
cursor = database.query("keywords", columns, "keyword like '' || ? || '%'", args, null, null, "keyword", "40");
int idcol = cursor.getColumnIndexOrThrow("_id");
int kwcol = cursor.getColumnIndexOrThrow("keyword");
while(cursor.moveToNext()) {
int id = cursor.getInt(idcol);
String kw = cursor.getString(kwcol);
Log.i("keyword", kw);
}
cursor.moveToPosition(-1);
return cursor;
}
...
}
You can also create a custom content provider but in this case it would be just another useless class you need to override.

Categories