PROBLEM
My problem is that after changing data in my SpinnerArrayAdapter my Spinner does not react to item clicks on dropDownList.
However after orientation change occurs everything is working fine(?!).
EDIT: I noticed it does catch the item clicks but not representing/showing it on Spinner. Because after orientation change the selected item appears on Spinner
CONSTRUCTION
I have AutoCompleteTextView(ACTV) that's connected to AutoCompleteAdapter implementing Filterable. After entering some data into ACTV the result is passed to SpinnerArrayAdapter that is connected to Spinner.
There is a customListener set on AutoCompleteAdapter that is connected to SpinnerArrayAdapter and responsible for passing data between them.
Reason behind such construction is that user can have a 2-step choice. One on drop-down when choosing the data from ACTV and second one in case he change his mind.
So you can put POSTCODE in the ACTV select province that's connected to and change province when you miss-clicked/changed mind without forcing to enter POSTCODE again.
CODE
This is the part that is responsible for data change inside SpinnerArrayAdapter.
#Override
public void setCitiesFromPostcode(ArrayList<String> cities) {
this.clear();
this.addAll(cities);
notifyDataSetChanged();
}
I had a similar problem with ArrayAdapter. I just changed it to BaseAdapter and it works. Don't really know what the reason is, but it's somewhere in the implementation of ArrayAdapter.
Two common causes for this:
Event though it may look big enough, if your Spinner is too small it may not be able to display the value, confirm this by hardcoding the Spinner width and height to something large. Using a custom spinner item layout may help if this is the problem.
You're using custom objects in your array, not simple Strings or numbers that can be converted to strings. Use a custom class MyAdapter extends BaseAdapter implements SpinnerAdapter { } class in this case.
Related
I currently have a whole bunch of spinners in my application. I wasn't thinking much and for each spinner, the first index is filled with N/A. It's not a big deal, but I was hoping to make it a bit cleaner.
I was wondering if there was a way that instead of calling setSelection( index ), I wanted to know if there was a way to set a Spinner to have nothing selected.
Basically, I want the spinner to have a list of items within it, but display none of them until you click on it. I also don't want there to be a blank index at the top.
I think its quite possible that there is no way to do this, so if anyone is sure of this, please let me know.
Thanks.
Its better to put first position as blank, so that it can be seen as nothing selected.
spinners have to have a selected state. i would recommend having a "no selection" option
Another option is to have the bottom button selected and then set that one to be invisible (the reason i set the last one to selected is so that you dont have a chunk of blank space in your app as that looks a bit trashy):
case SPINNER_OPTION_FIRST:
yourSpinnerLabel.setVisibility(View.VISIBLE);
yourTextField.setVisibility(View.VISIBLE);
break;
// do this for all your other ones if you want
case SPINNER_OPTION_LAST:
yourSpinnerLabel.setVisibility(View.GONE);
yourTextField.setVisibility(View.GONE);
maybe not the most efficient but it works so i hope that helps (still new so cut me some slack)!
Modifying the list which holds data can lead lots of errors or tough to manage the data.
Instead of manipulating the list,you can add item in adapter with NONE item.
Like this
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
adapter.add("NONE");
adapter.addAll(products);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinner.setAdapter(adapter);
I tried
// getSelectedItemPosition() returns -1 if there is nothing selected
spinner.setSelection(-1);
but it failed [spinner was setting 1st element from list].
I added "empty position" to spinner data which mimics that "nothing is selected". Note that 'empty position' is not visible on 'drop down' list.
BTW. Sorry for C#, hope the concept is clearly visible.
Custom adapter:
class CustomAdapter<T> : ArrayAdapter<T> {
public override int Count {
get { return base.Count - 1; }
}
}
Init spinner:
var data = new List<string> { "elem 1", "elem 2", "" };
spinner.Adapter = new CustomAdapter<String>(this, Resource.Layout.SimpleSpinnerItem, data);
spinner.SetSelection(2);
I have a ListView where I want each item to have an ID number attached to it (not the same as the position number). I was hoping this could be done by setting a tag to each View item in the ListView using setTag() when these Views are being created.
Right now I'm creating the ListView like this:
final ListView listview = (ListView) findViewById(R.id.listView1);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, names);
listview.setAdapter(adapter);
The names variable in the ArrayAdapter parameters above is an ArrayList, and each string value in this list also has a unique ID that I want to link to this string somehow.
Is there any way I can get access to and modify each of the Views with a tag? One idea was to create my own extended class of ArrayAdapter and override the getView() method, but I don't really understand how it works and how I would go about doing this.
Or is there a better way to link IDs with each string like this than adding tags like I'm trying to do?
Create a ViewBinder and set the tags as the ListView is being populated with whatever you need. You can check all properties of the view to determine what tag goes where, so this should be what you're looking for.
myAdapter.setViewBinder(new MyViewBinder());
public class MyViewBinder implements ViewBinder {
#Override
public boolean setViewValue(View view, Object data, String text){
//Since it iterates through all the views of the item, change accordingly
if(view instanceof TextView){
((TextView)view).setTag("whatever you want");
}
}
}
I just used this exact same answer on another question (albeit slightly different) yesterday.
about getView , it works by using a method of recycling views. i will try to explain it in a simple way.
suppose you have tons of items that can be viewed . you don't want to really create tons of views too , since that would take a lot of memory . google thought of it and provide you the means to update only the views that need to be shown at any specific time.
so , if there is an empty space on the listview , it will be filled with a new view . if the user scrolls , the view that becomes hidden is recycled and given back to you on the getView , to be updated with the data of the one that is shown instead .
for example , if you scroll down , the upper view becomes hidden for the end user , but in fact it becomes the exact same view that is on the bottom .
in order to understand how to make the listview have the best performance and see in practice how and why it works as i've talked about , watch this video:
http://www.youtube.com/watch?v=wDBM6wVEO70
as for tags , i think you want to do something else , since the data itself (usually some sort of collection, like an arrayList) already knows where to update , because you get the position via the getView . if you want a specific view to update , you might be able to do so by using a hashmap that keeps upadting , which its key is the position in the collection , and the value is the associated view . on each time you go to getView , you need to remove the entry that belong to the view (if exists) and assign the new position with the view that you got/created .
Thanks for the answers. thisMayhem's answer would probably have been easier in the end, but on my quest to learn more I ended up making my own adapter according to this tutorial. I pass down the names and the IDs into the adapter and set the names as the text of the TextViews and the IDs as the tags.
I would rather go with the solution discussed in this thread. It is always the easiest to have all related data in same place and in this case you just create a class to hold all the information you will need for every item.
I've got what I thought was a simple android UI design problem but I've been going around in circles for a couple of days. I have a REST service that I'm downloading XML from and displaying the XML in a form in an android app. I have a web page built and am mimicking this with android, same options, same URLs being sent to the REST service whether from android or the web pages. With HTML I can easily create checkbox groups and radiobutton/dropdowns for various id/display items, so for instance, I can display a planet option as:
<select name="planet"><option value="0">Mercury</option></select>
I wanted to do something similar in android where I had a pair of values, one an id and the other the user-friendly text to display. So I decided to create an adapter using android.util.Pair:
public class PairView extends Pair<String, String> {
public PairView(String first, String second) {
super(first, second);
}
public String toString() {
return second;
}
}
public class PairAdapter extends ArrayAdapter<PairView> {
}
So now I can put my id in pair.first and what to display to the user in pair.second.
My problem comes in that some of these options will be single-selects and some will be multi-selects. In html, that's not an issue, just use a checkbox group for multi, and radio buttons/dropdowns for single selects. In android however, it seems it's not so straight forward. I tried using Spinners for the adapters, but Spinner seems to only allow single selection. AlertDialog.Builder allows for single and multi-selections, but curiously I don't see an option for using an adapter for the multi-selection, just for single selections.
I guess what I really want is a consistent look for all my options, with radio buttons displayed for single selections and checkboxes displayed for multi selections, via an adapter so I can get the id's from the Pair for the items selected.
What approach should I use? A custom spinner with code added for multi-selections? AlertDialog.Builder and somehow make it use an adapter for multi-selections? Just create a plain Alert and wrap a ListView in it? Another option that is (hopefully) simpler?
I feel like I'm missing something very basic here.
I had a similar situation in an app I was making so would share what I opted for. I had different type of questions and depending on that I removed and added things in my activity. For radio buttons I used with elements in it. For multiple choice questions I wanted a checkbox based view so I added an empty within my layout and in code added CheckBox(s) to it.
As for the caption and value, for radio buttons and checkboxes you can set display text by setText and add any object/value as a tag. So what I used to do was something like this:
CheckBox option = new CheckBox(MyActivity.this);
option.setText("Option 1");
option.setTag(10);
Later on when you get the selected option, you can simply get its tag and use its value.
This is just one way of doing it which I found simple. Hope this helps
As the title, how can I remove the filtering on an ArrayAdapter used by an AutoCompleteTextView to get the original list back?
A little more background:
It all started from the sad fact that the "position" value passed in to onItemClick() is useless. The "position" refers to the position AFTER the array has been filtered, but I need to know its REAL position. So, what I'm trying to do is when I've got the text of the selected item (by using getItemAtPosition(position)), I compare it one-by-one with the original string array that backs the ArrayAdapter. However, I found that when onItemClick() is called, the adapter is already filtered, I no longer have access to the original array. So I thought if I can remove the filter, maybe I can get back the original array and look for the selected item in it.
ArrayAdapter<String> mAdapter;
public void onCreate() {
// Create an adapter and remembere it as a class member.
mAdapter = new ArrayAdapter<String>(this, layoutId);
// Add 100 strings to it and attach it to an AutoCompleteTextView
for (int i = 0; i < 100; i++)
mAdapter.add("random text");
((AutoCompleteTextView)findViewById(id)).setAdapter(mAdapter);
}
#Override
public void onItemClick(AdapterView<?> actv, View view, int position, long id) {
if (actv.getAdapter().equals(mAdapter))
Log.d("The adapter contained in actv is the same one I created earlier.");
// And, I can get the text of the item the user selected
String selected = (String)actv.getItemAtPosition(position);
// However, although the adapter passed in is still the same one, but the
// number of items in it is only 1! Because the array has been filtered.
int numItems = actv.getAdapter.getCount();
// So, I'm thinking if I can somehow remove the filtering here, then I can
// get back those 100 items, and do a search like following:
for (int i = 0; i < actv.getAdapter.getCount(); i++)
if (selected == actv.getAdapter.getItem(i))
break; // Eureka!!!
}
To tackle the problem of obtaining the REAL position of the selected item:
Is there a way to utilize the "id" value? Like, assign each item an id, then hopefully onItemClick() would pass back the correct id.
Like I said above, remove the filter (is it possible), get back the original 100 items, and perform a one-by-one search.
This is the last resort, I know it'll work, but I don't want to do it: Once I get the text of the selected text, I go back to the source of the data (from a database), query those 100 items out, and perform the search.
Another lame last resort: To avoid the overhead on accessing the database again as in #3, when in onCreate(), while creating the ArrayAdapter, I use an ArrayList of my own to remember all those 100 strings.
Am I doing it all wrong? What's the "right" way of obtaining the real position of the selected item from an AutoCompleteTextView?
Thank you very much!
(I read somewhere, some buy that seemed to be from Google Android team, said that one should use getFirstVisiblePosition() to resolve the position. But I can't figure out how.)
I don't know if you're still interested, but I found this answering a similar question: Problem with AutoCompleteTextView and Spinner using the same Adapter
Copying the method in the AutoCompleteTextView source code:
Filter filter = mAdapter.getFilter();
filter = null;
See my response in the above question for the grepcode link.
This is actually pretty simple to solve.. Instead of adding each element to the adapter as you get it (I'm assuming your random text part is just for example purposes), instead use the following:
First build your array into a variable, call it myArray..
then initialize your adapter like this:
mAdapter = new ArrayAdapter<String>(this, layoutId, myArray);
Now make sure that myArray is a class variable so you can reference it from anywhere else in the class.. Of course if you need access to this from another class you'd want to make a getter for it... Then you can easily iterate over the array to see if the value selected is in the array.. You'll have the whole set of data there instead of trying to get it from the adapter.
Here is a good example on using a validator for a similar looking use case:
Android, Autocomplettextview, force text to be from the entry list
In my case, I have address that can be set either by autocomplete or clicking on the map. If user click on the map, editText text should be set to address selected from the map, and in that case filtering should be disable temporarily.
I code like this:
public void OnAddressFound(String address) {
// Temporary disable autocomplete
editTextSearch.setAdapter(null);
editTextSearch.setText(address);
// Enable autocomplete again
setAutoCompleteAdapter();
}
where setAutoCompleteAdapter() is called during onCreate, and again in temporary disable/enable filter:
void setAutoCompleteAdapter() {
PlacesAutoCompleteAdapter adapter = new PlacesAutoCompleteAdapter(this, R.layout.item_autocomplete_map_search, autoCompleteList);
editTextSearch.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
Hope its help you also.
I found the solution, kinnda tricky but its works
after i looking inside of the source, i found that th treshold variable used as filtering validation, here we just need to set the treshold to maximum int so filtering never perform.
threshold = Integer.MAX_VALUE
or
setThreshold(Integer.MAX_VALUE)
I'm having some trouble with the Spinner widget. Given the following code:
ArrayList<Person> people= new ArrayList<Person>();
Person = null;
for(int i = 0; i!= 10; i++) {
p = new Person();
s.setID(i);
s.setName("Name " + i);
people.add(s);
}
I'm using the following code to bind it to a Spinner:
Spinner spinner1 = (Spinner) findViewById (R.id.spinner);
ArrayAdapter<Person> adapter = new ArrayAdapter<Person>(this, android.R.layout.simple_spinner_item, people);
spinner1.setAdapter(adapter);
What I would like is for the value (id) to be hidden but passed when selected, and the name to appear. Any help is appreciated.
Thanks.
If I understand your question correctly you would like the name to appear in the spinner dropdown view. When an item is selected you would like to return the id.
There are 2 ways you can approach this.
The simplest way is to implement a toString() that returns the name in your Person object. The ArrayAdapter will return this value when binding the text values of your object to the dropdown view.
Then in your activity you can set the onItemClickListener for the Spinner and call on the adapter.getItemAtPosition(position) to retrieve the Person object. From that person object you can get your id.
The second approach is to extend the ArrayAdapter and implement the getView and getDropDownView and getItem methods. The getView is responsible for creating the rows you see when you click the spinner. The getDropDownView is responsible for creating the view you see in the spinner. And the getItem method will return the object or in your case the id at that specified position. You should bind the name of the Person element to a textview in your getView and getDropDownView methods. After you've created your custom ArrayAdapter you should set the onItemClicKListener for the Spinner and handle it just as I have mentioned above.
The first approach is simple but the second approach is far superior and will yield much greater control over your adapter, especially when you begin to develop something much more complex.
Hope that helps.
very old post -- just came across looking for something else -- noiced a mistake (probably just oversight)
jagsund is mostly right, accept, he got the definion of getView and GetDropDownView wrong - its actually the opposite of what he said : getView shows the content inside the spinner control, getDropDownView creates the view rows that you see when you click on the spinner.
So, unless you click on the spinner, getDropDownView will not even get called - you'll see this if you override ArrayAdapter