Related Spinners - android

This application should have four or more related spinners which should reload when their 'parent' spinner' selection changes - as an example with 2 spinners: houses, and rooms - if you choose a house, the room spinner should reload from the sqlite database.
I have tried two approaches: a MySpinner class that takes a "child" Spinner in its constructor and tells the child to update itself when OnSelectedItem is triggered, like so
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (MySpinner.this.mChild.equals(null) == false) {
MySpinner.this.mChild.updateData((int)id);
}
}
the child's updateData is
public void updateData (int parentValue) {
new backgroundTask().execute("create");
}
which background tasks is an AsyncTask to query the sqlite database:
#Override
protected Void doInBackground(String... params) {
Db = new MyDatabase(mContext);
Db.open();
if(params[0] == "create") {
if (mTable.equals("T_room")){
mCursor = mDb.getRooms(mParentValue);
}
}
return null;
}
My second approach has been to create all my spinners directly in the activity.java file. This second approach has me implement one AsyncTask for all 4 or more spinners and choose what to query from the db, based on who calls with what value.
The first approach crashes on the only 'real' line of code in the asynctask, the second approach drives me mad with autosetting spinners and a jumble of ifs in the asynctask.
I'm not a coder by any means, and wonder if someone well versed in object-oriented coding can enlighten me as to what would be good coding behaviour to solve my specific problem (several spinners that update each other on selection.)

This is interesting, at this moment I'm doing something similar. Just keep a reference to the adapter, and inside onItemSelected access the object with adapter.getItem(pos). Then you can use this object to update the second spinner adapter. Just take care of UI threading. I would like to do this in a cleaner way but I don't know how to do it.

Related

Returning # Of Items from ParseQueryAdapter

I am trying to obtain the amount of objects returned from the ParseQueryAdapter and put them in a TextView to show the user the amount loaded.
I tried something like this, but keeps displaying 0 even though items are loaded into the listview fine.
bookSalesAdapter = new BookSalesAdapter(this);
setListAdapter(bookSalesAdapter);
int bookSalesAmount = bookSalesAdapter.getCount();
String bookSales = Integer.toString(bookSalesAmount);
TextView salesAmount = (TextView) findViewById(R.id.textView_bookSalesAmount);
salesAmount.setText(bookSales);
Any ideas how I would go about grabbing the amount of items using the ParseQueryAdapter rather then running another query?
This didn't seem to work from the activity with listview.
Try using ParseQueryAdapter.OnQueryLoadListener.
You can write code to run after the objects have been loaded by the adapter. From there, you should be able to fetch the count.
Before you call setListAdapter():
bookSalesAdapter.addOnQueryLoadListener(new ParseQueryAdapter.OnQueryLoadListener<Book>() {
#Override
public void onLoading() {}
#Override
public void onLoaded(List<Event> events, Exception e) {
// Access the item count through the adapter's getCount() and update your UI...
}
});

Obtaining the current Android view and forcing it to be redrawn

How can I get the current Android view when it displays data that has been updated, and force it to be redrawn? I worked through Android's Notepad tutorial and completed lesson three without any problems — the solution is provided, after all — but I'm stuck on my first non-trivial modification.
I added a new button to the menu, next to the Add note button. When pressed, that button adds a letter to the title of each note in the system. However, the new titles don't show up in the list of notes no matter how long I wait. I know the updater works because the changes do appear if I dismiss the app and bring it back up.
So far, I've discovered that I have to use some kind of invalidation method to make the program redraw itself with the new values. I know that invalidate() is used from the UI thread and postInvalidate() is used from non-UI threads 1, 2, but I don't even know which thread I'm in. Also, both of those methods have to be called from the View object that needs drawing, and I don't know how to obtain that object. Everything I try returns null.
My main class:
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
case INSERT_ID:
createNote();
return true;
case NEW_BUTTON:
expandTitles();
return true;
default:
// Intentionally empty
}
return super.onMenuItemSelected(featureId, item);
}
private void expandTitles() {
View noteListView = null;
// noteListView = findViewById(R.layout.notes_list); // null
// noteListView =
// getWindow().getDecorView().findViewById(android.R.id.content);
// From SO question 4486034
noteListView = findViewById(R.id.body); // Fails
mDbHelper.expandNoteTitles(noteListView);
}
My DAO class:
public void expandNoteTitles(View noteListView) {
Cursor notes = fetchAllNotes();
for(int i = 1; i <= notes.getCount(); i++) {
expandNoteTitle(i);
}
// NPE here when attempt to redraw is not commented out
noteListView.invalidate(); // Analogous to AWT's repaint(). Not working.
// noteListView.postInvalidate(); // Like repaint(). Not working.
}
public void expandNoteTitle(int i) {
Cursor note = fetchNote(i);
long rowId =
note.getLong(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_ROWID));
String title =
note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)) + "W";
String body =
note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY));
updateNote(rowId, title, body);
}
What do I have to do to get the updated note titles to show up as soon as I press the button?
Obviously, I'm a complete newbie to Android. I point this out to encourage you to use small words and explain even obvious things. I know this is the millionth "Android not redrawing" question, but I've read dozens of existing posts and they either don't apply or don't make sense to me.
1: What does postInvalidate() do?
2: What is the difference between Android's invalidate() and postInvalidate() methods?
According to the tutorial, the list of existing notes are presented in a ListView. That is an adapter based View, so the items it shows are sourced from an adapter extending theBaseAdapter class. In these cases, you should notify the adapter that the contents have changed by calling its notifyDatasetChanged method. This'll signal the ListView to update and redraw its rows.
Edit:
Sorry, I now realize that this example uses CursorAdapters. These source the items to show from a Cursor object that was obtained from a database query. Now, what the notifyDatasetChanged() tells the adapter is, that the data that backs the adapter has changed, so Views that show stuff based on this adapter need to redraw their contents. In the case of a CursorAdapter, this data is coming from a cursor. So you also need to requery that cursor, refreshing it from the DB, like this:
private void expandTitles() {
mDbHelper.expandNoteTitles();
CursorAdapter adapter = (CursorAdapter)getListAdapter();
adapter.getCursor().requery();
}
The requery() method automatically calls the notifyDatasetChanged() in this case, so you don't need to worry about that, the list will update itself. See this thread also: https://groups.google.com/forum/?fromgroups#!topic/android-developers/_FrDcy0KC-w%5B1-25%5D.

Cannot use onItemSelectListener() with Spinner with One Item

So I have a spinner (spinner2 here) which is populated via an ArrayAdapter from an SQLite table. On selecting an item I want it
deleted from the DB
deleted from the spinner
The code below actually works. Except when the spinner has only one item. When that happens
it seems onItemSelected is not called at all.
I get the following LogCat
10-01 22:30:55.895: WARN/InputManagerService(1143): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy#45a06028
Oh and when two items are populating the spinner, spinner.getcount() shows two items, so it's not some strange case of the system thinking the spinner is empty or something like that.
This is the code:
public class SpinnerItemSelectListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if(parent == spinner2){
if(autoselected){
autoselected=false;
}
else{
//uniqvalarray is the arraymade from pulling data from SQLite and populaitng array adapter
Integer i = uniquevalarray.get(pos);
deleteRow(i);//deletes the row from the database and repopulates the above array.
autoselected=true;//just a boolean to stop autoslecting in onCreate()
//makeAlert(i);initially wanted to make alert box.
loadSpinner2();//reloads the spinner with new data
}
}
}
public void onNothingSelected(AdapterView parent) {
//TODO
}
}
The spinner runs this way : Only fire when you change the selected item . If you dont change that element , cause its the only one that exist , it can't change .
The solution i think you must use is using a button next to the spinner to throw the delete funcions.
You must think that Spinner is not made to be with an unique element , cause only changes usually when you change the selected one . then the natural solution can be that .

Android - Scrolling through ListView leads to Out of Bounds Exception

I'm working on an application that primarily consists of a list view. It's backed up by my own custom array adapter whose size changes every 5 seconds. If I scroll through the list view as the array adapter changes from a greater size to a lesser size, I get an out of bounds exception. It makes sense to me why this occurs (since I'm scrolling at a position beyond the new array size), but I was wondering if there was a good way to debug it. I can't seem to come to a clear conclusion, and I was wondering if I could get some help.
I update the adapter using the following asyncTask...
public class myTask extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
while(isRunning){
myData.clear();
getData();
publishProgress();
SystemClock.sleep(5000);
}
return null;
}
protected void onProgressUpdate(Void...progress){
listAdapter.notifyDataSetChanged();
}
}
myData is the ArrayList that supports listAdapter and getData() is the function that populates myData with the relevant info that will eventually be displayed in my list view.
Is there a good way to tackle this problem?
Regards
Are you using a custom adapter?
Perhaps in your getViewTypeCount() and getItemViewType(int position) it is going out of bounds there. The view type count should be from (0, n] but the item view type should be [0, n).
E.g. view type count should be 2
But the view types should be 0 and 1, not 1 and 2.
try to override the getCount() function of the adapter class:
#Override
public int getCount() {
return [list that contains data].size();
}
Have you tried resetting the user to either the top of the list or as close as possible when the list changed sizes?
Sorry, maybe is not the answer for the question, but having that infinite loop seems for me that is not the correct way, even if it´s an async task.
I would try to use the an AlarmManager or a Timer.
With that said, seems what you have is a race condition, take into account that it´s not immediately when you ask for data, or a specific position and rebuild the views.
Cheers,
Francisco.

Passing variables to a ViewBinder

I have a ViewBinder that is acting as the middleman between my ListView and a cursor adapter to a database. Depending on the context this data is being displayed in, I need the ViewBinder to do different things.
As an example, take a task management application, that is displaying tasks for all of the task groups that exist. Maybe in this case the app wants to display the name of the task group in the list view, when it wouldn't want to if it was showing tasks for one of the specific task groups. The list view item could have a hidden field, and the ViewBinder can be used to map the task group's name to the field AND set it to visible when necessary (as instructed the main application).
My question is how would one tell the ViewBinder the context in which it is displaying, so it can determine how to behave?
I realize this can likely be done by implementing many different ViewBinders, but this would require much code to be duplicated, and would prefer doing it with a single ViewBinder that I just pass certain parameters to.
This can be done by implementing the constructor for the ViewBinder you are implementing.
Per the example, one can do something like this:
private Boolean displayGroupName = true;
public ToDoViewBinder(Boolean displayGroupName) {
this.displayGroupName = displayGroupName;
}
#Override
public boolean setViewValue(View view, Cursor c, int columnIndex) {
if(displayGroupName)
{
//Do necessary stuff
return true;
}
else
return false;
}

Categories

Resources