So I have an app where I have several drop down spinners several of which I would like to change their options based on the option selected by another spinner. My plan to do this was to put:
product_adapter.clear();
CharSequence[] array=makeArray(urlMaker.getProductid());
for(int i=0;i<array.length;i++){
product_adapter.add(array[i]);
}
product_adapter.notifyDataSetChanged();
within a case of the onItemSelected() method for the spinner that is to dictate the changing of contents of the other spinner. In theory the idea is that I clear the second spinner(product_adapter) then add a new array that is made by makeArray()(which returns an array of CharSequences as specified in the doc) to the spinner's adapter using product_adapter.add(array[i]) then call .notifyDataSetChanged() to formalize it. However, when it hits the first line the app crashes and yields the following stack trace:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.benhouse.weatherview, PID: 25791
java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(AbstractList.java:638)
at java.util.AbstractList$SimpleListIterator.remove(AbstractList.java:75)
at java.util.AbstractList.removeRange(AbstractList.java:658)
at java.util.AbstractList.clear(AbstractList.java:466)
at android.widget.ArrayAdapter.clear(ArrayAdapter.java:273)
at com.example.benhouse.weatherview.MainActivity$3.onItemSelected(MainActivity.java:102)
at android.widget.AdapterView.fireOnSelected(AdapterView.java:924)
at android.widget.AdapterView.dispatchOnItemSelected(AdapterView.java:913)
at android.widget.AdapterView.-wrap1(AdapterView.java)
at android.widget.AdapterView$SelectionNotifier.run(AdapterView.java:883)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Why am I getting this error and how can I fix it?
Edit: this is how I am intially populating the spinner that I am trying to change:
ArrayAdapter<CharSequence> T_spinnerAdapter = new ArrayAdapter(this, R.layout.support_simple_spinner_dropdown_item, makeArray("GFS"));
Time_spin.setAdapter(T_spinnerAdapter);
Edit 2: So I am a bit of an idiot. I was trying to modify product spinner rather than time spinner(the intended spinner) and the product spinner is initialized from an array in the values.xml. Also probably doesn't like modifying itself within itself. Thanks for the indirect help as it lead me to think and undummy-ify my code.
You are using list collection which doesn't support remove operation.
If you are constructing ArrayAdapter by providing the array of items:
ArrayAdapter (Context context,
int resource,
T[] objects)
Actual implementation creates fixed length list using Arrays.asList()
Fix:
ArrayList list = new ArrayList(Arrays.asList(makeArray("GFS")));
new ArrayAdapter(this, R.layout.support_simple_spinner_dropdown_item, list);
It is a common mistake. Another common mistake is providing an immutable list.
Related
I have a strange issue setting up an adapter for a ListView, the ListView is inside the CandidateView of a custom keyboard, I use a class that inherit of AsyncTask class to execute an API call that returns me a list of items to be displayed on a ListView, in the method onPostExecute() of AsyncTask I take the search results and add to an ArrayList, then this array is being passed to the adapter using the method updateResults(), and finally set the ListView's adapter, everything is fine until here, the code compiles, but during runtime I get the next error:
FATAL EXCEPTION: main
Process: ....., PID: 5326
java.lang.ClassCastException: >android.widget.FrameLayout$LayoutParams cannot be cast to >android.widget.AbsListView$LayoutParams
at >android.widget.ListView.clearRecycledState(ListView.java:545)
at android.widget.ListView.resetList(ListView.java:532)
at android.widget.ListView.setAdapter(ListView.java:475)
at >......KeyboardIME$SearchAsyncTask.onPostExecute(KeyboardIME.java:2325)
at >......KeyboardIME$SearchAsyncTask.onPostExecute(KeyboardIME.java:2195)
at android.os.AsyncTask.finish(AsyncTask.java:667)
at android.os.AsyncTask.-wrap1(AsyncTask.java)
at >android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:684)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at >com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.>java:886)
at >com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Based on what it said it is a casting error, but I'm not using a FrameLayout on any of the XML files related to the view, also the line 2195 is where the inner AsyncTask class starts and the line 2325 is where I set the adapter for the ListView, this adapter is a basic class inherited from BaseAdapter.
Here are the code lines I execute as described above:
List<SearchItem> searchResults = new ArrayList<>(serverResults);
keyboardInstance.get().mAdapter.updateResults(searchResults);
keyboardInstance.get().resultListView.setAdapter(keyboardInstance.get().mAdapter);
I have a strange problem. I have 2 pre-defined spinner in the xml. However, the item and related dropdown item are rendering in runtime.
However, the following 2 statement provide different result of the layout. I have no idea why the result is like this. But the main difference is getApplicationContext() and this
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, getSrvNumList());
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(),
android.R.layout.simple_spinner_item, getSrvNumList());
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
As #MikeM. commented, it is the issue about the context. Hope the following answer can help someone with this issue.
Use Activity context instead of Application context
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MyActivity.this,
android.R.layout.simple_spinner_item, getSrvNumList());
Otherwise, the application will get different theme to render the spinner.
According to this post (look for Chapter 3), we can consider 2 types of Context: UI-Context (for example getActivity(), view.getContext()...) and non-UI-Context (such as getApplicationContext()...).
When inflate view, let use UI-Context to keep its application theme. That rule is:
* Do you need to access UI related stuff? Use UI-Context.
* Otherwise, Use Non-UI Context.
I'm retrieving some default from a database on application start and I'm using it to set the selected value of a spinner item in my activity.
if( key.equals("default Altitude Units")) {
Spinner s = ((Spinner)findViewById(R.id.spinAltitudeUnits));
ArrayAdapter a = (ArrayAdapter) s.getAdapter();
s.setSelection( a.getPosition(result.getString(2)));
}
The code works fine however the problem is that i'm getting a warning in Android Studio that says Warning unchecked call to 'getposition(T)' as a member of raw type 'android.widget.ArrayAdapter' on the a.getPosition call.
I'm happy that it's working but being new to android and Java I want to understand and eliminate as many warnings as possible from my code so any help in getting rid of this warning would be most welcome.
You are getting the position from some string. sting 2 in this case. A ArrayAdapter can contain many types. Integers for example. In your case it is not clear what your ArrayAdapter is containing.
Use this instead:
ArrayAdapter<String> a = (ArrayAdapter<String>) s.getAdapter();
I believe you need to paramaterize your ArrayAdapter. Do so with the following code:
ArrayAdapter<String> a = (ArrayAdapter<String>) s.getAdapter();
That is, if your ArrayAdapter contains strings.
EDIT: woops, got beat by a few seconds.
I have a ListFragment backed by an ArrayAdapter that gets populated by a Loader. When the user clicks on one of the items, I want to pass a reference to the selected item, as well as the rest of the list items to another fragment. My question is how should I get all of the items from the adapter? Here are the possibilities that I see:
1. Keep a reference to the backing List
Create the adapter like so:
List<DomainObject> items = new ArrayList<DomainObject>();
listAdapter = new ArrayAdapter<DomainObject>(getActivity(), R.layout.mine, items);
and then simply pass items or a copy of it to the next activity.
The downside I see of this is that I'm relying on the undocumented fact that the same list that I pass to the constructor contains the items later on.
2. Iterate through the adapter
When an item is clicked, iterate through the adapter and build up the list. This seems like an unnecessary amount of work. The items are contained in a List in the adapter and I'm manually copying each item to a new list.
3. Keep a separate list of items when adding to adapter
Before adding an item to the adapter, add it to a separate list that I maintain in the fragment. This is also wasteful as the list of items is copied in the ArrayAdapter and the fragment.
I'm a little late to the game, but I've run up against a similar issue.
One way to deal with #1 would be to maintain the reference to the list within a subclass of ArrayAdapter, so that your reuse is controlled by the adapter object.
Something like:
public class DomainAdapter extends ArrayAdapter<DomainObject> {
private final List<DomainObject> items;
public DomainAdapter(Context context, List<DomainObject> items) {
super(context, R.layout.mine, items);
this.items = items;
}
public List<DomainObject> getItems() {
return items;
}
}
The solution that I've gone with in the meantime is just to not use ArrayAdapter. In cases where you're fighting against this API, it seems like it's better just to use the less fully-featured (and complex) BaseAdapter. You can read more about the decision to go with BaseAdapter instead of ArrayAdapter in this article: Android Adapter Good Practices.
A quick test says that method 1 works. It seems the quickest and cleanest, but since it is undocumented you may want to test it across the intended platforms and whenever they update in case the underlying structure of ArrayAdapter changes.
I am using compile SDK version 22 and min SDK Version 10.
The best method is to "keep a reference to the List" BUT not passing "items" variable/parameter to the Constructor:
List<DomainObject> items = new ArrayList<DomainObject>();
listAdapter = new ArrayAdapter<DomainObject>(getActivity(), R.layout.mine);
In this way you only instantiate the ArrayList as an empty array and you will have to manage YOUR list by yourself.
I think first method is best way to do this.
I dont think, Data would be original for the Another Activity. because, You would pass items through bundle, so the object is written on bundle first and then in next Activity we read from bundle.
However, if you are using some other way to pass the list, use list.clone() to create new Object, instead of passing original one.
Consider the following code.
List<String> list = new ArrayList<String>();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, resource, textViewResourceId, list);
// Method 1 : Add an item.
adapter.add("ITEM1");
// Method 2 : Add an item
list.add("ITEM2");
I was wondering, which is the correct way to add item into ArrayAdapter? As seems to me, both methods just work fine.
Method 1 updates the associated AdapterView, if you have already attached the ArrayAdapter to the AdapterView. Method 2 does not, requiring you to call notifyDataSetChanged() on the ArrayAdapter.
Typically, you populate the ArrayList before creating the ArrayAdapter, then use Method 1 to add new entries dynamically later on (e.g., based on user data entry).
This is what I do, especially for my Search Results page - where it grows as the user scroll down (list changes).
I'd keep a local ArrayList of Strings that is global to the class,
Initialize the adapter (also locally and globally),
Have a designated method alter the ArrayList of strings,
Then call the adapter.notifyDataSetChanged();
This will not only update your list and also update the adapter to work in sync.
Hope this helps,
best,
-serkan