I've got a wierd bug. I have created a listview with headings using another listview using this http://jsharkey.org/blog/2008/08/18/separating-lists-with-headers-in-android-09/. Ive populated the headings list with a straight array (there aren't that many) and populated the items listview from a preexisting database.
Both listviews show up fine and the items click through to the relevent selections ok, but there are entries on the list from outside the search parameters.
This is from the class that returns the lists:
private Cursor getBeginnersCursor()
{
this.cursor = null;
try{
this.getmDB();
if(this.mDB == null)
{
System.out.println("mDB = null");
}
this.cursor = mDB.query(TABLE_NAME,new String[]{KEY_ID,GAME_NAME}, "_id > 11 AND _id < 35",
null,null,null,null);
if(this.cursor != null)
{
this.cursor.moveToNext();
}
mDB.close();
return this.cursor;
}catch(SQLException e){
throw e;
}
}
private List<String> getBeginnersArrayList()
{
this.getmDB();
this.cursor = this.getBeginnersCursor();
String result;
for(this.cursor.moveToFirst(); !this.cursor.isAfterLast();this.cursor.moveToNext())
{
result = this.cursor.getString(1) + "\n";
this.beginnersArrayList.add(result);
}
this.cursor.close();
return beginnersArrayList;
}
Many thanks in advance.
Ok I worked it out. SQLite doesnt seem to treat text integer values the same as Integer values, so it was mixing the id 1 in between id's 9 and 10 and so on. Ive created another column to define the sections instead.
Related
I have a JSON Array which consists of some contacts in my phonebook who are also users of my app. For example, the JSON Array might look like :
[{"contact_phonenumber":"11111"},{"contact_phonenumber":"22222"},{"contact_phonenumber":"33333"}]
phoneNumberofContact is a string which, in the do statement in my code below, returns every contact in my phone. How can I check which phoneNumberofContact numbers appear in my JSON Array and then, besides those contacts in the ListView put the words '- app user'. My ListView is working fine, I just want to add this feature in.
So, for example, for the number 11111 I would have in my ListView :
Joe Blogs - app user
11111
Here's my code:
JSONArray jsonArrayContacts = response;
//response is something like [{"contact_phonenumber":"11111"}, etc...]
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_contact);
//selectPhoneContacts is an empty array list that will hold our SelectPhoneContact info
selectPhoneContacts = new ArrayList<SelectPhoneContact>();
listView = (ListView) findViewById(R.id.listviewPhoneContacts);
}
//******for the phone contacts in the listview
// Load data in background
class LoadContact extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
// we want to delete the old selectContacts from the listview when the Activity loads
// because it may need to be updated and we want the user to see the updated listview,
// like if the user adds new names and numbers to their phone contacts.
selectPhoneContacts.clear();
// we have this here to avoid cursor errors
if (cursor != null) {
cursor.moveToFirst();
}
try {
// get a handle on the Content Resolver, so we can query the provider,
cursor = getApplicationContext().getContentResolver()
// the table to query
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
null,
null,
// display in ascending order
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
// get the column number of the Contact_ID column, make it an integer.
// I think having it stored as a number makes for faster operations later on.
// get the column number of the DISPLAY_NAME column
int nameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
// get the column number of the NUMBER column
int phoneNumberofContactIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
cursor.moveToFirst();
// We make a new Hashset to hold all our contact_ids, including duplicates, if they come up
Set<String> ids = new HashSet<>();
do {
System.out.println("=====>in while");
// get a handle on the display name, which is a string
name = cursor.getString(nameIdx);
// get a handle on the phone number, which is a string
phoneNumberofContact = cursor.getString(phoneNumberofContactIdx);
//----------------------------------------------------------
// get a handle on the phone number of contact, which is a string. Loop through all the phone numbers
// if our Hashset doesn't already contain the phone number string,
// then add it to the hashset
if (!ids.contains(phoneNumberofContact)) {
ids.add(phoneNumberofContact);
SelectPhoneContact selectContact = new SelectPhoneContact();
selectContact.setName(name);
selectContact.setPhone(phoneNumberofContact);
selectPhoneContacts.add(selectContact);
}
} while (cursor.moveToNext());
} catch (Exception e) {
Toast.makeText(NewContact.this, "what the...", Toast.LENGTH_LONG).show();
e.printStackTrace();
// cursor.close();
} finally {
}
if (cursor != null) {
cursor.close();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
adapter = new SelectPhoneContactAdapter(selectPhoneContacts, NewContact.this);
// we need to notify the listview that changes may have been made on
// the background thread, doInBackground, like adding or deleting contacts,
// and these changes need to be reflected visibly in the listview. It works
// in conjunction with selectContacts.clear()
adapter.notifyDataSetChanged();
listView.setAdapter(adapter);
}
}
In the first, you can parse the jsonArrayContacts to a List:
final List<String> responseContacts = new ArrayList<String>();
try {
JSONArray responseObject = new JSONArray(response);
for (int i = 0; i < responseObject.length(); i++) {
final JSONObject obj = responseObject.getJSONObject(i);
responseContacts.add(obj.getString("contact_phonenumber"));
}
// System.out.println("the matching contacts of this user are :" + responseContacts);
} catch(Exception e) {
e.printStackTrace();
}
after you get your local contacts, then you have two sets of contacts, so it's easy to check which number appears in your json array contacts.
And then you can pass the responseContacts into SelectPhoneContactAdapter during you initialize it, and in getView() method of the adapter, you can know whether you need to put the words '- app user' to your item view or not.
I have 5 empty TextViews where I add the names. After adding a name, it is stored in a database. The database consist on 2 columns, the item ID and the item NAME. This is an example of what I'm doing:
- Mark1 //ID=1, NAME= Mark1
- Mark2 //ID=2, NAME= Mark2
- Mark3 //ID=3, NAME= Mark3
- Empty
- Empty
I add and edit perfectly the textViews, but I'm facing a problem when deleting. This has something to do with the way I'm getting the values from the database, I'll explain:
Every time the app starts, or I edit, add or delete one element, what I do is get the items from the database, get them into a Map, and copy them into the textviews (whose at a first time are invisible) making visible just the ones that have a name setted.
This is the code I use to do that:
public void getTravelers() {
/*Create map where store items*/
Map<Integer, String> nameList = new HashMap<Integer, String>();
/*Lon in providers query() method to get database's items and save them into the map*/
Cursor c = getContentResolver().query(TravelersProvider.CONTENT_URI, PROJECTION, null, null, null);
if (c.moveToFirst()) {
do {
nameList.put(Integer.parseInt(c.getString(c.getColumnIndex(Travelers._ID))), c.getString(c.getColumnIndex(Travelers.NAME)));
}while(c.moveToNext());
}
if (c != null && !c.isClosed()) {
c.close();
}
/*Check size*/
int size = nameList.size();
if (size >= 1) {
/*Save items in TextViews*/
//TODO: This is the code I should fix
for (int i = 0; i <= size; i++) {
if (i==1) {
traveler1.setText(nameList.get(i).toString());
traveler1.setVisibility(View.VISIBLE);
}
if (i==2) {
traveler2.setText(nameList.get(i).toString());
traveler2.setVisibility(View.VISIBLE);
}
if (i==3) {
traveler3.setText(nameList.get(i).toString());
traveler3.setVisibility(View.VISIBLE);
}
if (i==4) {
traveler4.setText(nameList.get(i).toString());
traveler4.setVisibility(View.VISIBLE);
}
if (i==5) {
traveler5.setText(nameList.get(i).toString());
traveler5.setVisibility(View.VISIBLE);
}
}
}
}
The problem comes in the for loop. Let's supposse that from the items named above, I want to delete Mark2 with ID=2, so then the size of the new Map would be 2, and it would enter to (i == 1) and (i == 2). But when entering to this last one, it would do traveler2.setText(nameList.get(2).toString()); and as seen, there is no element existing with the ID=2 because that is the one that I've deleted and it throws a NPE.
So my question is, what would be the right way to do this without facing this problem?
You should go for switch case other than for loop. Than code will not be in loop.
Finally I get what I need just changing the Key value of the Map that was the same as the ID of the database:
if (c.moveToFirst()) {
int key = 0;
do {
key++;
nameList.put(key, c.getString(c.getColumnIndex(Travelers.NAME)));
}while(c.moveToNext());
}
if (c != null && !c.isClosed()) {
c.close();
}
Basically this way I don't need to change nothing more as now the key value of the Map will match with the Textview position
I am working on android project and am making using of a ListView that retrieves data from the SQLite database.
I am making a dataset using an ArrayList and then adding this ArrayList into an ArrayAdapter.
When the data is being retrieved from the database, I am telling SQLite to do the sorting so everything is in alphabetical order when it is added into the ListView. At certain times, the information will be added dynamically to to the ListView without it requiring to re-fetch everythin from the database again. However, I want to keep everything in alphabetical order.
How would I do this, do I sort the DataSet and then call the notifyDataSet Changes or do I do the sort directly on the ArrayAdapter. I've looked into performing the sort on the ArrayAdapter but this wants an argument that uses a Comparator but not sure what this is and can't find any working examples that may be of any help for what I want to achieve.
Below is the code that populates the array and sets the list adapter
ArrayList<Spanned> passwords = managePasswordList.getPasswordList();
if (passwords != null && passwords.size() > 0)
{
passwordArrayAdapter = new ArrayAdapter<Spanned>(getActivity().getApplicationContext(),
android.R.layout.simple_list_item_activated_1, passwords);
setListAdapter(passwordArrayAdapter);
myListView.setTextFilterEnabled(true);
txtNoRecords.setVisibility(View.GONE);
}
else
{
txtNoRecords.setVisibility(View.VISIBLE);
}
I am then adding data to the dataset and refreshing the list view using the following
String company = Encryption.decrypt(passwords.get(i).company);
String username = Encryption.decrypt(passwords.get(i).username);
details = Html.fromHtml(company + "<br />" + "<small><font color=\"#767676\">" + username + "</b></small>");
passwords.add(details);
passwordArrayAdapter.notifyDataSetChanged();
Thanks for any help you can provide.
UPDATE 1
I've tried doing what Nick Bradbury suggested but I am having a problem with the comparator. I have the following code but I don't know where to go from here.
SQLiteDatabase myDb = null;
Cursor cursor = null;
ArrayList<Spanned> passwords = new ArrayList<Spanned>();
try
{
myDb = context.openOrCreateDatabase("PasswordManager", Context.MODE_PRIVATE, null);
cursor = myDb.rawQuery("SELECT * FROM password ASC", null);
while (cursor.moveToNext())
{
final String company = Encryption.decrypt(cursor.getString(2));
final String username = Encryption.decrypt(cursor.getString(4));
Spanned details = Html.fromHtml(company + "<br />" + "<small><font color=\"#767676\">" + username + "</b></small>");
passwords.add(details);
Collections.sort(passwords, new Comparator<Spanned>() {
public int compare(Spanned lhs, Spanned rhs) {
return 0;
}
});
}
}
catch (SQLiteException ex)
{
common.showBasicAlertDialog("Unfortunately something has gone wrong.\n\nWe will fix this as soon as we can", false);
Log.e("Database Error", ex.toString());
return null;
}
In the return statement I have no idea what to do, I've tried return lhs.compareTo but the lhs and rhs variables don't have the compareTo function so I have not got a clue what to do.
Here's a simple example of sorting an ArrayList using Comparator. In this example, the ArrayList is defined as:
public class StatusList extends ArrayList<Status>
A sort routine for this ArrayList could look like this:
public void sort() {
Collections.sort(this, new Comparator<Status>() {
#Override
public int compare(Status item1, Status item2) {
return item2.getDate().compareTo(item1.getDate());
}
});
}
Replace <Status> with whatever object your ArrayList contains, then change the comparison to compare the values of the object you wish to sort by.
I have a Spinner which is to show a list of data fetched from database. The data is returned to a cursor from query, and the cursor gets passed to spinner's SimpleCursorAdapter. It is working fine as such, but I want to insert another item on top of this data. For example, the spinner is already showing a list of user created templates saved in DB, but I want to insert "New Template" and "Empty Template" on top of the list of templates, and it needs to be inserted into Cursor/SimpleCursorAdapter somehow.
I have considered using an arraylist and populating the arraylist from cursor, but cursor is better solution for me since it contains other related rows of data too. I searched internet for other solutions and found some answers asking to use CursorWrapper for this purpose, but I could not find a concrete example how to use CursorWrapper to accomplish what I want. How can I insert some rows in cursor or can someone please give a easy to follow CursorWrapper example!! Thanks in advance.
You can use a combination of MergeCursor and MatrixCursor with your DB cursor like this:
MatrixCursor extras = new MatrixCursor(new String[] { "_id", "title" });
extras.addRow(new String[] { "-1", "New Template" });
extras.addRow(new String[] { "-2", "Empty Template" });
Cursor[] cursors = { extras, cursor };
Cursor extendedCursor = new MergeCursor(cursors);
This is the method I tried.
MatrixCursor m = new MatrixCursor(c.getColumnNames());
Cursor c = DBHelper.rawQuery("Select values from your_table");
MatrixCursor m = new MatrixCursor(c.getColumnNames());
//Use MatrixCursor#addRow here to add before the original cursor
while (c.moveToNext()) {
//Use MatrixCursor#addRow here to add before the original row
DBHelper.insertRow(c, m);
//Use MatrixCursor#addRow here to add after the original row
}
//Use MatrixCursor#addRow here to add after the original cursor
m.addRow(new String[]{col1Val, col2Val, col3Val,..., //to match the number of columns needed});
DBHelper.insertRow()
public final static void insertRow(Cursor from, MatrixCursor to) {
final String columns[] = from.getColumnNames(), values[] = new String[columns.length];
final int size = columns.length;
for (int i = 0; i < size; i++) {
values[i] = getStringFromColumn(from, columns[i]);
}
to.addRow(values);
}
With this method, you can add any amount of rows anywhere in your cursor. Even though it is not making use of CursorWrapper, it can be used with CursorAdapters or SimpleCursorAdapters.
I tried the solution provided by #naktinis, but the result wasn't what I expected. What I myself wanted to achieve as an adapter in which new elements can be added at the top (index 0). However, with the solution given, new elements were indeed added at the top but only to the END of the MatrixCursor. In other words, when I added rows dynamically to the "extras" MatrixCursor, I got something like this:
"extras" row 1
"extras" row 2
"extras" row 3
"cursor" row 1
"cursor" row 2
"cursor" row 3.
However, what I really wanted to achieve was something like this:
"extras" row 3
"extras" row 2
"extras" row 1
"cursor" row 1
"cursor" row 2
"cursor" row 3.
In other words, most recent elements enter at the top (index 0).
I was able to achieve this manually by doing the follow. Note that I did not include any logic to handle dynamically removing elements from the adapter.
private class CollectionAdapter extends ArrayAdapter<String> {
/**
* This is the position which getItem uses to decide whether to fetch data from the
* DB cursor or directly from the Adapter's underlying array. Specifically, any item
* at a position lower than this offset has been added to the top of the adapter
* dynamically.
*/
private int mCursorOffset;
/**
* This is a SQLite cursor returned by a call to db.query(...).
*/
private Cursor mCursor;
/**
* This stores the initial result returned by cursor.getCount().
*/
private int mCachedCursorCount;
public Adapter(Context context, Cursor cursor) {
super(context, R.layout.collection_item);
mCursor = cursor;
mCursorOffset = 0;
mCachedCursorCount = -1;
}
public void add(String item) {
insert(item, 0);
mCursorOffset = mCursorOffset + 1;
notifyDataSetChanged();
}
#Override
public String getItem(int position) {
// return the item directly from underlying array if it was added dynamically.
if (position < mCursorOffset) {
return super.getItem(position);
}
// try to load a row from the cursor.
if (!mCursor.moveToPosition(position - mCursorOffset)) {
Log.d(TAG, "Failed to move cursor to position " + (position - mCursorOffset));
return null; // this shouldn't happen.
}
return mCursor.getString(INDEX_COLLECTION_DATA);
}
#Override
public int getCount() {
if (mCachedCursorCount == -1) {
mCachedCursorCount = mCursor.getCount();
}
return mCursorOffset + mCachedCursorCount;
}
}
How can I create a list Array (the list display First Alphabet when scroll) with the cursor data?
Go through every element in the Cursor, and add them one by one to the ArrayList.
ArrayList<WhateverTypeYouWant> mArrayList = new ArrayList<WhateverTypeYouWant>();
for(mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) {
// The Cursor is now set to the right position
mArrayList.add(mCursor.getWhateverTypeYouWant(WHATEVER_COLUMN_INDEX_YOU_WANT));
}
(replace WhateverTypeYouWant with whatever type you want to make a ArrayList of, and WHATEVER_COLUMN_INDEX_YOU_WANT with the column index of the value you want to get from the cursor.)
One quick correction: the for loop above skips the first element of the cursor.
To include the first element, use this:
ArrayList<String> mArrayList = new ArrayList<String>();
mCursor.moveToFirst();
while(!mCursor.isAfterLast()) {
mArrayList.add(mCursor.getString(mCursor.getColumnIndex(dbAdapter.KEY_NAME))); //add the item
mCursor.moveToNext();
}
Even better than #imbrizi's answer is this:
ArrayList<String> mArrayList = new ArrayList<String>();
while(mCursor.moveToNext()) {
mArrayList.add(mCursor.getString(mCursor.getColumnIndex(dbAdapter.KEY_NAME))); //add the item
}
moveToNext() will return false if there isn't anything left, so this reduces the SLOC by a few, and is easier to see.
Even better is to get the column index outside of the loop.
ArrayList<String> mArrayList = new ArrayList<String>();
int columnIndex=mCursor.getColumnIndex(dbAdapter.KEY_NAME)
while(mCursor.moveToNext()) {
mArrayList.add(mCursor.getString(columnIndex)); //add the item
}
This one worked really well for me because I wanted an arraylist of objects:
List<MyObject> myList = new ArrayList<String>();
Cursor c = ...
while(c.moveToNext()) {
myList.add(new MyObject(cursor.getInt(cursor.getColumnIndex("_id")), cursor.getString(cursor.getColumnIndex("column1")), cursor.getInt(cursor.getColumnIndex("column2")));
}
c.close();
Just make a POJO MyObject and make sure it has a constructor.
In Kotlin you can use this extension:
fun <T> Cursor.toList(block: (Cursor) -> T) : List<T> {
return mutableListOf<T>().also { list ->
if (moveToFirst()) {
do {
list.add(block.invoke(this))
} while (moveToNext())
}
}
}
and use it:
val listOfIds = cursor.toList {
// create item from cursor. For example get id:
it.getLong(it.getColumnIndex("_id"))
}