วิธีการป้องกัน onItemSelected ไม่ให้ใช้งานสปินเนอร์ที่เพิ่งสร้างใหม่?


419

ฉันคิดว่าวิธีที่ยอดเยี่ยมกว่านี้ในการแก้ปัญหานี้ แต่ฉันรู้ว่าฉันต้องพลาดบางสิ่งบางอย่าง

onItemSelectedไฟของฉันดับลงทันทีโดยไม่มีการโต้ตอบกับผู้ใช้และนี่เป็นพฤติกรรมที่ไม่พึงประสงค์ ฉันต้องการให้ UI รอจนกระทั่งผู้ใช้เลือกบางอย่างก่อนที่จะทำอะไร

ฉันได้ลองตั้งค่าฟังใน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.
    }
}
}

2
คุณสามารถดูวิธีนี้มันเป็นเรื่องง่ายและปฏิบัติ stackoverflow.com/a/10102356/621951
GünayGültekin

1
วิธีแก้ปัญหาง่ายๆคือการทำให้ไอเท็มแรกSpinnerว่างเปล่าและข้างในonItemSelectedคุณสามารถตรวจสอบได้ว่าสตริงนั้นไม่ว่างเปล่าstartActivity!
มูฮัมหมัดบาบา

รูปแบบนี้ทำงานได้อย่างถูกต้องstackoverflow.com/questions/13397933//
saksham

คำตอบ:


78

ฉันคาดว่าโซลูชันของคุณจะทำงาน - ฉันแม้ว่าเหตุการณ์การเลือกจะไม่เริ่มทำงานหากคุณตั้งค่าอะแดปเตอร์ก่อนตั้งค่าผู้ฟัง

ที่ถูกกล่าวว่าธงบูลีนที่เรียบง่ายจะช่วยให้คุณสามารถตรวจสอบเหตุการณ์การเลือกครั้งแรกอันธพาลและไม่สนใจมัน


15
เอ่อใช่ นั่นคือสิ่งที่ฉันหมายถึงโดยวิธีแก้ปัญหาที่ไม่เหมาะสม ดูเหมือนว่าจะต้องมีวิธีที่ดีกว่า ขอบคุณค่ะ
FauxReal

5
หัวข้อนี้เกี่ยวกับ Dev ml มีข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับเรื่องนี้: groups.google.com/group/android-developers/browse_thread/thread/ … - น่าเสียดายที่ไม่มีวิธีแก้ปัญหาให้ ...
BoD

25
กระบวนการในการจัดวางองค์ประกอบทำให้เกิดการเลือกฟัง คุณจะต้องเพิ่มผู้ฟังหลังจากจัดเลย์เอาท์เรียบร้อยแล้ว ฉันไม่สามารถหาสถานที่ที่เหมาะสมและตรงไปตรงมาได้เนื่องจากรูปแบบดูเหมือนจะเกิดขึ้นในบางครั้งonResume()และonPostResume()ดังนั้นตะขอปกติทั้งหมดจึงเสร็จสมบูรณ์ตามเวลาที่เค้าโครงเกิดขึ้น
Dan Dyer

28
ฉันจะอยู่ห่างจากธงบูลีนนี้ - ราวกับว่าการเปลี่ยนแปลงพฤติกรรมในอนาคตอาจทำให้เกิดข้อผิดพลาด วิธีแก้ปัญหากระสุนกันมากขึ้นคือการเก็บตัวแปรด้วย "ดัชนีที่เลือกในปัจจุบัน" เริ่มต้นไปที่รายการแรกที่เลือก จากนั้นในกิจกรรมการคัดเลือก - ตรวจสอบว่าเท่ากับตำแหน่งใหม่ - ส่งคืนและไม่ทำอะไรเลย แน่นอนปรับปรุงตัวแปรในการเลือก
daniel.gindi

2
สิ่งนี้ใช้ไม่ได้ ตอบโดย @casanova ทำงาน นั่นควรเป็นคำตอบที่ยอมรับได้
Siddharth

379

การใช้ 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);

48
+1 อัญมณีที่ซ่อนอยู่! การส่งผ่าน false เป็นพารามิเตอร์ "animate" จะไม่เรียก callback listener ! น่ากลัว
pkk

3
+1 ทางออกที่แปลก แต่สง่างาม :) โชคดีที่ฉันต้องโทรหา setSelection อยู่แล้ว ...
Martin T.

35
ผู้ฟังจะเริ่มทำงานเมื่อมีการรวมองค์ประกอบ Spinner UI ดังนั้นจึงจะเริ่มทำงานโดยไม่คำนึงถึงสิ่งที่ไม่ได้ป้องกันพฤติกรรมที่ไม่พึงประสงค์ที่อธิบายโดย OP วิธีนี้ใช้งานได้ดีหากไม่ได้ประกาศในระหว่างหรือก่อนหน้า onCreateView () แต่นั่นไม่ใช่สิ่งที่พวกเขาขอ
ฤดี Kershaw

6
มีประโยชน์ แต่แก้ปัญหาแตกต่างจาก OP ที่นำเสนอ OP อ้างถึงเหตุการณ์การเลือกที่ (โชคไม่ดี) จะเปิดอัตโนมัติเมื่อมุมมองปรากฏขึ้นครั้งแรกแม้ว่าโปรแกรมเมอร์จะไม่ได้ตั้งค่าการเลือก
ToolmakerSteve

2
พารามิเตอร์ค่า "false" ในเมธอด setSelection (.. ) เป็นวิธีแก้ปัญหาสำหรับฉัน TY!
Dani

195

อ้างถึงคำตอบของ Dan Dyer พยายามที่จะลงทะเบียนOnSelectListenerในpost(Runnable)วิธีการ:

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

โดยการทำเช่นนั้นสำหรับฉันพฤติกรรมที่ต้องการในที่สุดก็เกิดขึ้น

ในกรณีนี้ก็หมายความว่าผู้ฟังจะยิงเฉพาะรายการที่เปลี่ยนแปลง


1
ฉันได้รับข้อผิดพลาดว่า: วิธีการ setOnItemSelectedListener (AdapterViewOnItemSelectedListener) ในประเภท AdapterView <SpinnerAdapter> ไม่สามารถใช้งานได้กับอาร์กิวเมนต์ (Runnable ใหม่ () {}) ทำไมถึงเป็นเช่นนั้น
จาคอบ

นี่ไม่ใช่การตั้งค่าสภาพการแข่งขันระหว่าง Runnable และ UI Thread หรือไม่?
kenny_k

6
@theFunkyEngineer - รหัสนี้ควรถูกเรียกใช้จากหนึ่งในวิธีการหลักด้ายเช่นonCreate(), onResume()ฯลฯ ในกรณีที่เป็นเคล็ดลับที่ยอดเยี่ยมของตนกับอันตรายจากสภาพการแข่งขันที่ไม่มี ปกติฉันจะใช้เคล็ดลับนี้onCreate()หลังจากโค้ดเลย์เอาต์
Richard Le Mesurier

1
นี่เป็นทางออกที่ดีและไม่แฮ็คอย่างแน่นอน! การทำงานประเภทนี้เป็นสิ่งที่ทำในกรอบลึก มันเป็นความอัปยศที่ปินเนอร์ไม่ได้ทำสิ่งนี้ภายใน อย่างไรก็ตามวิธีนี้เป็นวิธีที่ปลอดภัยที่สุดที่จะมีรหัสรับประกันว่าจะทำงานหลังจากการสร้างกิจกรรม ใช้งานได้เพราะผู้ฟังไม่ได้ตั้งค่าไว้บนสปินเนอร์เมื่อกิจกรรมพยายามแจ้ง
jophde

1
นี่คือทางออกที่ยอมรับได้ ไม่ใช่คนตาบอด ทางออกอื่นมีแนวโน้มที่จะเกิดปัญหาการเปลี่ยนแปลงพฤติกรรมในอนาคต
Kuldeep Singh Dhaka

50

ฉันสร้างวิธีการยูทิลิตี้ขนาดเล็กเพื่อเปลี่ยนการ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);
                }
            });
        }
    });
}

มันปิดการใช้งานฟังเปลี่ยนการเลือกและเปิดใช้งานฟังหลังจากนั้น

เคล็ดลับก็คือการโทรแบบอะซิงโครนัสกับเธรด UI ดังนั้นคุณต้องทำในการจัดการโพสต์ต่อเนื่อง


น่ากลัว ฉันมีสปินเนอร์หลายตัวและพยายามตั้งค่าให้ผู้ฟังทั้งหมดเป็นโมฆะก่อนที่จะตั้งค่าของพวกเขาจากนั้นฉันตั้งให้ทุกคนกลับไปเป็นสิ่งที่พวกเขาควรจะเป็น แต่ด้วยเหตุผลบางอย่างที่ไม่ได้ผล ลองใช้ฟังก์ชั่นนี้แทนและใช้งานได้ ฉันไม่รู้ว่าทำไมของฉันถึงใช้งานไม่ได้ แต่สิ่งนี้ใช้ได้ผลฉันเลยไม่สนใจ: D
JStephen

4
หมายเหตุ: หากคุณโทรsetSpinnerSelectionWithoutCallingListenerอย่างรวดเร็วสองครั้งเพื่อให้มีการโทรครั้งที่สองในขณะที่สายแรกตั้งค่าฟังไว้nullแล้วสปินเนอร์ของคุณจะติดอยู่กับnullผู้ฟังตลอดไป ผมเสนอการแก้ไขดังต่อไปนี้: เพิ่มหลังif (listener == null) return; spinner.setSelection(selection)
Violet Giraffe

34

น่าเสียดายที่ทั้งสองวิธีที่แนะนำโดยทั่วไปสำหรับปัญหานี้คือการนับการโทรกลับที่เกิดขึ้นและการโพสต์ Runnable เพื่อตั้งค่าการโทรกลับในเวลาต่อมาอาจล้มเหลวเมื่อเปิดใช้งานตัวเลือกการเข้าถึงตัวอย่างเช่น นี่คือคลาสผู้ช่วยที่แก้ไขปัญหาเหล่านี้ คำอธิบายเพิ่มเติมอยู่ในบล็อกความคิดเห็น

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

3
นี่ควรเป็นคำตอบที่ได้รับการโหวตสูงสุด มันง่าย แต่ยอดเยี่ยม ช่วยให้คุณสามารถทำให้การใช้งานปัจจุบันของคุณเหมือนเดิมยกเว้นหนึ่งบรรทัดที่คุณเริ่มต้น แน่นอนว่าการทำโปรเจ็กต์เก่า ๆ แบบ retro retro ค่อนข้างง่าย ยิ่งกว่านั้นฉันได้ฆ่านกสองตัวด้วยหินก้อนเดียวโดยใช้อินเทอร์เฟซ OnTouchLisener เพื่อปิดแป้นพิมพ์เมื่อเปิดสปินเนอร์ ตอนนี้เหยื่อของฉันทุกคนประพฤติตนอย่างที่ฉันต้องการ
3829751

คำตอบที่สวยงาม มันยังคงก่อให้เกิดองค์ประกอบที่ 0 เมื่อฉัน addAll () ไปยังอะแดปเตอร์ แต่องค์ประกอบที่ 0 ของฉันคือรูปไข่สำหรับพฤติกรรมที่เป็นกลาง (ไม่ต้องทำอะไรเลย)
jwehrle

31

ฉันมีปัญหามากมายเกี่ยวกับการเผาไหม้ของเครื่องปั่นด้ายเมื่อฉันไม่ต้องการและคำตอบทั้งหมดที่นี่ไม่น่าเชื่อถือ พวกเขาทำงาน - แต่บางครั้งเท่านั้น ในที่สุดคุณจะพบกับสถานการณ์ที่พวกเขาจะล้มเหลวและแนะนำข้อบกพร่องในรหัสของคุณ

สิ่งที่ใช้ได้ผลสำหรับฉันคือการจัดเก็บดัชนีที่เลือกล่าสุดในตัวแปรและประเมินผลในผู้ฟัง หากเป็นเช่นเดียวกับดัชนีที่เลือกใหม่จะไม่ทำอะไรเลยและส่งคืนสิ่งอื่นดำเนินการกับผู้ฟัง ทำเช่นนี้:

//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

}

เชื่อใจฉันเมื่อฉันพูดแบบนี้นี่เป็นทางออกที่น่าเชื่อถือที่สุด แฮ็ค แต่ใช้งานได้!


สิ่งนี้จะทำงานได้หรือไม่หากคุณพยายามเปลี่ยนค่าหรือไม่ ในกรณีของฉันฉันกำลังพยายามตั้งค่าเป็นอย่าง 3 เมื่อเป็นจริง 0 โดยไม่ทริกเกอร์ผู้ฟังการเปลี่ยนแปลง คุณกำลังบอกว่า int i จะคืนค่าที่แตกต่างกันหากผู้ใช้เลือกหรือไม่
JStephen

สวัสดี JStephen ฉันไม่แน่ใจ 100% ว่าคุณหมายถึงอะไร แต่ int i จะเป็นตำแหน่งของสปินเนอร์ทุกครั้งที่มีการเรียกใช้ onItemSelected ปัญหาคือว่า onItemSelected จะถูกเรียกเมื่อใดก็ตามที่สปินเนอร์ถูกโหลดครั้งแรกโดยไม่มีการโต้ตอบกับผู้ใช้จริงใด ๆ นำไปสู่พฤติกรรมที่ไม่พึงประสงค์ในกรณีนี้ int i จะเท่ากับ 0 ณ จุดเริ่มต้นนี้เนื่องจากนี่เป็นดัชนีเริ่มต้นเริ่มต้นเมื่อโหลดสปินเนอร์เป็นครั้งแรก ดังนั้นโซลูชันของฉันจะตรวจสอบเพื่อให้แน่ใจว่ามีการเลือกรายการที่แตกต่างกันจริงแทนที่จะเป็นรายการที่เลือกในปัจจุบันที่ถูกเลือกใหม่ ... นี่จะตอบคำถามของคุณหรือไม่?
คริส

สวัสดีคริสฉันมีหน้าเว็บที่ดึงข้อมูลจากฐานข้อมูลเพื่อให้ผู้ใช้แก้ไขได้ เมื่อหน้าเปิดขึ้นฉันเติมสปินเนอร์แล้วตั้งค่าตำแหน่งให้เป็นค่าที่อยู่ในฐานข้อมูล ดังนั้นหากฉันตั้งค่าตำแหน่งเป็น 3 สิ่งนี้จะทำให้ onItemSelected เรียกใช้โดยฉันตั้งค่าเป็น 3 ซึ่งแตกต่างจากครั้งแรก ฉันคิดว่าคุณกำลังพูดว่าฉันถูกตั้งค่าเฉพาะในกรณีที่ผู้ใช้เปลี่ยนมันเอง
JStephen

4
เกิดอะไรขึ้นถ้าผู้ใช้เลือกตำแหน่ง 0 พวกเขาจะถูกละเว้น
Yetti99

ฉันไม่คิดว่าตำแหน่งสุดท้ายเป็นความคิดที่ดี ฉันเริ่มปั่นด้ายด้วยการโหลดตำแหน่งจาก SharedPreferences และใช้ setSelection บ่อยครั้งที่ค่าใน SharedPrefs ไม่เหมือนกับค่าเริ่มต้นเมื่อสร้างสปินเนอร์ดังนั้น onItemSelected จะถูกเรียกใช้เมื่อเริ่มต้น
Arthez

26

ฉันอยู่ในสถานการณ์ที่คล้ายกันและฉันมีวิธีแก้ไขปัญหาที่ง่ายสำหรับฉัน

ดูเหมือนว่าวิธีการsetSelection(int position)และsetSelected(int position, boolean animate)มีการใช้งานภายในที่แตกต่างกัน

เมื่อคุณใช้วิธีที่สองsetSelected(int position, boolean animate)ด้วยการตั้งค่าสถานะการเคลื่อนไหวที่ผิดพลาดคุณจะได้รับการเลือกโดยไม่ต้องonItemSelectedฟังผู้ฟัง


วิธีที่ดีกว่าคือไม่ต้องกังวลเกี่ยวกับการโทรพิเศษไปที่ onItemSelected แต่เพื่อให้แน่ใจว่าจะแสดงการเลือกที่ถูกต้อง ดังนั้นการเรียก spinner.setSelection (selectedIndex) ก่อนที่จะเพิ่มผู้ฟังทำให้มันทำงานได้อย่างสม่ำเสมอสำหรับฉัน
andude

1
ไม่มีวิธี setSelected (ตำแหน่ง int, บูลีนแอนนิเมชั่น) สำหรับสปินเนอร์
shift66

4
โทรจริงที่คุณต้องการคือsetSelection(int position, boolean animate);
แบรด

+1 สำหรับคุณ วิธีนี้จะช่วยแก้ปัญหาทั่วไปได้มากขึ้นเมื่อโค้ดมีการเปลี่ยนแปลงบ่อยครั้งยิ่งขึ้นเนื้อหาสปินเนอร์และการเลือกรายการที่เลือกไว้สำหรับการโต้ตอบกับผู้ใช้เท่านั้น
alrama

4
ธงภาพเคลื่อนไหวที่เป็นเท็จที่น่าเศร้ายังคงเรียกใช้onItemSelectedใน API23
mcy

23

เพื่อสรุปคำแนะนำในการใช้ onTouchListener เพื่อแยกความแตกต่างระหว่างการโทรอัตโนมัติไปยัง setOnItemSelectedListener (ซึ่งเป็นส่วนหนึ่งของการเริ่มต้นกิจกรรม ฯลฯ ) เทียบกับการเรียกที่ถูกกระตุ้นโดยการโต้ตอบผู้ใช้จริงฉันทำสิ่งต่อไปนี้หลังจากลองคำแนะนำอื่น ๆ พบว่าทำงานได้ดีกับโค้ดที่น้อยที่สุด

เพียงแค่ตั้งค่าฟิลด์บูลีนสำหรับกิจกรรม / ชิ้นส่วนของคุณเช่น:

private Boolean spinnerTouched = false;

จากนั้นก่อนที่คุณจะตั้งค่า setOnItemSelectedListener ของสปินเนอร์ให้ตั้งค่า 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;

1
ใช้งานได้ดีและตั้งแต่ Android 6+ นั่นเป็นวิธีเดียวที่ใช้งานได้ แต่คุณต้องทำเช่นเดียวกันกับ setOnKeyListener () หรือไม่ทำงานเมื่อผู้ใช้นำทางด้วยคีย์บอร์ด
Stéphane

ใช้งานได้ดีโซลูชันอื่น ๆ ทั้งหมดมีปัญหากับโทรศัพท์ที่แตกต่างกัน
Ziwei Zeng

ง่ายและสมบูรณ์แบบที่สุด! ไม่จำเป็นต้องเพิ่มเรื่องไร้สาระเพียงแค่คำนึงถึงตรรกะ ฉันดีใจที่ฉันเลื่อนลงมาจนถึงที่นี่!
user3833732

แทนที่จะเป็น setOnKeyListener () คุณสามารถ subclass spinner และ set flag spinnerTouched = true ในเมธอด preformClick () ที่ถูกแทนที่ซึ่งเรียกในทั้งสองกรณี (touch / key) ส่วนที่เหลือเหมือนกัน
ผู้ทรงอำนาจ

แค่อยากจะพูดถึงเรื่องนี้ดูเหมือนจะแก้ไขข้อผิดพลาดเดียวกันกับ DropDownPreferences ฉันเพิ่งโพสต์ที่นี่: stackoverflow.com/questions/61867118/ …ฉันไม่สามารถเชื่อมั่นได้ tbh: D
Daniel Wilson

13
spinner.setSelection(Adapter.NO_SELECTION, false);

3
รหัสอาจพูดให้ตัวเอง แต่คำอธิบายเล็กน้อยไปทางยาว :)
nhaarman

8

หลังจากดึงผมออกมาเป็นเวลานานตอนนี้ฉันได้สร้างคลาสสปินเนอร์ของตัวเอง ฉันได้เพิ่มวิธีการที่ตัดการเชื่อมต่อและเชื่อมต่อผู้ฟังอย่างเหมาะสม

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 เนื่องจากเป็น PITA


1
มันใช้งานไม่ได้สำหรับฉันมันยังคงเปิดใช้งานใน ItemSelected
Arthez

Arthez โปรดตรวจสอบอีกครั้งว่าคุณกำลังผ่านข้อโต้แย้งที่สามไปจริง ๆ ถ้าใช่มีอย่างอื่นไม่ถูกต้องที่นี่ ถ้าเป็นไปได้โพสต์รหัสของคุณ
fusion44

8

ไม่มีเหตุการณ์ไม่พึงประสงค์จากเฟสเลย์เอาต์หากคุณเลื่อนการเพิ่มผู้ฟังจนกว่าเลย์เอาต์จะเสร็จสิ้น:

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

            });

        }
    });

วิธีนี้ใช้งานได้และ IMO เป็นวิธีแก้ปัญหาที่ชัดเจนที่สุดสำหรับปัญหาเฉพาะของ OP ฉันต้องการที่จะทราบว่าคุณสามารถลบViewTreeObserver.OnGlobalLayoutListenerในรุ่นด้านล่าง J โดยการโทรViewTreeObserver.removeGlobalOnLayoutListenerซึ่งเลิกใช้แล้วและมีชื่อคล้ายกับวิธีการที่คำตอบนี้ใช้
Jack Meister

7

สิ่งนี้จะเกิดขึ้นหากคุณเลือกรหัสเป็น;

   mSpinner.setSelection(0);

แทนที่จะใช้ข้อความข้างต้น

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

แก้ไข: วิธีนี้ใช้ไม่ได้กับ Mi Android เวอร์ชั่น Mi UI


2
นี่เป็นการแก้ปัญหาสำหรับฉันอย่างแน่นอน ฉันอ่านเอกสารเกี่ยวกับเครื่องมือตัวปั่นด้าย .. มันยากมากที่จะเข้าใจความแตกต่าง: setSelection (ตำแหน่ง int, บูลีนแอนนิเมต) -> ข้ามไปยังรายการเฉพาะในข้อมูลอะแดปเตอร์โดยตรง setSelection (ตำแหน่ง int) -> ตั้งค่ารายการที่เลือกในปัจจุบัน
Matt

5

ฉันได้รับคำตอบที่ง่ายมากและแน่ใจว่าได้ผล 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
}

3

ฉันพบวิธีแก้ปัญหาที่สง่างามกว่านี้แล้ว มันเกี่ยวข้องกับการนับจำนวนครั้งที่เรียกใช้ 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);

ประกาศตัวนับ int หลังจาก onCreate และจากนั้นภายในเมธอด onItemSelected () วางเงื่อนไข "if" เพื่อตรวจสอบว่ามีการเรียก atapter กี่ครั้ง ในกรณีของคุณคุณเรียกมันว่าครั้งเดียว:

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

2

ผลงานเล็ก ๆ ของฉันคือการเปลี่ยนแปลงของบางอย่างข้างต้นที่เหมาะกับฉันไม่กี่ครั้ง

ประกาศตัวแปรจำนวนเต็มเป็นค่าเริ่มต้น (หรือค่าที่ใช้ล่าสุดบันทึกไว้ในการตั้งค่า) ใช้ spinner.setSelection (myDefault) เพื่อตั้งค่านั้นก่อนที่ผู้ฟังจะลงทะเบียน ในการ onItemSelected ตรวจสอบว่าค่าสปินเนอร์ใหม่เท่ากับค่าที่คุณกำหนดก่อนที่จะใช้รหัสเพิ่มเติมใด ๆ

นี่เป็นข้อได้เปรียบที่เพิ่มเข้ามาของการไม่ใช้รหัสหากผู้ใช้เลือกค่าเดิมอีกครั้ง


1

หลังจากที่มีปัญหาเดียวกันฉันมาถึงวิธีการแก้ปัญหานี้โดยใช้แท็ก แนวคิดเบื้องหลังนั้นเรียบง่าย: เมื่อใดก็ตามที่สปินเนอร์เปลี่ยนไปในทางโปรแกรมตรวจสอบให้แน่ใจว่าแท็กแสดงตำแหน่งที่เลือก ในฟังแล้วคุณตรวจสอบว่าตำแหน่งที่เลือกเท่ากับแท็ก หากเป็นเช่นนั้นตัวเลือกสปินเนอร์จะถูกเปลี่ยนตามโปรแกรม

ด้านล่างเป็นคลาส "สปินเนอร์พร็อกซี" ใหม่ของฉัน:

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มันจะตั้งค่าแท็กเพื่อให้สายที่สองในสายเดียวกันผู้ฟังมักจะกลับ

wrapper นี้จะจัดการกับปัญหากับ listener ที่ถูกเรียกระหว่างการสร้างโครงร่าง

ขอให้สนุก Jens


1

เนื่องจากไม่มีอะไรเหมาะกับฉันและฉันมีสปินเนอร์มากกว่า 1 ตัวในมุมมองของฉัน (และ IMHO ที่ถือแผนที่บูลเป็น overkill) ฉันใช้แท็กเพื่อนับจำนวนคลิก:

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

1

มีคำตอบมากมายแล้วนี่เป็นของฉัน

ฉันขยาย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
}

0

ฉันจะลองโทร

spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());

หลังจากคุณโทร setAdapter () ลองโทรออกก่อนอะแดปเตอร์

คุณมีวิธีแก้ปัญหาเสมอไปกับการทำคลาสย่อยซึ่งคุณสามารถตัดแฟล็กบูลีนให้กับเมธอด setAdapter overriden ของคุณเพื่อข้ามเหตุการณ์


0

วิธีแก้ปัญหาด้วยค่าสถานะบูลีนหรือตัวนับไม่ได้ช่วยฉัน 'สาเหตุในระหว่างการวางแนวการเปลี่ยนแปลง onItemSelected () เรียก "overflew" ค่าสถานะหรือตัวนับ

ฉันจัดคลาสย่อย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;
}

0

นี่ไม่ใช่ทางออกที่หรูหราเช่นกัน ในความเป็นจริงมันค่อนข้าง 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);

อะแดปเตอร์อาร์เรย์ overriden:

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

0

หากคุณต้องการสร้างกิจกรรมใหม่ได้ทันทีเช่น: การเปลี่ยนธีมการตั้งค่าสถานะ / ตัวนับอย่างง่ายจะไม่ทำงาน

ใช้ฟังก์ชั่น onUserInteraction () เพื่อตรวจจับกิจกรรมของผู้ใช้

การอ้างอิง: https://stackoverflow.com/a/25070696/4772917


0

ฉันได้ทำด้วยวิธีที่ง่ายที่สุด:

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

        }
    });

เสร็จสิ้น


มันเป็นทางออกที่น่าสนใจ สามารถใช้คำอธิบายเพิ่มเติม โดยทั่วไปจะไม่สนใจเหตุการณ์ onItemSelected เป็นครั้งแรก พวกเขาอาจจะทำงานได้ดีในบางกรณี แต่คนอื่นไม่ได้เช่นเมื่อตัวเลือกการเข้าถึงจะเปิดใช้งาน(ดูคำอธิบายของ Jorrit)
jk7

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

     }
});

0

นั่นเป็นโซลูชันสุดท้ายและใช้งานง่ายของฉัน:

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

ใช้ค่าเริ่มต้นsetSelection(...)สำหรับพฤติกรรมเริ่มต้นหรือใช้setSelectionWithoutInformListener(...)สำหรับเลือกรายการในสปินเนอร์โดยไม่เรียกใช้ OnItemSelectedListener callback


0

ฉันต้องใช้mSpinnerใน ViewHolder ดังนั้นการตั้งค่าสถานะ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
            }
        });

0

ฉันจะเก็บดัชนีเริ่มต้นในระหว่างการสร้างวัตถุ 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) {
      }
  });

0

โซลูชันของฉันใช้onTouchListenerแต่ไม่ จำกัด จากการใช้งาน มันจะสร้างเสื้อคลุมสำหรับในกรณีที่จำเป็นที่ติดตั้งonTouchListeneronItemSelectedListener

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

0

ฉันอาจจะตอบรับสายเกินไปกว่าการโพสต์ แต่ฉันจัดการเพื่อให้บรรลุนี้โดยใช้ 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. อย่าลืมเปิดใช้งาน databinding ในไฟล์ Gradle ของคุณ

       android {
     ....
     dataBinding {
     enabled = true
    }
    }
  2. รวมไฟล์เลย์เอาต์ของคุณไว้ใน<layout>แท็ก


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

2
1) โปรดจัดรูปแบบรหัสของคุณให้ถูกต้อง 2) คำอธิบายเกี่ยวกับสิ่งที่โค้ดของคุณกำลังทำอยู่จะได้รับการชื่นชมเช่นกัน ไม่เข้าใจโค้ดบางส่วนในทันทีเมื่ออ่านโค้ด
Mike Koch
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.