Return object from CursorAdapter.get() - android

I'm overriding CursorAdapter and I need to get the last item, problem is that CursorAdapter has actually a get() method...but source is a db and it returns a plain object!! (I don't even know what is it, I'd expect it returning a Cursor object instead...)
Neverthless, how can I make it return an instance of my Wrapper db row class?
Example:
say my db has rows like these:
id|first name| surname
I'd make a class Person from that.
Now I'd like to have a Person get(int i) method from cursor adapter...

well just use the adapter.getItem() and cast it to Cursor, and there is no need to move the cursor manually like in accepted answer
Cursor cursor = (Cursor) myCursorAdapter.getItem(position);
String myColumnValue = cursor.getString(cursor.getColumnIndex("YOUR_COLUMN_NAME"));

Now I'd like to have a Person get(int i) method from cursor adapter...
This seems like a strange request. I would pass the Cursor itself (or the Cursor returned from CursorAdapter's getItem()) to a regular method in my Activity instead. But here are the basic steps to create a Person get() method.
Create your Person class:
public class Person {
long id;
String firstName;
String surname;
}
And in your custom CursorAdapter simply use a method like this:
public Person get(int position) {
Cursor cursor = getCursor();
Person person;
if(cursor.moveToPosition(position)) {
person = new Person();
person.id = cursor.getLong(cursor.getColumnIndex("_id"));
person.firstName = cursor.getString(cursor.getColumnIndex("firstName"));
person.surname = cursor.getString(cursor.getColumnIndex("surname"));
results.add(person);
}
return person;
}

Well, i had to debug to find the answer. Suppose you have something like this with
SimpleCursorAdapter
themesAdapter = new SimpleCursorAdapter(getContext(), R.layout.spinner_with_count, null,
new String[]{ThemeData.COLUMN_ID, ThemeData.COLUMN_SET_COUNT}, new int[] { R.id.spinnerTxLabel, R.id.spinnerTxCount }, 0);
inputTheme.setAdapter(themesAdapter);
Then you have to retrieve like this way.
inputTheme.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
// if not all themes, get subthemes items
if(position > 0){
// load subtheme
Cursor currentCursor = (Cursor)inputTheme.getAdapter().getItem(position);
if(currentCursor != null) {
String value = currentCursor.getString(currentCursor.getColumnIndexOrThrow(Data.COLUMN_ID));
}
}
}
};

Related

AutoCompleteTextView with SimpleCursorAdapter does not filter

In my app, I have a few AutoCompleteTextView widgets that use an ArrayAdapter.
private List<String> adapterList = new ArrayList<String>();
ArrayAdapter<String> dropdownAdapter;
dropdownAdapter = new ArrayAdapter<>(getContext(), R.layout.simple_dropdown_item, adapterList);
autoCompleteTextView.setAdapter(dropdownAdapter);
It works beautifully. As I type into the View, I get words-starting-with results in the dropdown.
I want to do this with another AutoCompleteTextView, but this time using a SimpleCursorAdapter.
nameSearchCursor = dbHelper.getChecklistTabDataByChecklistId(outingId, checklistId, nameColumn);
NameSearch = root.findViewById(R.id.SearchNames);
String[] nsColumns = new String[]{nameColumn};
int[] nsTo = new int[]{R.id.simpleDropdownItem};
nameSearchCursorAdapter = new SimpleCursorAdapter(getContext(), R.layout.simple_dropdown_item,
nameSearchCursor, nsColumns, nsTo, 0);
NameSearch.setAdapter(nameSearchCursorAdapter);
If I start typing in this new View, the dropdown appears and shows the entire list, and nothing changes as I type. No filtering occurs. What do I need to do differently (and perhaps why) to get a CursorAdapter to work with this View that I didn't need to do when using an ArrayAdapter. I have searched this site and read the Developer Docs and there must be something I just don't get. Please enlighten me.
This site allowed me to answer this question: http://www.outofwhatbox.com/blog/2010/11/android-simpler-autocompletetextview-with-simplecursoradapter/
Here is my completed code:
private void setUpNameSearch() {
// Get AutoCompleteTextView
nameSearchView = root.findViewById(R.id.SearchNames);
// Define from/to info
final String[] nsColumns = new String[]{nameColumn};
final int[] nsTo = new int[]{R.id.simpleDropdownItem};
// Create adapter. Cursor set in setFilterQueryProvider() below.
nameSearchCursorAdapter = new SimpleCursorAdapter(getContext(), R.layout.simple_dropdown_item,
null, nsColumns, nsTo, 0);
// Set adapter on view.
nameSearchView.setAdapter(nameSearchCursorAdapter);
// OnItemClickListener - User selected value from DropDown
nameSearchView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view, int position, long id) {
// Get the cursor. Positioned to the corresponding row in the result set.
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
// Get the name selected
String selectedName = cursor.getString(cursor.getColumnIndexOrThrow(nameColumn));
// Do something with this value...
}
});
// Set the CursorToStringconverter, to provide the values for the choices to be displayed
// in the AutoCompleteTextview.
nameSearchCursorAdapter.setCursorToStringConverter(new SimpleCursorAdapter.CursorToStringConverter() {
#Override
public CharSequence convertToString(Cursor cursor) {
final String name = cursor.getString(cursor.getColumnIndexOrThrow(nameColumn));
return name;
}
});
// Set the FilterQueryProvider, to run queries for choices
nameSearchCursorAdapter.setFilterQueryProvider(new FilterQueryProvider() {
#Override
public Cursor runQuery(CharSequence constraint) {
Cursor cursor = dbHelper.getMatchingNames(outingId, checklistId, nameColumn,
(constraint != null ? constraint.toString() : null));
return cursor;
}
});
}
I wanted to duplicate the word-starting-with default functionality of the AutoCompeteTextView using the SQLite Cursor, only to find that REGEXP are not fully supported. So this StackOverflow topic gave me the LIKE workaround. SQLite LIKE alternative for REGEXP, Match Start of Any Word
I hope this helps others.

Updating the SQLite DB with a Button click on listView

I am trying to build a simple stock application, I have a list view on my main activity which has a "Sell" button on every list item I have. The functionality of the Sell button should decrease the quantity of that particular item by updating the row for that item and setting the quantity to quantity-1.
To achieve this, I have found that setting up an on click listener in my custom cursor adapter class was the way to do it. I am using a content provider class for my Database operations. So what I tried to do is, trigger a function which is in my main activity, within the OnClickListener which is in my cursor adapter. Here is some code that would explain more. (please forgive my terrible programming skills, I am fairly new )
My approach does not seem to work for some reason, first click on Sell button does not do anything, and the second one crashes the application with the reason:
android.database.StaleDataException: Attempting to access a closed CursorWindow.Most probable cause: cursor is deactivated prior to calling this method.
p.s. I did not send the context from the adapter to decrease count method, and it was crashing of a null pointer on the getContentResolver().
Update function in my content provider:
private int updateItem (Uri uri, ContentValues values, String selection, String[] selectionArgs){
if (values.containsKey(InventoryContract.ItemEntry.COLUMN_NAME)){
String name = values.getAsString(InventoryContract.ItemEntry.COLUMN_NAME);
if (name == null){
throw new IllegalArgumentException("Item requires a name");
}
}
// If values size is zero, do not try to update the database.
if (values.size() == 0){
return 0;
}
// Otherwise, get writeable database to update the data
SQLiteDatabase database = mDbHelper.getWritableDatabase();
// Perform the update on the database and get the number of rows affected
int rowsUpdated = database.update(InventoryContract.ItemEntry.TABLE_NAME, values, selection, selectionArgs);
// If 1 or more rows were updated, then notify all listeners that the data at the
// given URI has changed
if (rowsUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
// Return number of rows updated
return rowsUpdated;
}
The function I have written ( or tried to write ) in my main activity
public void decreaseCount(Context context, int columnId, int quantity){
quantity = quantity -1;
ContentValues values = new ContentValues();
values.put(InventoryContract.ItemEntry.COLUMN_QUANTITY, quantity);
Uri updateUri = ContentUris.withAppendedId(InventoryContract.ItemEntry.CONTENT_URI, columnId);
int rowsAffected = context.getContentResolver().update(updateUri, values,null, null);
}
and lastly, the custom OnClickListener I have added to the button (p.s. the listener is inside the overriden bindView method of the cursor adapter )
sellButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int columnIdIndex = mCursor.getColumnIndex(InventoryContract.ItemEntry._ID);
int quantityIndex = mCursor.getColumnIndex(InventoryContract.ItemEntry.COLUMN_QUANTITY);
CatalogActivity catalogActivity = new CatalogActivity();
catalogActivity.decreaseCount(context2, Integer.valueOf(mCursor.getString(columnIdIndex)), Integer.valueOf(mCursor.getString(quantityIndex)));
}
});
Thank you in advance !
The problem is very trivial. I fixed your codes. First don't create objects out of activities. Try to use boxing and unboxing technic to retrieve your context back. In your InsertCursorAdapter constructor should be like this
public ItemCursorAdapter(Context context, Cursor c) {
super(context, c);
this.context = context;
}
Then you need to save your cursor from bindView method.
Then you need to bind the context object to get your activity object back. All in all, you would have something like this:
#Override
public void bindView(View view, final Context context, Cursor cursor) {
this.mCursor = cursor;
TextView nameTextView = view.findViewById(R.id.name);
TextView quantityTextView = view.findViewById(R.id.quantity);
sellButton = view.findViewById(R.id.sell_button);
ImageView imageView = view.findViewById(R.id.item_image);
sellButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int columnIdIndex = mCursor.getColumnIndex(InventoryContract.ItemEntry._ID);
int quantityIndex = mCursor.getColumnIndex(InventoryContract.ItemEntry.COLUMN_QUANTITY);
String col= mCursor.getString(columnIdIndex);
String quan= mCursor.getString(quantityIndex);
CatalogActivity catalogActivity = (CatalogActivity) context;
catalogActivity.decreaseCount( Integer.valueOf(col), Integer.valueOf(quan));
}
});
Also I changed your decreaseCount arguments. Because this method is in activity class you don't need to pass it anytime you need to decrease the value. getContentResolver() method is a method in super class AppCompatActivity and because it is public, your activity have implemented it already.
//TODO: Decrease count by one
public void decreaseCount(int columnId, int quantity){
quantity = quantity -1;
ContentValues values = new ContentValues();
values.put(InventoryContract.ItemEntry.COLUMN_QUANTITY, quantity);
Uri updateUri = ContentUris.withAppendedId(InventoryContract.ItemEntry.CONTENT_URI, columnId);
int rowsAffected = getContentResolver().update(updateUri, values,null, null);
}

How to display data from a list view to a activity

I have a list view and a database I want to display the data from the database in another activity so I made a OnItemClickListener for my list view.
Now I get the position of the ClickListener but because I have made my adapter to display data so the latest input from the user is on top. I nead to reverse the position of the onClick.
At the moment I get:
1
2
3
4
but I need:
4
3
2
1
because of the database id.
If for example the user clicks position 3 on the list I want the database to return the row 3.
ListView:
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
Intent myIntent = new Intent(getActivity(), JornalListViewClick.class);
myIntent.putExtra("intVariableName",position);
startActivity(myIntent);
}
});
The getRows of the database:
public Cursor getAllRowre(){
String where=null;
Cursor cursor=db.query(true, DATABASE_TABLE, ALL_KEY, where, null, null, null,ID_KEY + " DESC", null);
if(cursor!=null){
cursor.moveToFirst();
}
return cursor;
}
The activity where I want to display the data:
Intent mIntent = getIntent();
int intValue = mIntent.getIntExtra("intVariableName", 0);
intValue++;
text=(TextView) findViewById(R.id.textViewUserInputFromListClicked);
Cursor cursor=dbJ.getRowUserInput(intValue+"");
if(cursor.moveToFirst()){
do{
String mes=cursor.getString(0);
text.setText(mes);
}while(cursor.moveToNext());
}
An alternative way:
Use a POJO to store every row retrieved
Implement Comparator and use Collections.sort(List, Comparator) to sort it before putting it into adapter
public class MyData{
private String field;
// getter and setter
}
After retrieve the String from database, you can instantiate MyData class and set the string (or more fields) into the instance. Put all results in a Collection. E.g.:
Vector<MyData> listOfResults=new Vector<MyData>();
if(cursor.moveToFirst()){
do{
String mes=cursor.getString(0);
MyData instance=new MyData();
listOfResults.add(instance);
}while(cursor.moveToNext());
}
return listOfResults;
After retrieving data from database, you want to sort it, right? Try this:
Collections.sort(listOfResults,new Comparator<MyData>(){
public int compareTo(MyData a,MyData b){
return a.getField().compareTo(b.getField());
}
});
Inside the Activity containing the ListView, create a private class implementing ListAdapter. E.g.:
private class MyListAdapter implements ListAdapter{
private Vector<MyData> data;
public MyListAdapter(Vector<MyData> list){
data=list;
}
/*** other methods you need to implement ***/
}
Instantiate MyListAdapter by supplying the Vector you got from database access method.
Then call setAdapter(ListAdapter) of the ListView inside the Activity after the data is ready.

How can i use the Check Boxes with ListView and Insert the values in Sqlite Database in android

I'm developing an android app which shows phone contact as ListView(used Cursoradapter).Now I Want to add checkbox to the listView ,My problem is How to Insert checkbox data into database, based on if it is checked or not?
In my database class, I have a function which use to add names and numbers to my database,
createntry(String number,String name) // in my database class
Should I invoke this function in my CursorAdapter class ?
Recently, I found out that I should use getView function,but unfortunately I have no idea about getView, My question are
1-I should use this function in My CursorAdapter or else?
2- how to implement this function?
My CursorAdapterClass
public class ContactCursorAdapterCT extends CursorAdapter {
public ContactCursorAdapterCT(Context context, Cursor c) {
super(context, c);
}
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView name = (TextView)view.findViewById(R.id.contactlistTV1);
name.setText(cursor.getString
(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
TextView phone = (TextView)view.findViewById(R.id.contactlistTV2);
phone.setText(cursor.getString
(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.lvct, parent, false);
bindView(v, context, cursor);
return v;
}
public View getView(final int pos, View inView, ViewGroup parent) { //getView
}
My activity class
public class Contacts extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.contacts);
Cursor cursor = getContentResolver().query
(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null,null, null);
startManagingCursor(cursor);
ContactCursorAdapterCT adapter= new ContactCursorAdapterCT
(Contacts.this, cursor);
ListView contactLV = (ListView) findViewById(R.id.listviewblcontactsDB);
contactLV.setAdapter(adapter);
My database Class
public long creatEntry(String inputnumber , String name) { // for add data
// TODO Auto-generated method stub
ContentValues cv= new ContentValues();
cv.put(KEY_NUMBER, inputnumber);
cv.put(N_NAME, name);
Log.v(inputnumber, "adding to Database");
return ourdatabase.insert(DATABASE_TABLE, null, cv);
}
Firstly, no you don’t need getView. bindView in conjunction with newView is completely sufficient as a replacement for it, some would probably say even better. Moreover, you don’t need to call bindview in new view. here’s a restructuring of what should be there.
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.lvct, parent, false);
return v;
}
This is totally your call but no I don’t think that you should your createEntry method into the adapter, at least in its methods. The thing is that methods in the adapter are called for each row of your listview, so you might have a lot happening redundantly and plus I personally find it wasteful to be making insertions into a database into in increments. rather I think you have no choice but to do it all at once, because what if someone unselects a checkbox? you delete your entry? not only is it wasteful but it would be too cumbersome to keep track of the cursor positions or _id, you’d need to re-query every-time that something had been added. What you should do is maintain a list of what needs to be added to the database and bulk insert it when it’s done.
First you need to make an object holding the data that you want inserted. An object is the cleanest way cause you need to hold multiple pieces of information. Here it is very simple, you insert values you want into the constructor and then retrieve with the getter methods.
public class ContactObject {
private String name;
private String phone;
public ContactObject(String name, String phone) {
super();
this.name = name;
this.phone = phone;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
}
Now you need an object to hold these objects when they’re checked. weird I know, but it much more convenient if they’re identified and can be iterated over and in general are batched together for referencing. I think this sort of task calls for the HashMap. Make it in the constructor.
contactMap = new HashMap<Integer, ContactObject>(c.getCount());
Now it’s time to boogie. make methods for the checkbox to add and remove stuff from your HashMap.
cbInsert.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (cbInsert.isChecked()) {
contactMap.put(cursor.getPosition(), new ContactObject(str_name, str_phone));
} else {
contactMap.remove(cursor.getPosition());
}
}
});
We’ve used the cursor position like an id for our objects within the HashMap. And when our checkbox is unchecked and we want to remove the object that was put in, we can just refer that that identifier that we used. Someone more prudent might want to check if something is there at the position before removing, that’s at your discretion. Now we’re almost done. How do we convert our HashMap to entries in the database? You have to access a database object, loop through and then get at your object one by one. The question is now is where. You could do it right in the adapter, but I usually do something like this in my activity because in my cases I usually have a database already made for the activity for other tasks and I don’t like to make more objects than I’m pushed to. So, what we can do is finish up with a getter method in our adapter for our HashMap:
public HashMap<Integer, ContactObject> getContactMap() {
return contactMap;
}
Now I’d imagine that you’d do something like this when your app is leaving so here goes.
#Override
protected void onDestroy() {
super.onDestroy();
HashMap<Integer, ContactObject> contactMap = adapter.getContactMap();
DatabaseHelper db = new DatabaseHelper(this);
// iterate through hashmap
for (Map.Entry<Integer, ContactObject> entry : contactMap.entrySet()) {
Integer key = entry.getKey();
ContactObject value = entry.getValue();
db.creatEntry(key, value.getPhone(), value.getName());
}
db.close();
}
Now things look a little weird, what happened what did i do with your entry method?
public long creatEntry(Integer id, String inputnumber, String name) { // for add data
long lng;
String strId = id.toString();
String[] selectionArgs = {strId};
Cursor cursor = ourdatabase.query(DATABASE_TABLE, null, "other_id = ?", selectionArgs, null, null, null);
if (cursor.moveToFirst()) {
// it exists, i'd assume that you might not want anything else done here
lng = -1;
} else {
// it doesn't exist
ContentValues cv= new ContentValues();
cv.put(KEY_NUMBER, inputnumber);
cv.put(N_NAME, name);
cv.put(KEY_OTHERID, strId);
Log.v(inputnumber, "adding to Database");
lng = ourdatabase.insert(DATABASE_TABLE, null, cv);
}
// cursor.close();
return lng;
}
As you can i see I modified it so that it takes in the id as well. I thought that you an issue that you'd run into would be having repeats in your database. I thought you could manage it by having another field for an id that you can modify.This id refers to the id passed in from the HashMap. I figured that every time that you make an insertion you first check if that previous id is there, then decide what you want to do. This is not a perfect solution but i just wanted to alert you that that issue is possible and give a possible hint as to manage it. In general the insert method should be fine if you only want to insert a couple rows but if you have a lot of stuff to insert, you might wanna look into bulk transactions for performance.
One more thing, checkboxes in your listview cannot be expected to have their states persist as you might normally expect it. You must explicitly dictate what state the checkbox has at each position. I made it correspond with if your HashMap has something filled with its corresponding key. Here's the full adapter method in hopes that it's made clearer:
public class ContactCursorAdapterCT extends CursorAdapter {
private LayoutInflater inflater;
private HashMap<Integer, ContactObject> contactMap;
public ContactCursorAdapterCT(Context context, Cursor c) {
super(context, c);
inflater = LayoutInflater.from(context);
contactMap = new HashMap<Integer, ContactObject>(c.getCount());
// i used c.getCount() as a capacity limit for this.
// if you opened made this multiple times, it might get over inflated and
// slow things down.
}
#Override
public void bindView(View view, Context context, final Cursor cursor) {
TextView name = (TextView)view.findViewById(R.id.contactlistTV1);
TextView phone = (TextView)view.findViewById(R.id.contactlistTV2);
final CheckBox cbInsert = (CheckBox) view.findViewById(R.id.contactlistCB1);
String str_name = cursor.getString
(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String str_phone = cursor.getString
(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
name.setText(str_name);
phone.setText(str_phone);
boolean isFilled = contactMap.containsKey(cursor.getPosition());
cbInsert.setChecked(isFilled);
// insert, remove objects to hashmap
cbInsert.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (cbInsert.isChecked()) {
contactMap.put(cursor.getPosition(), new ContactObject(str_name, str_phone));
} else {
contactMap.remove(cursor.getPosition());
}
}
});
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.lvct, parent, false);
return v;
}
public HashMap<Integer, ContactObject> getContactMap() {
return contactMap;
}
}
It would be a good idea to add a flag to your records which will indicate that they are checked or not. Meaning an entry will have a name, a number and a flag (checked/unchecked).
In your Adapter itself, you can check the value of this flag an act accordingly.
(A related answer was regarding checkboxes in ListView is given here)

How to get the values from DB in spinner

How to get values into the spinner from the DB. I want to display values or datas on UI from DB. Is it cursor necessary to use to populate the values onto the UI?
I just follow the things to get the values from database, when the spinner values changed by postion -
Java class
Spinner spinner = (Spinner) findViewById(R.id.spinner1);
//set some adapter here
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
public class MyOnItemSelectedListener implements OnItemSelectedListener
{
#SuppressWarnings("unused")
public void onItemSelected(AdapterView<?> parent,View view, int pos, long id)
{
try
{
switch(parent.getId())
{
case R.id.spinner2:
int selected_spinner_item_position=pos;
String expired_spinner_item =parent.getItemAtPosition(pos).toString();
if(selected_spinner_item_position==0)
{
//am just using simple cursor adapter here and getting the values from database using dh.fetchAll() when the position is 0
sc_adapter(dh.fetchAll());
spinnertemp = selected_spinner_item_position;
}
}
}
}
}
Database
public Cursor fetchAll()
{
SQLiteDatabase db = this.getReadableDatabase();
cursor = db.query(t1, new String[] {"_id",task, pname, prior, time, dateformat, duptitle,dupname}, "stat=0", null, null, null, prior);
return cursor;
}
Just do what you need like these steps.
How to populate a Spinner widget from a database
Follow it, its really simple.
yes, cursor is required to fetch result set object or data as result set. You can find solution from
Android Developers Blog

Categories

Resources