I've a Spinner with onItemSelected interation that works, but how the Api specification says:
This callback is invoked only when the newly selected position is
different from the previously selected position or if there was no
selected item.
I need to remove this limitation and i want that the callback is invoked also if the user select the same element.
How to do that? I read a suggestion about extending Spinner class and set the position to INVALID_POSITION, but i've not understood/able to do that.
Anyone did the same thing?
I also needed a solution to this problem. What I wanted to do was have a Spinner that has date ranges with a custom range option. The rows would look something like this:
Apr 10 - May 10
Mar 10 - Apr 10
Feb 10 - Mar 10
Custom Range
The problem is that if the user selects a custom range and then wants to change their custom range, they have to select a different range and then select the custom range option again. I wanted the user to just be able to select "Custom Range" again so that the custom range dialog could be shown again.
I sub-classed Spinner and created my own listener. The code switches the selection, but then immediately switches it so that nothing is selected. In my listener I just ignore any position that is less than zero.
The Spinner just displays the last selected item. I created my own custom adapter and specify what to display for each view, but that shouldn't be necessary. Here is how I sub-classed Spinner.
package com.example.widget;
import android.content.Context;
import android.widget.Spinner;
public class DateRangeSpinner extends Spinner {
private ItemSelectionListener listener;
public DateRangeSpinner(Context context) {
super(context);
}
/**
* This listener will be fired every time an item is selected,
* regardless of whether it has already been selected or not.
*
* #param l
*/
public void setOnItemSelectedListener(ItemSelectionListener l) {
listener = l;
}
public void removeOnItemSelectedListener() {
listener = null;
}
#Override
public void setSelection(int position) {
setSelection(position, true);
}
#Override
public void setSelection(int position, boolean animate) {
if (listener != null) {
listener.onItemSelected(position);
}
super.setSelection(position, animate);
super.setSelection(-1, animate);
}
public interface ItemSelectionListener {
public void onItemSelected(int position);
}
}
I hope this helps!
You can do this by a custom adapter, like create a layout of your desire views, then inflate this in custom adapter then on onItemClick function you can get the view by this function.
To distinguish each view you must to set the tag of each row.
It probably works in your condition.
Let me know any issue if you have
why you have select the selected item again. Just give a refresh button if you want to perform that task again.
This is the spinner Element with custom dialog mode and whitout promt:
<Spinner
android:id="#+id/spinner_metatag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="#array/search_adv"
/>
The Array element where the default value is putted at position 0:
<string-array name="search_adv">
<item>#string/search_adv_prompt</item>
<item>#string/search_adv_title</item>
<item>#string/search_adv_desc</item>
<item>#string/search_adv_autore</item>
....
</string-array>
The String elements for the array with the default value:
<string name="search_adv_prompt">Scegli un metatag</string> <!-- Default value-->
<string name="search_adv_title">Titolo</string>
<string name="search_adv_desc">Descrizione</string>
<string name="search_adv_autore">Autore</string>
...
And here the code to prevent the event fired on onCreateMethod and the work around to permit to select the same element already selected:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_adv_main);
spinner = (Spinner) findViewById(R.id.spinner_metatag);
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
//prevent onCreate event fire and the loop
if(pos==0)
return;
//HERE YOUR CODE
//at the end move to the default element the spinner
spinner.setSelection(0);
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {}
});
}
Hope helps. The idea come from the second solution from TreKing's answer
Use OnItemClickListener instead of on itemSelectedListener.That will work for every click whether it is same or different.
I guess you must be storing the value in a variable, initialize the vairable with -1. And change the value as the user selects the item spinner, if the value is -1 ask the user to reselect or whatever u want.
#i want that the callback is invoked also if the user select the same element.
Android will do it for u as this is the default behavior of android.
Related
I am making a single choice list inside a dialog.
I have completed all the basic functionalities like added a setonitemclicklistener but what i want is to set the first item of list by default, but when i use setSelection(1) it does not work:
i have done :
private void resetPosition(final ListView lv) {
lv.post(new Runnable() {
#Override
public void run() {
lv.setSelection(1);
}
});
}
and i am calling this function on button click which will start this dialog,
What is wrong with this code?
Documentation of setSelection() shows a line "If in touch mode, the item will not be selected but it will still be positioned appropriately" is this the fault?
If you need item selection, you should allow list items to be checked. You need to set the choice mode of the ListView to CHOICE_MODE_SINGLE using ListView.setChoiceMode(CHOICE_MODE_SINGLE).
Then you need to use ListView.setItemChecked(int position, boolean checked) method as ListView.setItemChecked(0, true) to set the first position as selected.
Also see: http://www.vogella.com/articles/AndroidListView/article.html#listviewselection
I make a spinner put the value in spinner using array,now even I was not select any value from spinner it automatically takes first value from spinner array, I want to set default value when nothing is selected form spinner array by user,means I get default value which I set ,when I was not selected any value, is this possible, and what is the use of onNothingSelected(AdapterView ....below code should run when user select manually any value from spinner but it run always and get first value which is in array,so please tell how to get default value when I nothing selected from spinner, can I use on nothing selected method..?
ArrayAdapter<String> CurrencyAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line, Currency);
currency.setAdapter(CurrencyAdapter);
currency.setOnItemSelectedListener( new AdapterView.OnItemSelectedListener()
{
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
}
#Override
public void onNothingSelected(AdapterView<?> parent)
{
}
});
Better set first value of array,the value what you want to set as default. And then you can check whether that value is selected or something else to figure out,is spinner selected or not??
A related question I asked a couple of days ago could help you here, have a look at How to use Android Spinner like a drop-down list and see some of the answers which I found helpful.
And no, onNothingSelected() does not do what you're after... you want to have a look at the methods setSelection() and setSelectionInt() in the question I referred to.
i have created two spinners both of different class.i want to set content of one spinner based on selected value of other spinner which is having in other class.can any one help me.thanks in advance.
There is a solution,you might feel it lengthy to implement but it would perfectly work for you,i think!
Lets say you have spinner_one in your ActivityOne.class and spinner_two in your ActivityTwo.class. And you want to populate spinner_2 based on what is selected in spinner_1.
Then,
spinner_1.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
// save your selected item into a SharedPreference Variable
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
Now,for spinner_2:
String spinner1_value=get value from SharedPreference;
if(spinner1_value.equals("something_1"))
{
//populate spinner_2 accordingly
}
else if(spinner1_value.equals("something_2"))
{
//populate spinner_2 accordingly
}
else
{
//populate spinner_2 accordingly
}
And don't forget to put this code to populate spinner_2 in onResume() of your ActivityTwo.class,so that it will reflect the changes user made in spinner_1 value.
This will allow you to change spinner_2 content according to spinner_1 value as and when user changes it.
The short answer is that you can do this programatically - either by assigning a new ArrayAdapter to the Spinner, or by removing/adding items from the existing ArrayAdapter.
The longer answer is that you have to be very careful in relying on OnItemSelectedListener.onItemSelected(), since it will not get called if the old selection and a new selection happen to be in the same position, and Spinner.getSelectedItemPosition(), since it can actually return positions greater than the number of items in the ArrayAdapter if you're removing items on the fly.
I have a system of three cascaded Spinners, which in turn can drive the contents of other Buttons and Texts. I was able to get 95% solutions until I recognized the above and changed my view of what was authoritative and what was not - by making the Spinner subservient to the logic that determines content, rather than the other way around. So rather than calling setSelection() and relying on the onItemSelected() callback, do all your cascading logic outside the handler, and only then call setSelection() and return back up the chain.
Adapted from my working code:
class Spinster extends Activity {
...
private void setSpinnerOne( int pos ) {
// 1. Do our own chores, etc
doSomeStuff();
mSomeText.setText( "Blah!" );
mSomeButton.setEnabled( some_condition );
// 2. Populate dependent Spinner based on pos
populateSpinnerTwoAdapter( pos );
// 3. Cascade by calling SpinnerTwo logic (not the Spinner itself)
lSpinnerTwoPos = someNiceFunction();
setSpinnerTwo( lSpinnerTwoPos );
// 4. Now set SpinnerOne
mSpinnerTwo.setSelection( pos );
}
private void setSpinnerTwo( int pos ) {
// Follows the same pattern as setSpinnerOne(), but cascades to SpinnerThree
}
private void setSpinnerThree( int pos ) {
// Follows the same pattern as setSpinnerOne(), but need not cascade
}
...
private OnItemSelectedListener item_select = new OnItemSelectedListener() {
public void onItemSelected( AdapterView parent, View v, int position, long id )
{
...
int lId = parent.getId();
if ( lId == R.id.spinner_one ) {
setSpinnerOne( position );
} else if ( lId == R.id.spinner_two ) {
setSpinnerTwo( position );
} else if ( lId == R.id.spinner_three ) {
setSpinnerThree( position );
}
}
}
}
I've left out a small detail here, that of setting and clearing a guard variable in the setSpinner*() methods so that they don't bother doing all their work again when onSelectedItemListener() calls them back. I think the basic logic is easier to show by leaving it out. (And they don't strictly need to be there - onItemSelected() will not be called the second time round.)
I could post a real, working example if this is deemed to abstract.
I want to have three Spinners with contents which depend on each other.
E.g. Spinner1 displays {item1, item2} and Spinner2 either {item3, item4} or {item5, item6} depending on whether item1 or item2 is selected on Spinner1.
The same I want for Spinner 3, which reacts to changes of Spinner1 and/or Spinner2.
For the latter, I have to determine first which of the possible value sets is shown atm in Spinner2.
My question is kind of similar to this question, but I don't know what to do after getting the adapter.
That's what I have so far:
ArrayAdapter adapter1 = (ArrayAdapter) spinner2.getAdapter();
if(items_spinner1[0].contentEquals(adapter1.getItem(0)))
{
//...
}
I get the adapter, ask for the first value and compare it to the first String value of my Array to identify it. It doesn't at all seem elegant to me. Is there an easier solution?
You say the contents of the later spinners depend on the selection in the earlier ones, but the code you posted depends only on the contents of a spinner.
adapter1.getItem(0) returns the first item in the list, not the currently selected item. To get the currently selected item, use the spinner's (not the adapter's) getSelectedItem() method.
You could, for example, put something like this in your first Spinner's onItemSelectedListener (edited based on your comment below):
public void onItemSelected (AdapterView<?> parent, View view, int position, long id) {
Object selectedItem = parent.getSelectedItem();
// Do this if the first Spinner has a set of options that are
// known in advance.
if (/*selectedItem is something*/) {
// Set up the second Spinner in some way
} else if (/*selectedItem is something else*/) {
// Set up the second Spinner in another way
}
// OR, if you need to do something more complex
// that would cause too much clutter here, do this.
fillSecondSpinner(selectedItem);
}
Then place something similar in the second Spinner's onItemSelectedListener. Get the selected items from the first and second Spinners using getSelectedItem() (or the item positions using getSelectedItemId() for the first and the position parameter for the second). Use the selected items to set up the third.
Edit: The OnItemSelectedListener for the second Spinner would look something like this.
// This must be defined in the enclosing scope.
final Spinner firstSpinner; // Must be final to be accessible from inner class.
Spinner secondSpinner;
// ...
secondSpinner.setOnItemSelectedListener(new OnItemSelectedListener {
public void onItemSelected (AdapterView<?> parent, View view, int position, long id) {
// Again, usually the selected items should be of
// a more specific type than Object.
Object firstSelection = firstSpinner.getSelectedItem();
Object secondSelection = parent.getSelectedItem();
fillThirdSpinner(firstSelection, secondSelection);
}
public void onNothingSelected (AdapterView<?> parent) { }
});
I'm in a trouble managing a spinner, so may I ask for your help ?
I have a spinner with its adapter.
I initialize the spinner with a list of values when starting my activity.
Then I force the selected value to be the one used in the object that I manage.
Once the screen is initialized :
When the user selects a value in the spinner, according to the selected value, I may continue (or not) to another activity for let the user choose a complementary and necessary value.
If the user "cancels" this second activity, I want to rollback the spinner to its previous selected value, and cancel some actions made in the meantime.
If the user goes to the end of the second activity, everything is fine and I want juste to refresh the spinner display with the datas selected in the second activity (I overload the getView method in the adapter to do this).
Overall, I can easily do all of this, however, when I force the selected value in the spinner at the begining of my activity, or whene returning back from the second activity by "Cancel", the change value event is catched and the second activity is triggered (the user did not click anything at all).
How would you allow the second activity to be lauched only if the change of the selected value in the spinner is due to a manual action from the user, and prevent that same second activity to be launched when the value of spinner is changed "in the code "?
I tried many solutions, as setting a boolean into the adapter that tells if the next event will be raised because of an "in the code" action.
Or also putting a boolean in the adapter that tells if the adapter has initialised itself, and I force that boolean to true on the forst change catched event.
But nothing that really works fine.
Thank you for your help.
Oliver
I've always solved that issue with boolean flags, it isnt pretty at all, but it works if you think it through.
The idea is more or less, create a global usable boolean and init with false, in the onSelectedItemListener() use that boolean to choose wether or not to trigger the action, the important thing to remember is to set it to true after the computer has selected it the first time automatically, and reset it to false in the onResume() method.
This isnt perfect but it should work.
Edit:
bool spinnerUsable1;
bool spinnerUsable2;
int positionSpinner;
public void onCreate(Bundle savedInstanceState){
spinnerUsable1 = false;
spinnerUsable2 = true;
if(savedInstanceState != null){
positionSpinner = savedInstanceState.getInt("posSpinner");
if(positionSpinner != 0) spinnerUsable2 = false;
}
//Declare your spinner, set the on item selected lister
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
boolean spinnerUsable = (spinnerUsable1 && spinnerUsable2);
if (!spinnerUsable1) {
spinnerUsable1 = true;
} else if (!spinnerUsable2) {
spinnerUsable2 = true;
}
if (spinnerUsable) {
//Action;
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// Nothing
}
});
}
Something like this should work.