كيفية الحفاظ على onItemSelected من التشغيل على Spinner الذي تم إنشاء مثيل له حديثًا؟

StackOverflow https://stackoverflow.com/questions/2562248

سؤال

لقد فكرت في بعض الطرق الأقل أناقة لحل هذه المشكلة، لكنني أعلم أنني لا بد أن أفتقد شيئًا ما.

لي 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); في الاختيار الأولي من قبل setOnItemSelectedListener(listener)

وبهذه الطريقة ، قمت بتعيين اختيارك بدون رسوم متحركة تسبب استدعاء المستمع المحدد على العنصر. لكن المستمع فارغ ، لذا لا يتم تشغيل شيء. ثم يتم تعيين المستمع الخاص بك.

لذا اتبع هذا التسلسل الدقيق:

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

في إشارة إلى إجابة دان داير ، حاول تسجيل OnSelectListener في post(Runnable) طريقة:

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

من خلال القيام بذلك بالنسبة لي ، حدث السلوك المريح أخيرًا.

في هذه الحالة ، يعني ذلك أيضًا أن المستمع يطلق النار فقط على عنصر تم تغييره.

لقد قمت بإنشاء طريقة فائدة صغيرة للتغيير 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);
                }
            });
        }
    });
}

إنه يعطل المستمع ، ويغير التحديد ، ويعيد تخصيص المستمع بعد ذلك.

الحيلة هي أن المكالمات غير متزامنة لخيط واجهة المستخدم ، لذلك عليك القيام بذلك في منشورات معالج متتالية.

لسوء الحظ ، يبدو أن الحلول الأكثر شيوعًا لهذه المشكلة ، وهما حساب حوادث رد الاتصال ونشر عملية إعادة الاتصال في وقت لاحق يمكن أن تفشل عندما يتم تمكين خيارات الوصول على سبيل المثال. إليك فصل مساعد يعمل حول هذه القضايا. مزيد من الاستكشاف في كتلة التعليق.

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(); }
}

لقد واجهت الكثير من المشكلات مع إطلاق الدوران عندما لم أكن أرغب في ذلك ، وكل الإجابات هنا غير موثوقة. إنهم يعملون - ولكن في بعض الأحيان فقط. سوف تصطدم في نهاية المطاف سيناريوهات حيث سيفشلون وتقديم الأخطاء في الكود الخاص بك.

ما نجح بالنسبة لي هو تخزين آخر فهرس محدد في متغير وتقييمه في المستمع. إذا كان ذلك هو نفسه الفهرس المحدد الجديد لا يفعل شيئًا وإرجاعه ، فاستمر في المستمع. افعل هذا:

//Declare a int member variable and initialize to 0 (at the top of your class)
private int mLastSpinnerPosition = 0;

//then evaluate it in your listener
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {

  if(mLastSpinnerPosition == i){
        return; //do nothing
  }

  mLastSpinnerPosition = i;
  //do the rest of your code now

}

صدقني عندما أقول هذا ، هذا هو الحل الأكثر موثوقية إلى حد بعيد. اختراق ، لكنه يعمل!

كنت في وضع مماثل ، ولدي حل بسيط يعمل بالنسبة لي.

يبدو وكأنه طرق setSelection(int position) و setSelected(int position, boolean animate) لديك تطبيق داخلي مختلف.

عندما تستخدم الطريقة الثانية setSelected(int position, boolean animate) مع علم متحرك كاذب ، تحصل على الاختيار دون إطلاق النار onItemSelected المستمع.

فقط لتجسيد تلميحات عند استخدام onTouchListener للتمييز بين المكالمات التلقائية إلى SetOnitemSelectedListener (والتي هي جزء من تهيئة النشاط ، وما إلى ذلك) مقابل المكالمات التي تسببت في تفاعل المستخدم الفعلي ، فعلت ما يلي بعد تجربة بعض الاقتراحات هنا و وجدت أنها تعمل بشكل جيد مع أقل عدد من خطوط التعليمات البرمجية.

ما عليك سوى تعيين حقل منطقي لنشاطك/جزءك مثل:

private Boolean spinnerTouched = false;

ثم قبل تعيين SetOnitemSelectener من Spinner الخاص بك ، قم بتعيين onTouchListener:

    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;
spinner.setSelection(Adapter.NO_SELECTION, false);

بعد سحب شعري لفترة طويلة الآن ، قمت بإنشاء فصل الدوار الخاص بي. لقد أضفت طريقة لها والتي تفصل وتوصيل المستمع بشكل مناسب.

public class SaneSpinner extends Spinner {
    public SaneSpinner(Context context) {
        super(context);
    }

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

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

    // set the ceaseFireOnItemClickEvent argument to true to avoid firing an event
    public void setSelection(int position, boolean animate, boolean ceaseFireOnItemClickEvent) {
        OnItemSelectedListener l = getOnItemSelectedListener();
        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(null);
        }

        super.setSelection(position, animate);

        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(l);
        }
    }
}

استخدمه في XML مثل هذا:

<my.package.name.SaneSpinner
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/mySaneSpinner"
    android:entries="@array/supportedCurrenciesFullName"
    android:layout_weight="2" />

كل ما عليك فعله هو استرداد مثيل Sanespinner بعد التضخم واختيار مجموعة المكالمات مثل هذا:

mMySaneSpinner.setSelection(1, true, true);

مع هذا ، لا يتم إطلاق أي حدث ولم ينقطع تفاعل المستخدم. هذا قلل من تعقيد الكود الخاص بي كثيرًا. يجب أن يتم تضمين هذا في الأسهم Android لأنه في الواقع هو بيتا.

لا توجد أحداث غير مرغوب فيها من مرحلة التخطيط إذا قمت بتأجيل إضافة المستمع حتى يتم الانتهاء من التخطيط:

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) {
                }

            });

        }
    });

سيحدث هذا إذا كنت تقوم بالتحديد في الكود AS ؛

   mSpinner.setSelection(0);

بدلا من استخدام البيان أعلاه

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

تحرير: هذه الطريقة لا تعمل مع إصدار Mi Android Mi UI.

حصلت على إجابة بسيطة للغاية ، متأكد 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
}

لقد وجدت حل أكثر أناقة لهذا. إنه ينطوي على حساب عدد المرات التي تم فيها استدعاء ArrayAdapter (في قضيتك "محول"). لنفترض أن لديك واحدة من الدوران وتتصل:

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);

أعلن أن عداد int بعد oncreate ثم داخل طريقة OnItemseced () وضعت شرط "if" للتحقق من عدد المرات التي تم فيها استدعاء atapter. في حالتك ، لقد دعوتها مرة واحدة فقط:

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

مساهمتي الصغيرة هي تباين في بعض ما سبق كان يناسبني عدة مرات.

إعلان متغير عدد صحيح كقيمة افتراضية (أو آخر قيمة تستخدم في التفضيلات). استخدم spinner.setselection (myDefault) لتعيين هذه القيمة قبل تسجيل المستمع. في OnItemseced ، تحقق مما إذا كانت قيمة الدوار الجديدة تساوي القيمة التي قمت بتعيينها قبل تشغيل أي رمز إضافي.

هذا له ميزة إضافية لعدم تشغيل التعليمات البرمجية إذا كان المستخدم يختار نفس القيمة مرة أخرى.

بعد أن واجهت نفس المشكلة ، جئت إلى هذه الحلول باستخدام العلامات. الفكرة وراءها بسيطة: كلما تم تغيير الدوار برمجيًا ، تأكد من أن العلامة تعكس الموضع المحدد. في المستمع ، يمكنك التحقق مما إذا كان الموضع المحدد يساوي العلامة. إذا كان الأمر كذلك ، فقد تم تغيير اختيار الدوار برمجيًا.

فيما يلي صفي الجديد "SPONNER Proxy":

package com.samplepackage;

import com.samplepackage.R;
import android.widget.Spinner;

public class SpinnerFixed {

    private Spinner mSpinner;

    public SpinnerFixed(View spinner) {
         mSpinner = (Spinner)spinner;
         mSpinner.setTag(R.id.spinner_pos, -2);
    }

    public boolean isUiTriggered() {
         int tag = ((Integer)mSpinner.getTag(R.id.spinner_pos)).intValue();
         int pos = mSpinner.getSelectedItemPosition();
         mSpinner.setTag(R.id.spinner_pos, pos);
         return (tag != -2 && tag != pos);
    }

    public void setSelection(int position) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position);
    }

    public void setSelection(int position, boolean animate) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position, animate);
    }

    // If you need to proxy more methods, use "Generate Delegate Methods"
    // from the context menu in Eclipse.
}

ستحتاج أيضًا إلى ملف XML مع إعداد العلامة في Values الدليل. لقد سميت ملفي spinner_tag.xml, ، لكن هذا متروك لك. تبدو هكذا:

<resources xmlns:android="http://schemas.android.com/apk/res/android">
  <item name="spinner_pos" type="id" />
</resources>

الآن استبدل

Spinner myspinner;
...
myspinner = (Spinner)findViewById(R.id.myspinner);

في الكود الخاص بك مع

SpinnerFixed myspinner;
...
myspinner = new SpinnerFixed(findViewById(R.id.myspinner));

وجعل معالجك يبدو مثل هذا:

myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (myspinner.isUiTriggered()) {
            // Code you want to execute only on UI selects of the spinner
        }
    }

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

الوظيفة isUiTriggered() سيعود صحيح إذا وفقط إذا تم تغيير الدوار من قبل المستخدم. لاحظ أن هذه الوظيفة لها تأثير جانبي - ستقوم بتعيين العلامة ، لذا فإن المكالمة الثانية في نفس مكالمة المستمع ستعود دائمًا false.

سيتعامل هذا الغلاف أيضًا مع المشكلة مع المستمع الذي يتم استدعاؤه أثناء إنشاء التصميم.

استمتع يا جينز.

نظرًا لأن لا شيء يعمل بالنسبة لي ، ولدي أكثر من مرة واحدة في رأيي (و IMHO التي تحمل خريطة منطقية هي عبارة عن مبالغة) أستخدم العلامة لحساب النقرات:

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) {
        }
    });

الكثير من الإجابات بالفعل ، ها هي لي.

أنا أمتد AppCompatSpinner وأضف طريقة pgmSetSelection(int pos) يسمح ذلك بإعداد الاختيار البرمجي دون تشغيل رد اتصال محدد. لقد قمت بترميز هذا باستخدام Rxjava بحيث يتم تسليم أحداث الاختيار عبر Observable.

package com.controlj.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;

import io.reactivex.Observable;

/**
 * Created by clyde on 22/11/17.
 */

public class FilteredSpinner extends android.support.v7.widget.AppCompatSpinner {
    private int lastSelection = INVALID_POSITION;


    public void pgmSetSelection(int i) {
        lastSelection = i;
        setSelection(i);
    }

    /**
     * Observe item selections within this spinner. Events will not be delivered if they were triggered
     * by a call to setSelection(). Selection of nothing will return an event equal to INVALID_POSITION
     *
     * @return an Observable delivering selection events
     */
    public Observable<Integer> observeSelections() {
        return Observable.create(emitter -> {
            setOnItemSelectedListener(new OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if(i != lastSelection) {
                        lastSelection = i;
                        emitter.onNext(i);
                    }
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {
                    onItemSelected(adapterView, null, INVALID_POSITION, 0);
                }
            });
        });
    }

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

    public FilteredSpinner(Context context, int mode) {
        super(context, mode);
    }

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

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

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

مثال على استخدامه ، ودعا في onCreateView() في Fragment فمثلا:

    mySpinner = view.findViewById(R.id.history);
    mySpinner.observeSelections()
        .subscribe(this::setSelection);

أين setSelection() هي طريقة في العرض المرفق الذي يشبه هذا ، والذي يسمى كلاهما من أحداث اختيار المستخدم عبر Observable وأيضًا في مكان آخر برمجيًا ، وبالتالي فإن منطق التحديدات الشائعة في كل من طرق الاختيار.

private void setSelection(int position) {
    if(adapter.isEmpty())
        position = INVALID_POSITION;
    else if(position >= adapter.getCount())
        position = adapter.getCount() - 1;
    MyData result = null;
    mySpinner.pgmSetSelection(position);
    if(position != INVALID_POSITION) {
        result = adapter.getItem(position);
    }
    display(result);  // show the selected item somewhere
}

سأحاول الاتصال

spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());

بعد الاتصال setadapter (). حاول أيضًا الاتصال قبل المحول.

لديك دائمًا الحل للذهاب مع التصنيف الفرعي ، حيث يمكنك لف العلم المنطقي إلى طريقة SetAdapter الخاصة بك لتخطي الحدث.

لم يساعدني الحل مع العلم المنطقي أو العداد ، "تسبب أثناء تغيير الاتجاه على onItemseced () استدعاء" الفائض "العلم أو العداد.

أنا فئات فرعية android.widget.Spinner وجعل إضافات صغيرة. الأجزاء ذات الصلة أدناه. عملت هذا الحل بالنسبة لي.

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;
}

هذا ليس حلًا أنيقًا أيضًا. في الواقع ، إنه بالأحرى Rube-Goldberg ولكن يبدو أنه يعمل. أتأكد من استخدام الدوار مرة واحدة على الأقل عن طريق تمديد محول الصفيف والتغلب على GetDropDownView. في طريقة GetDropDownView الجديدة ، تم استخدام علامة منطقية تم تعيينها لإظهار القائمة المنسدلة مرة واحدة على الأقل. أتجاهل المكالمات إلى المستمع حتى يتم تعيين العلم.

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);

محول صفيف التجاوز:

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);
    }
}

المستمع المعدل:

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

إذا كنت بحاجة إلى إعادة إنشاء نشاط على سبيل المثال: تغيير السمات ، فلن يعمل العلم/العداد البسيط

استخدم وظيفة onuserInterAction () للكشف عن نشاط المستخدم ،

المرجعي : https://stackoverflow.com/a/25070696/4772917

أملك فعله بأبسط طريقة:

private AdapterView.OnItemSelectedListener listener;
private Spinner spinner;

oncreate () ؛

spinner = (Spinner) findViewById(R.id.spinner);

listener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {

            Log.i("H - Spinner selected position", position);
        }

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

        }
    };

 spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            spinner.setOnItemSelectedListener(listener);
        }

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

        }
    });

فعله

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) {

     }
});

يمكنني الحصول على حل مشكلتي فقط بضرب العرض مع 2 كان قد تم ناتج عن الرمز: giveacodicetagpre.

أنا بحاجة إلى استخدام mSpinner في العرض ، لذلك العلم mOldPosition تم تعيينه في الفصل الداخلي المجهول.

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
            }
        });

سأقوم بتخزين الفهرس الأولي أثناء إنشاء كائن onClickListener.

   int thisInitialIndex = 0;//change as needed

   myspinner.setSelection(thisInitialIndex);

   myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

      int initIndex = thisInitialIndex;

      @Override
      public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         if (id != initIndex) { //if selectedIndex is the same as initial value
            // your real onselecteditemchange event
         }
      }

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

يستخدم الحل الخاص بي onTouchListener ولكن لا يقيد من استخدامه.يقوم بإنشاء غلاف لـ onTouchListener إذا لزم الأمر حيث الإعداد 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;
    }
}

قد أجب على بعد فوات الأوان على المنشور ، ومع ذلك تمكنت من تحقيق ذلك باستخدام مكتبة ربط بيانات Android Android Databinding . لقد قمت بإنشاء ملزمة مخصصة للتأكد من عدم استدعاء المستمع حتى يتم تغيير العنصر المحدد ، لذلك حتى إذا كان المستخدم يختار نفس الموقف مرارًا وتكرارًا ، لم يتم إطلاق الحدث.

تخطيط ملف XML

    <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 هو المكان الذي تمر فيه بالوضع للاختيار.

الربط المخصص

  @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) {

        }
    });
}

يمكنك قراءة المزيد حول ربط البيانات المخصصة هنا Android Custom Setter

ملاحظة

  1. لا تنسَ تمكين ربط البيانات في ملف Gradle

       android {
     ....
     dataBinding {
     enabled = true
    }
    }
    
  2. قم بتضمين ملفات التخطيط الخاصة بك في <layout> العلامات

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) {
            }
        });
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top