android - एक नए तत्काल स्पिनर पर फायरिंग से इटैम को कैसे रखा जाए?




spinner android-spinner (20)

मैंने इसे हल करने के कुछ शानदार तरीकों से सोचा है, लेकिन मुझे पता है कि मुझे कुछ याद आना चाहिए।

मेरा onItemSelected गए उपयोगकर्ता के साथ किसी भी बातचीत के बिना तुरंत आग लगती है, और यह अवांछित व्यवहार है। मैं यूआई की प्रतीक्षा करना चाहता हूं जब तक कि उपयोगकर्ता कुछ भी करने से पहले कुछ चुनता न हो।

मैंने onResume() में श्रोता को स्थापित करने की भी कोशिश की, उम्मीद है कि इससे मदद मिलेगी, लेकिन ऐसा नहीं है।

उपयोगकर्ता नियंत्रण को छूने से पहले मैं इसे कैसे फायरिंग से रोक सकता हूं?

public class CMSHome extends Activity { 

private Spinner spinner;

@Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Heres my spinner ///////////////////////////////////////////
    spinner = (Spinner) findViewById(R.id.spinner);
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
            this, R.array.pm_list, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);
    };

public void onResume() {
    super.onResume();
    spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}

    public class MyOnItemSelectedListener implements OnItemSelectedListener {

    public void onItemSelected(AdapterView<?> parent,
        View view, int pos, long id) {

     Intent i = new Intent(CMSHome.this, ListProjects.class);
     i.putExtra("bEmpID", parent.getItemAtPosition(pos).toString());
        startActivity(i);

        Toast.makeText(parent.getContext(), "The pm is " +
          parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
    }

    public void onNothingSelected(AdapterView parent) {
      // Do nothing.
    }
}
}

Runnables का उपयोग पूरी तरह से गलत है।

setSelection(position, false); प्रयोग करें setSelection(position, false); setOnItemSelectedListener(listener) से पहले प्रारंभिक चयन में

इस तरह आप अपने चयन को एनीमेशन के साथ सेट करते हैं जो आइटम को चयनित श्रोता कहलाता है। लेकिन श्रोता शून्य है इसलिए कुछ भी नहीं चल रहा है। फिर आपका श्रोता सौंपा गया है।

तो इस सटीक अनुक्रम का पालन करें:

Spinner s = (Spinner)Util.findViewById(view, R.id.sound, R.id.spinner);
s.setAdapter(adapter);
s.setSelection(position, false);
s.setOnItemSelectedListener(listener);

एक बुलियन ध्वज या काउंटर के साथ समाधान ने मेरी मदद नहीं की, 'इटैम चयन () पर अभिविन्यास परिवर्तन के दौरान कारण ध्वज या काउंटर को "ओवरफ्लू" कहते हैं।

मैंने android.widget.Spinner subclassed और छोटे जोड़ों बनाया। प्रासंगिक भाग नीचे हैं। यह समाधान मेरे लिए काम किया।

private void setHandleOnItemSelected()
{
  final StackTraceElement [] elements = Thread.currentThread().getStackTrace();

  for (int index = 1; index < elements.length; index++)
  {
     handleOnItemSelected = elements[index].toString().indexOf("PerformClick") != -1; //$NON-NLS-1$

     if (handleOnItemSelected)
     {
        break;
     }
  }
}

@Override
public void setSelection(int position, boolean animate)
{
  super.setSelection(position, animate);

  setHandleOnItemSelected();
}

@Override
public void setSelection(int position)
{
  super.setSelection(position);

  setHandleOnItemSelected();
}

public boolean shouldHandleOnItemSelected()
{
  return handleOnItemSelected;
}

ऐसा होगा यदि आप कोड में चयन कर रहे हैं;

   mSpinner.setSelection(0);

उपरोक्त कथन के उपयोग के बजाय

   mSpinner.setSelection(0,false);//just simply do not animate it.

संपादित करें: यह विधि एमआई एंड्रॉइड वर्जन एमआई यूआई के लिए काम नहीं करती है।


चूंकि मेरे लिए कुछ भी काम नहीं करता है, और मेरे विचार में 1 से अधिक स्पिनर हैं (और आईएमएचओ एक बूल मैप धारण करना एक ओवरकिल है) मैं क्लिक को गिनने के लिए टैग का उपयोग करता हूं:

spinner.setTag(0);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Integer selections = (Integer) parent.getTag();
            if (selections > 0) {
                // real selection
            }
            parent.setTag(++selections); // (or even just '1')
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        }
    });

दान डायर के जवाब का जिक्र करते हुए, एक post(Runnable) विधि में OnSelectListener को पंजीकृत करने का प्रयास करें:

spinner.post(new Runnable() {
    public void run() {
        spinner.setOnItemSelectedListener(listener);
    }
});

मेरे लिए यह कर कर कामना अंततः हुआ।

इस मामले में इसका भी अर्थ है कि श्रोता केवल बदले गए आइटम पर आग लगती है।


दुर्भाग्यवश ऐसा लगता है कि इस मुद्दे के दो सबसे अधिक सुझाए गए समाधान, अर्थात् कॉलबैक घटनाओं की गणना करना और बाद में कॉलबैक सेट करने के लिए एक रननेबल पोस्ट करना दोनों विफल हो सकते हैं जब उदाहरण के लिए अभिगम्यता विकल्प सक्षम होते हैं। यहां एक सहायक वर्ग है जो इन मुद्दों के आसपास काम करता है। टिप्पणी ब्लॉक में आगे की व्याख्या है।

import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;

/**
 * Spinner Helper class that works around some common issues 
 * with the stock Android Spinner
 * 
 * A Spinner will normally call it's OnItemSelectedListener
 * when you use setSelection(...) in your initialization code. 
 * This is usually unwanted behavior, and a common work-around 
 * is to use spinner.post(...) with a Runnable to assign the 
 * OnItemSelectedListener after layout.
 * 
 * If you do not call setSelection(...) manually, the callback
 * may be called with the first item in the adapter you have 
 * set. The common work-around for that is to count callbacks.
 * 
 * While these workarounds usually *seem* to work, the callback
 * may still be called repeatedly for other reasons while the 
 * selection hasn't actually changed. This will happen for 
 * example, if the user has accessibility options enabled - 
 * which is more common than you might think as several apps 
 * use this for different purposes, like detecting which 
 * notifications are active.
 * 
 * Ideally, your OnItemSelectedListener callback should be
 * coded defensively so that no problem would occur even
 * if the callback was called repeatedly with the same values
 * without any user interaction, so no workarounds are needed.
 * 
 * This class does that for you. It keeps track of the values
 * you have set with the setSelection(...) methods, and 
 * proxies the OnItemSelectedListener callback so your callback
 * only gets called if the selected item's position differs 
 * from the one you have set by code, or the first item if you
 * did not set it.
 * 
 * This also means that if the user actually clicks the item
 * that was previously selected by code (or the first item
 * if you didn't set a selection by code), the callback will 
 * not fire.
 * 
 * To implement, replace current occurrences of:
 * 
 *     Spinner spinner = 
 *         (Spinner)findViewById(R.id.xxx);
 *     
 * with:
 * 
 *     SpinnerHelper spinner = 
 *         new SpinnerHelper(findViewById(R.id.xxx))
 *         
 * SpinnerHelper proxies the (my) most used calls to Spinner
 * but not all of them. Should a method not be available, use: 
 * 
 *      spinner.getSpinner().someMethod(...)
 *
 * Or just add the proxy method yourself :)
 * 
 * (Quickly) Tested on devices from 2.3.6 through 4.2.2
 * 
 * @author Jorrit "Chainfire" Jongma
 * @license WTFPL (do whatever you want with this, nobody cares)
 */
public class SpinnerHelper implements OnItemSelectedListener {
    private final Spinner spinner;

    private int lastPosition = -1;
    private OnItemSelectedListener proxiedItemSelectedListener = null;  

    public SpinnerHelper(Object spinner) {
         this.spinner = (spinner != null) ? (Spinner)spinner : null;        
    }

    public Spinner getSpinner() {
        return spinner;
    }

    public void setSelection(int position) { 
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position);     
    }

    public void setSelection(int position, boolean animate) {
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position, animate);        
    }

    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        proxiedItemSelectedListener = listener;
        spinner.setOnItemSelectedListener(listener == null ? null : this);
    }   

    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (position != lastPosition) {
            lastPosition = position;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onItemSelected(
                        parent, view, position, id
                );
            }
        }
    }

    public void onNothingSelected(AdapterView<?> parent) {
        if (-1 != lastPosition) {
            lastPosition = -1;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onNothingSelected(
                        parent
                );
            }
        }
    }

    public void setAdapter(SpinnerAdapter adapter) {
        if (adapter.getCount() > 0) {
            lastPosition = 0;
        }
        spinner.setAdapter(adapter);
    }

    public SpinnerAdapter getAdapter() { return spinner.getAdapter(); } 
    public int getCount() { return spinner.getCount(); }    
    public Object getItemAtPosition(int position) { return spinner.getItemAtPosition(position); }   
    public long getItemIdAtPosition(int position) { return spinner.getItemIdAtPosition(position); }
    public Object getSelectedItem() { return spinner.getSelectedItem(); }
    public long getSelectedItemId() { return spinner.getSelectedItemId(); }
    public int getSelectedItemPosition() { return spinner.getSelectedItemPosition(); }
    public void setEnabled(boolean enabled) { spinner.setEnabled(enabled); }
    public boolean isEnabled() { return spinner.isEnabled(); }
}

मुझे इसके लिए और अधिक सुरुचिपूर्ण समाधान मिला है। इसमें गिनती शामिल है कि ArrayAdapter (आपके मामले में "एडाप्टर") कितनी बार लागू किया गया है। मान लें कि आपके पास 1 स्पिनर है और आप कॉल करते हैं:

int iCountAdapterCalls = 0;

ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
            this, R.array.pm_list, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);

ऑनक्रेट के बाद एक इंट काउंटर घोषित करें और उसके बाद इटैम सेलेक्टेड () विधि में "if" स्थिति डालें ताकि यह पता चल सके कि कितनी बार एडेप्टर बुलाया गया है। आपके मामले में आपने इसे केवल एक बार कहा है:

if(iCountAdapterCalls < 1)
{
  iCountAdapterCalls++;
  //This section executes in onCreate, during the initialization
}
else
{
  //This section corresponds to user clicks, after the initialization
}

मुझे एक बहुत ही सरल जवाब मिला, 100% यकीन है कि यह काम करता है:

boolean Touched=false; // this a a global variable

public void changetouchvalue()
{
   Touched=true;
}

// this code is written just before onItemSelectedListener

 spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            changetouchvalue();
            return false;
        }
    });

//inside your spinner.SetonItemSelectedListener , you have a function named OnItemSelected iside that function write the following code

if(Touched)
{
 // the code u want to do in touch event
}

मैं आपके समाधान के लिए काम करने की उम्मीद करता था - हालांकि, अगर आप श्रोता सेट अप करने से पहले एडाप्टर सेट करते हैं तो चयन ईवेंट आग नहीं लगेगा।

ऐसा कहा जा रहा है कि, एक साधारण बूलियन झंडा आपको दुष्ट पहले चयन कार्यक्रम का पता लगाने और इसे अनदेखा करने की अनुमति देगा।


मैं इसी तरह की स्थिति में था, और मेरे पास मेरे लिए काम करने का एक आसान समाधान है।

ऐसा लगता है कि विधियों setSelection(int position) और setSelected(int position, boolean animate) में अलग-अलग आंतरिक कार्यान्वयन होते हैं।

जब आप झूठी एनिमेट ध्वज के साथ दूसरी विधि setSelected(int position, boolean animate) उपयोग करते हैं, तो आपको setSelected(int position, boolean animate) श्रोता पर गोलीबारी किए बिना चयन मिलता है।


मैंने उपयोगकर्ता को सूचित किए बिना Spinner चयन को बदलने के लिए एक छोटी उपयोगिता विधि बनाई:

private void setSpinnerSelectionWithoutCallingListener(final Spinner spinner, final int selection) {
    final OnItemSelectedListener l = spinner.getOnItemSelectedListener();
    spinner.setOnItemSelectedListener(null);
    spinner.post(new Runnable() {
        @Override
        public void run() {
            spinner.setSelection(selection);
            spinner.post(new Runnable() {
                @Override
                public void run() {
                    spinner.setOnItemSelectedListener(l);
                }
            });
        }
    });
}

यह श्रोता को अक्षम करता है, चयन में परिवर्तन करता है, और उसके बाद श्रोता को फिर से सक्षम करता है।

चाल यह है कि कॉल यूआई थ्रेड के लिए असीमित हैं, इसलिए आपको इसे लगातार हैंडलर पोस्ट में करना होगा।


यह या तो एक सुरुचिपूर्ण समाधान नहीं है। असल में यह रूबे-गोल्डबर्ग है लेकिन ऐसा लगता है कि यह काम करता है। I make sure the spinner has been used at least once by extending the array adapter and overriding its getDropDownView. In the new getDropDownView method I have a boolean flag that is set to show the dropdown menu has been used at least once. I ignore calls to the listener until the flag is set.

MainActivity.onCreate():

ActionBar ab = getActionBar();
ab.setDisplayShowTitleEnabled(false);
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
ab.setListNavigationCallbacks(null, null);

ArrayList<String> abList = new ArrayList<String>();
abList.add("line 1");
...

ArAd  abAdapt = new ArAd (this
   , android.R.layout.simple_list_item_1
   , android.R.id.text1, abList);
ab.setListNavigationCallbacks(abAdapt, MainActivity.this);

overriden array adapter:

private static boolean viewed = false;
private class ArAd extends ArrayAdapter<String> {
    private ArAd(Activity a
            , int layoutId, int resId, ArrayList<String> list) {
        super(a, layoutId, resId, list);
        viewed = false;
    }
    @Override
    public View getDropDownView(int position, View convertView,
            ViewGroup parent) {
        viewed = true;
        return super.getDropDownView(position, convertView, parent);
    }
}

modified listener:

@Override
public boolean onNavigationItemSelected(
   int itemPosition, long itemId) {
   if (viewed) {
     ...
   }
   return false;
}

लेआउट चरण से कोई अवांछित घटना नहीं है यदि आप लेआउट समाप्त होने तक श्रोता जोड़ने को रोकते हैं:

spinner.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // Ensure you call it only once works for JELLY_BEAN and later
            spinner.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            // add the listener
            spinner.setOnItemSelectedListener(new OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                    // check if pos has changed
                    // then do your work
                }

                @Override
                public void onNothingSelected(AdapterView<?> arg0) {
                }

            });

        }
    });

सेटऑनइटम चयनकर्ता (जो गतिविधि प्रारंभिकरण आदि का हिस्सा हैं) के लिए स्वचालित कॉल के बीच अंतर करने के लिए ऑन टचलिस्टर का उपयोग करने के लिए संकेतों को दूर करने के लिए बस वास्तविक उपयोगकर्ता इंटरैक्शन द्वारा ट्रिगर किए गए कॉल के लिए, मैंने यहां कुछ अन्य सुझावों को आजमाने के बाद निम्नलिखित किया है और पाया कि यह कोड की सबसे कम लाइनों के साथ अच्छी तरह से काम किया।

बस अपनी गतिविधि / टुकड़े के लिए एक बूलियन फ़ील्ड सेट करें जैसे:

private Boolean spinnerTouched = false;

फिर अपने स्पिनर के सेटऑनटिम चयन किए गए लिस्टर को सेट करने से ठीक पहले, एक टचलिस्टर सेट करें:

    spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            spinnerTouched = true;
            return false;
        }
    });

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    ...
         if (spinnerTouched){
         //Do the stuff you only want triggered by real user interaction.
        }
        spinnerTouched = false;

I might be answering too late over the post, however I managed to achieve this using Android Data binding library Android Databinding . I created a custom binding to make sure listener is not called until selected item is changed so even if user is selecting same position over and over again event is not fired.

Layout xml file

    <layout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_vertical_margin"
xmlns:app="http://schemas.android.com/apk/res-auto">


<Spinner
    android:id="@+id/spinner"
    android:layout_width="150dp"
    android:layout_height="wrap_content"
    android:spinnerMode="dropdown"
    android:layout_below="@id/member_img"
    android:layout_marginTop="@dimen/activity_vertical_margin"
    android:background="@drawable/member_btn"
    android:padding="@dimen/activity_horizontal_margin"
    android:layout_marginStart="@dimen/activity_horizontal_margin"
    android:textColor="@color/colorAccent"
    app:position="@{0}"
    />
 </RelativeLayout>
 </layout>

app:position is where you are passing position to be selected.

Custom binding

  @BindingAdapter(value={ "position"}, requireAll=false)
  public static void setSpinnerAdapter(Spinner spinner, int selected) 
  {

    final int [] selectedposition= new int[1];
    selectedposition[0]=selected;


    // custom adapter or you can set default adapter
        CustomSpinnerAdapter customSpinnerAdapter = new CustomSpinnerAdapter(spinner.getContext(), <arraylist you want to add to spinner>);
        spinner.setAdapter(customSpinnerAdapter);
            spinner.setSelection(selected,false);


    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            String item = parent.getItemAtPosition(position).toString();
        if( position!=selectedposition[0]) {
                        selectedposition[0]=position;
            // do your stuff here
                    }
                }


        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    });
}

You can read more about custom data binding here Android Custom Setter

ध्यान दें

  1. Don't forget to enable databinding in your Gradle file

       android {
     ....
     dataBinding {
     enabled = true
    }
    }
    
  2. Include your layout files in <layout> tags


I need to use mSpinner in ViewHolder, so the flag mOldPosition is set in the anonymous inner class.

mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            int mOldPosition = mSpinner.getSelectedItemPosition();

            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long l) {
                if (mOldPosition != position) {
                    mOldPosition = position;
                    //Do something
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
                //Do something
            }
        });

My solution uses onTouchListener but doesn't restricts from its use. It creates a wrapper for onTouchListener if necessary where setup onItemSelectedListener .

public class Spinner extends android.widget.Spinner {
    /* ...constructors... */

    private OnTouchListener onTouchListener;
    private OnItemSelectedListener onItemSelectedListener;

    @Override
    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        onItemSelectedListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    @Override
    public void setOnTouchListener(OnTouchListener listener) {
        onTouchListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    private OnTouchListener wrapTouchListener(final OnTouchListener onTouchListener, final OnItemSelectedListener onItemSelectedListener) {
        return onItemSelectedListener != null ? new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                Spinner.super.setOnItemSelectedListener(onItemSelectedListener);
                return onTouchListener != null && onTouchListener.onTouch(view, motionEvent);
            }
        } : onTouchListener;
    }
}

That's my final and easy to use solution :

public class ManualSelectedSpinner extends Spinner {
    //get a reference for the internal listener
    private OnItemSelectedListener mListener;

    public ManualSelectedSpinner(Context context) {
        super(context);
    }

    public ManualSelectedSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ManualSelectedSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
        mListener = listener;
        super.setOnItemSelectedListener(listener);
    }

    public void setSelectionWithoutInformListener(int position){
        super.setOnItemSelectedListener(null);
        super.setSelection(position);
        super.setOnItemSelectedListener(mListener);
    }

    public void setSelectionWithoutInformListener(int position, boolean animate){
        super.setOnItemSelectedListener(null);
        super.setSelection(position, animate);
        super.setOnItemSelectedListener(mListener);
    }
}

Use the default setSelection(...) for default behaviour or use setSelectionWithoutInformListener(...) for selecting an item in the spinner without triggering OnItemSelectedListener callback.


if () {        
       spinner.setSelection(0);// No reaction to create spinner !!!
     } else {
        spinner.setSelection(intPosition);
     }


spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

         if (position > 0) {
           // real selection
         }

      }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

     }
});

mYear.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View arg1, int item, long arg3) {
                if (mYearSpinnerAdapter.isEnabled(item)) {

                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });




android-spinner