Android Spinner: หลีกเลี่ยงการโทร onItemSelected ระหว่างการเริ่มต้น


157

ฉันสร้างโปรแกรม Android ด้วยและSpinner TextViewฉันต้องการแสดงรายการที่เลือกจากรายการดรอปดาวน์ของสปินเนอร์ใน TextView ฉันใช้งานสปินเนอร์ในonCreateวิธีการดังนั้นเมื่อฉันรันโปรแกรมมันจะแสดงค่าในTextView(ก่อนเลือกรายการจากรายการดรอปดาวน์)

ฉันต้องการแสดงค่าใน TextView เฉพาะหลังจากเลือกรายการจากรายการแบบหล่นลง ฉันจะทำสิ่งนี้ได้อย่างไร

นี่คือรหัสของฉัน:

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

public class GPACal01Activity extends Activity implements OnItemSelectedListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

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

        // Create an ArrayAdapter using the string array and a default spinner layout
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,R.array.noofsubjects_array, android.R.layout.simple_spinner_item);
        // Specify the layout to use when the list of choices appears
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        // Apply the adapter to the spinner
        spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(this);
    }

    public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
        TextView textView = (TextView) findViewById(R.id.textView1);
        String str = (String) parent.getItemAtPosition(pos);
        textView.setText(str);
    }

    public void onNothingSelected(AdapterView<?> arg0) {
        // TODO Auto-generated method stub

    }
}

สปินเนอร์มักจะมีค่าที่เลือกไว้เป็นค่าเริ่มต้นหากคุณต้องการมีสปินเนอร์ที่ไม่มีค่าเริ่มต้นที่เลือกไว้คุณควรสร้างสปินเนอร์ที่กำหนดเองของคุณหรือสร้างสปินเนอร์ที่กำหนดเองด้วยรายการที่ว่าง เลย์เอาต์ดิบของ GONE
Houcine

5
ทำไมข้อผิดพลาดที่น่ารำคาญนี้ยังคงมีอยู่ในปลายปี 2561 ???
ผู้ใช้ -12 123

คำตอบ:


174
spinner.setOnItemSelectedListener(this); // Will call onItemSelected() Listener.

ดังนั้นครั้งแรกที่จัดการสิ่งนี้ด้วยค่าจำนวนเต็มใด ๆ

ตัวอย่าง: เริ่มแรกใช้ int check = 0;

public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
   if(++check > 1) {
      TextView textView = (TextView) findViewById(R.id.textView1);
      String str = (String) parent.getItemAtPosition(pos);
      textView.setText(str);
   }
}

คุณสามารถทำได้ด้วยค่าบูลีนและโดยการตรวจสอบตำแหน่งปัจจุบันและก่อนหน้า ดูที่นี่


2
ผู้ชายที่ยอดเยี่ยม .. เมื่อฉันเผชิญกับปัญหานี้เป็นครั้งแรกฉันพยายามใช้สปินเนอร์ที่กำหนดเอง .. แต่มันก็ไม่ได้ผล ..
Sash_KP

1
คุณประกาศเช็คที่ไหน นอก getView ()? ข้างในมุมมอง ที่ไหน? ลองวิธีแก้ปัญหาของคุณ แต่ใช้ไม่ได้สำหรับฉัน
3718908

10
มันเป็นแพทช์ไม่แก้ปัญหาฉันเดา
saksham

1
ฉันกำลังเผชิญกับปัญหาเดียวกัน มันเป็นข้อผิดพลาดบางอย่าง? นอกจากนี้หากฉันเลือกรายการเดียวกันซ้ำ ๆ ผู้ฟังจะไม่ทำงานหลังจากครั้งแรก ใช้งานได้เฉพาะเมื่อมีการเปลี่ยนแปลงรายการตัวเลือก ความคิดเห็นใด ๆ ที่ช่วย?
amitava

1
ฉันลองวิธีแก้ปัญหานี้ด้วยปัญหาเดียวกันของฉัน แต่ woks เพียงอันเดียวเช่น: เมื่อฉันมี 2 รายการในรายการแบบหล่นลงมันจะตื่นเมื่อฉันเลือกรายการปั่นใด ๆ ครั้งที่ 1 เมื่อฉันลองครั้งที่ 2 มันไม่ทำงานอย่างถูกต้อง
Waseem

117

เพียงใส่บรรทัดนี้ก่อนตั้งOnItemSelectedListener

spinner.setSelection(0,false)

7
มันจะเป็นคำตอบที่ดีกว่าถ้าคุณจะเขียนว่ามันช่วยได้อย่างไรและทำไม
user3533716

1
มันช่วย แต่อยากได้อย่างไร
AEMLoviji

14
วิธีนี้ใช้งานได้เพราะคุณตั้งค่าการเลือกเป็นอันดับแรกจากนั้นเพิ่มผู้ฟัง แต่ผู้ฟังจะไม่ถูกเรียกเพราะคุณได้เลือกการเลือกนี้มาก่อน เฉพาะการเลือกใหม่เท่านั้นที่จะเรียกผู้ฟัง
นักพัฒนา android ที่

4
สิ่งนี้ทำงานได้เพราะการsetSelection(int, boolean)โทรsetSelectionInt()ภายในและคุณจำเป็นต้องตั้งค่าฟังหลังจาก ระวังที่ใช้setSelection(int)ไม่ได้เพราะมันเรียกsetNextSelectedPositionInt()ภายในและนี่คือสิ่งที่ทำให้ฉันที่นี่
Hai Zhang

3
สิ่งนี้ไม่ทำงานหากมีการประกาศระหว่างหรือก่อนหน้า onCreateView () onItemSelected จะถูกเรียก
Arash

62

เริ่มต้นด้วย API ระดับ 3 คุณสามารถใช้ onUserInteraction () ในกิจกรรมด้วยบูลีนเพื่อพิจารณาว่าผู้ใช้โต้ตอบกับอุปกรณ์หรือไม่

http://developer.android.com/reference/android/app/Activity.html#onUserInteraction ()

@Override
public void onUserInteraction() {
     super.onUserInteraction();
     userIsInteracting = true;
}

เป็นสนามในกิจกรรมฉันมี:

 private boolean userIsInteracting;

ในที่สุดปั่นของฉัน:

      mSpinnerView.setOnItemSelectedListener(new OnItemSelectedListener() {

           @Override
           public void onItemSelected(AdapterView<?> arg0, View view, int position, long arg3) {
                spinnerAdapter.setmPreviousSelectedIndex(position);
                if (userIsInteracting) {
                     updateGUI();
                }
           }

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

           }
      });

เมื่อคุณผ่านกิจกรรมบูลีนจะถูกรีเซ็ตเป็นเท็จ ทำงานเหมือนจับใจ


3
หนึ่ง Bill..I ดีคิดว่านี่เป็นทางออกที่ดีกว่าคนที่ได้รับการยอมรับเป็นคำตอบ
Ritesh Gune

คุณจะได้รับ setmPreviousSelectedIndex ที่ไหน?!?!
TheQ

สะกดผิด? :) setPreviousSelectedIndex ()
Bill Mote

4
สิ่งนี้จะไม่ทำงานในกรณีที่มีชิ้นส่วนซ้อนเนื่องจาก onUserInteraction เป็นวิธีการของกิจกรรม ทางออกอื่น ๆ ?
Chitrang

1
@ErikB nvm คิดออก การตั้งค่าเป็นเท็จภายในฟังได้ผลดี
Sikander

20

สิ่งนี้ใช้ได้สำหรับฉัน

การเริ่มต้นของสปินเนอร์ใน Android เป็นปัญหาบางครั้งปัญหาข้างต้นได้รับการแก้ไขโดยรูปแบบนี้

Spinner.setAdapter();
Spinner.setSelected(false);  // must
Spinner.setSelection(0,true);  //must
Spinner.setonItemSelectedListener(this);

อะแดปเตอร์การตั้งค่าควรเป็นส่วนแรกและ onItemSelectedListener (นี้) จะมีอายุการใช้งานเมื่อเริ่มต้นสปินเนอร์ โดยรูปแบบด้านบนของฉัน OnItemSelected () ไม่ได้ถูกเรียกระหว่างการเริ่มต้นของสปินเนอร์


11

ฮ่าฮ่า ... ฉันมีคำถามเดียวกัน เมื่อ initViews () ทำเช่นนี้ลำดับคือกุญแจผู้ฟังเป็นคนสุดท้าย โชคดี !

spinner.setAdapter(adapter);
spinner.setSelection(position);
spinner.setOnItemSelectedListener(listener);

สิ่งที่ดี! ไม่มีอะไรทำงานให้ฉันไม่ว่าฉันจะสมัครอะไรมาก่อน แต่อันนี้ได้ผลสำหรับฉันเหมือนมีเสน่ห์ ขอบคุณ @TreeSouth
Deepika Lalra

11
สำหรับฉันทำงาน spinner.setSelection (ตำแหน่งเท็จ) ที่ใช้ในลักษณะเดียวกัน ด้วยเมธอด setSelection (ตำแหน่ง) ผู้ฟังจะถูกเรียกระหว่างการเริ่มต้น
Mario Kutlev

2
@HugoGresse ลองโทร spinner.setSelection (0, false); . สิ่งที่เป็นตอนนี้มันจะไม่สนใจตำแหน่งที่เลือกเพราะมันถูกเลือกแล้ว
นักพัฒนา Android

8

ทางออกของฉัน:

protected boolean inhibit_spinner = true;


@Override
        public void onItemSelected(AdapterView<?> arg0, View arg1,
                int pos, long arg3) {

            if (inhibit_spinner) {
                inhibit_spinner = false;
            }else {

            if (getDataTask != null) getDataTask.cancel(true);
            updateData();
            }

        }

7

เพื่อหลีกเลี่ยงการเรียก spinner.setOnItemSelectedListener () ในระหว่างการเริ่มต้น

spinner.setSelection(Adapter.NO_SELECTION, true); //Add this line before setting listener
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

    }

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

    }
});

6

คุณสามารถทำได้โดยวิธีนี้:

AdapterView.OnItemSelectedListener listener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            //set the text of TextView
        }

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

        }
    });

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

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

        }
    });

ตอนแรกฉันสร้างผู้ฟังและประกอบกับการโทรกลับตัวแปร จากนั้นฉันจะสร้าง listener ตัวที่สองแบบไม่ระบุชื่อและเมื่อสิ่งนี้ถูกเรียกในครั้งแรกการเปลี่ยนแปลงตัวฟัง =]


3

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

ในรหัส:

สร้างผู้ฟังของคุณสำหรับปินเนอร์:

public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {

    boolean userSelect = false;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        userSelect = true;
        return false;
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        if (userSelect) { 
            userSelect = false;
            // Your selection handling code here
        }
    }

}

เพิ่มฟังเพื่อปั่นเป็นทั้งOnItemSelectedListenerและOnTouchListener:

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);

2

วิธีแก้ปัญหาง่ายๆที่คล้ายกันที่ช่วยให้หลายสปินเนอร์คือการใส่ AdapterView ลงในคอลเล็กชัน - ในกิจกรรมซูเปอร์คลาส - เมื่อเรียกใช้งานครั้งแรกของ onItemSelected (... ) จากนั้นตรวจสอบดูว่า AdapterView อยู่ในกลุ่มก่อนเรียกใช้หรือไม่ สิ่งนี้ช่วยให้วิธีการหนึ่งชุดในซูเปอร์คลาสและรองรับหลาย AdapterViews และหลายสปินเนอร์

ซูเปอร์คลาส ...

private Collection<AdapterView> AdapterViewCollection = new ArrayList<AdapterView>();

   protected boolean firstTimeThrough(AdapterView parent) {
    boolean firstTimeThrough = ! AdapterViewCollection.contains(parent);
    if (firstTimeThrough) {
       AdapterViewCollection.add(parent);
     }
    return firstTimeThrough;
   }

คลาสย่อย ...

public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
      if (! firstTimeThrough(parent)) {
        String value = safeString(parent.getItemAtPosition(pos).toString());
        String extraMessage = EXTRA_MESSAGE;
        Intent sharedPreferencesDisplayIntent = new         Intent(SharedPreferencesSelectionActivity.this,SharedPreferencesDisplayActivity.class);
    sharedPreferencesDisplayIntent.putExtra(extraMessage,value);
    startActivity(sharedPreferencesDisplayIntent);
  }
  // don't execute the above code if its the first time through
  // do to onItemSelected being called during view initialization.

}


2

สร้างฟิลด์บูลีน

private boolean inispinner;

ภายในการสร้างกิจกรรม

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if (!inispinner) {
                inispinner = true;
                return;
            }
            //do your work here
        }

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

        }
    });


1

คุณสามารถทำได้โดย setOnTouchListener ก่อนจากนั้น setOnItemSelectedListener ใน onTouch

@Override
public boolean onTouch(final View view, final MotionEvent event) {
 view.setOnItemSelectedListener(this)
 return false;
}

ฉันรักสิ่งนี้. แม้ว่ามันจะสร้างฟังใหม่ทุกครั้งที่ผู้ใช้สัมผัสกับมุมมอง ดังนั้นฉันชอบแคชผู้ฟังที่สร้างขึ้นครั้งแรกและนำมาใช้ซ้ำ
Vlad

1

สิ่งนี้ใช้ได้กับฉัน:

    spinner.setSelection(0, false);
    new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                spinner.setOnItemSelectedListener(listener);
            }, 500);

1

จากคำตอบของ Abhi ฉันทำให้ผู้ฟังง่าย ๆ

class SpinnerListener constructor(private val onItemSelected: (position: Int) -> Unit) : AdapterView.OnItemSelectedListener {

    private var selectionCount = 0

    override fun onNothingSelected(parent: AdapterView<*>?) {
        //no op
    }

    override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        if (selectionCount++ > 1) {
           onItemSelected(position)
        }
    }
}

0

มีปัญหาเดียวกันและสิ่งนี้ใช้ได้กับฉัน:

ฉันมีสปินเนอร์ 2 ตัวและฉันอัปเดตพวกเขาระหว่าง init และระหว่างการโต้ตอบกับส่วนควบคุมอื่น ๆ หรือหลังจากรับข้อมูลจากเซิร์ฟเวอร์

นี่คือแม่แบบของฉัน:

public class MyClass extends <Activity/Fragment/Whatever> implements Spinner.OnItemSelectedListener {

    private void removeSpinnersListeners() {
        spn1.setOnItemSelectedListener(null);
        spn2.setOnItemSelectedListener(null);
    }

    private void setSpinnersListeners() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                spn1.setOnItemSelectedListener(MyClass.this);
                spn2.setOnItemSelectedListener(MyClass.this);
            }
        }, 1);
    }

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

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

    }
}

เมื่อคลาสเริ่มต้นใช้setSpinnersListeners ()แทนการตั้งค่า listener โดยตรง

Runnable จะป้องกันไม่ให้สปินเนอร์ยิง onItemSelected ทันทีหลังจากที่คุณตั้งค่า

หากคุณต้องการอัปเดตสปินเนอร์ (หลังการโทรหาเซิร์ฟเวอร์ ฯลฯ ) ให้ใช้removeSpinnersListeners ()ทันทีก่อนบรรทัดอัปเดตของคุณและsetSpinnersListeners ()ทันทีหลังบรรทัดอัปเดต สิ่งนี้จะป้องกันไม่ให้ onItemSelected ไม่ทำงานหลังจากการอัปเดต


0

รหัส

spinner.setOnTouchListener(new View.OnTouchListener() { 
@Override public boolean onTouch(View view, MotionEvent motionEvent) { isSpinnerTouch=true; return false; }});

holder.spinner_from.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int slot_position, long l) {
                if(isSpinnerTouch)
                {
                    Log.d("spinner_from", "spinner_from");
                    spinnerItemClickListener.onSpinnerItemClickListener(position, slot_position, AppConstant.FROM_SLOT_ONCLICK_CODE);
                }
                else {

                }
            }

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

            }
        });

0

สำหรับฉันแล้วโซลูชั่นของ Abhi นั้นยอดเยี่ยมมากจนถึง Api ระดับ 27

แต่ดูเหมือนว่าจาก Api ระดับ 28 ขึ้นไป onItemSelected () จะไม่ถูกเรียกเมื่อตั้งค่า listener ซึ่งหมายความว่า onItemSelected () จะไม่ถูกเรียก

ดังนั้นฉันจึงเพิ่ม if-statement สั้น ๆ เพื่อตรวจสอบระดับ Api:

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

            if(Build.VERSION.SDK_INT >= 28){ //onItemSelected() doesn't seem to be called when listener is set on Api 28+
                check = 1;
            }

            if(++check > 1) {
                //Do your action here
            }
        }

ฉันคิดว่ามันค่อนข้างแปลกและฉันก็ไม่แน่ใจว่าคนอื่นจะมีปัญหานี้หรือไม่ แต่ในกรณีของฉันมันใช้งานได้ดี


0

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

รหัส Kotlin ของฉันมีลักษณะดังนี้:

private var mySpinnerHasBeenTapped = false

private fun initializeMySpinner() {

    my_hint_text_view.setOnClickListener {
        mySpinnerHasBeenTapped = true //turn flag to true
        my_spinner.performClick() //call spinner click
    }

    //Basic spinner setup stuff
    val myList = listOf("Leonardo", "Michelangelo", "Rafael", "Donatello")
    val dataAdapter: ArrayAdapter<String> = ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, myList)
    my_spinner.adapter = dataAdapter

    my_spinner.onItemSelectedListener = object : OnItemSelectedListener {

        override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {

            if (mySpinnerHasBeenTapped) { //code below will only run after the user has clicked
                my_hint_text_view.visibility = View.GONE //once an item has been selected, hide the textView
                //Perform action here
            }
        }

        override fun onNothingSelected(parent: AdapterView<*>?) {
            //Do nothing
        }
    }
}

ไฟล์เลย์เอาต์มีลักษณะดังนี้โดยมีส่วนสำคัญที่สปินเนอร์และ TextView แชร์ความกว้างความสูงและระยะขอบเดียวกัน:

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Spinner
                android:id="@+id/my_spinner"
                android:layout_width="match_parent"
                android:layout_height="35dp"
                android:layout_marginStart="10dp"
                android:layout_marginEnd="10dp"
                android:background="@drawable/bg_for_spinners"

                android:paddingStart="8dp"
                android:paddingEnd="30dp"
                android:singleLine="true" />

            <TextView
                android:id="@+id/my_hint_text_view"
                android:layout_width="match_parent"
                android:layout_height="35dp"                
                android:layout_marginStart="10dp"
                android:layout_marginEnd="10dp"
                android:background="@drawable/bg_for_spinners"

                android:paddingStart="8dp"
                android:paddingEnd="30dp"
                android:singleLine="true"
                android:gravity="center_vertical"
                android:text="*Select A Turtle"
                android:textColor="@color/green_ooze"
                android:textSize="16sp" />

        </FrameLayout>

ฉันแน่ใจว่าโซลูชันอื่นทำงานที่คุณไม่สนใจการโทรครั้งแรกบน ItItemSelected แต่ฉันไม่ชอบความคิดที่จะสมมติว่ามันจะถูกเรียก

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