จะสร้าง Android Spinner ด้วยข้อความเริ่มต้น“ Select One” ได้อย่างไร?


569

ฉันต้องการใช้สปินเนอร์ที่เริ่มต้น (เมื่อผู้ใช้ยังไม่ได้ทำการเลือก) จะแสดงข้อความ "เลือกหนึ่ง" เมื่อผู้ใช้คลิกสปินเนอร์รายการของรายการจะปรากฏขึ้นและผู้ใช้เลือกหนึ่งในตัวเลือก หลังจากที่ผู้ใช้ทำการเลือกรายการที่เลือกจะปรากฏในสปินเนอร์แทน "เลือกหนึ่ง"

ฉันมีรหัสต่อไปนี้เพื่อสร้างสปินเนอร์:

String[] items = new String[] {"One", "Two", "Three"};
Spinner spinner = (Spinner) findViewById(R.id.mySpinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);

ด้วยรหัสนี้ในขั้นต้นรายการ "หนึ่ง" จะปรากฏขึ้น ฉันสามารถเพิ่มรายการใหม่ "เลือกหนึ่งรายการ" ลงในรายการ แต่จากนั้น "เลือกหนึ่งรายการ" ก็จะปรากฏในรายการแบบเลื่อนลงเป็นรายการแรกซึ่งไม่ใช่สิ่งที่ฉันต้องการ

ฉันจะแก้ไขปัญหานี้ได้อย่างไร


6
โซลูชันที่สมบูรณ์แบบอยู่ในคำถามนี้: stackoverflow.com/questions/9863378/…เพียงแทนที่เมธอด getDropDownView ()
Sourab Sharma

คุณลองตั้งค่าองค์ประกอบแรกของอะแดปเตอร์เป็น "เลือกหนึ่ง" หรือไม่
IgorGanapolsky

[นี่เป็นวิธีแก้ปัญหาที่ดีเยี่ยมอื่น ๆ !] [1] [1]: stackoverflow.com/questions/9863378//
AirtonCarneiro

ปินเนอร์ที่ใช้ซ้ำได้: github.com/henrychuangtw/ReuseSpinner
HenryChuang

android--code.blogspot.in/2015/08/android-spinner-hint.htmlบทช่วยสอนอื่นที่ดี
Prabs

คำตอบ:


255

นี่คือโซลูชันทั่วไปที่แทนที่Spinnerมุมมอง มันแทนที่setAdapter()การตั้งค่าตำแหน่งเริ่มต้นเป็น -1 และพร็อกซีที่ให้SpinnerAdapterมาเพื่อแสดงสตริงพรอมต์สำหรับตำแหน่งน้อยกว่า 0

สิ่งนี้ได้รับการทดสอบบน Android 1.5 ถึง 4.2 แต่ผู้ซื้อระวัง! เพราะวิธีนี้อาศัยการไตร่ตรองในการโทรหาส่วนตัวAdapterView.setNextSelectedPositionInt()และAdapterView.setSelectedPositionInt()จึงไม่รับประกันว่าจะสามารถทำงานในการอัปเดตระบบปฏิบัติการในอนาคต ดูเหมือนว่าจะเป็นไปได้ แต่ก็ไม่รับประกัน

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

/**
 * A modified Spinner that doesn't automatically select the first entry in the list.
 *
 * Shows the prompt if nothing is selected.
 *
 * Limitations: does not display prompt if the entry list is empty.
 */
public class NoDefaultSpinner extends Spinner {

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

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

    public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(SpinnerAdapter orig ) {
        final SpinnerAdapter adapter = newProxy(orig);

        super.setAdapter(adapter);

        try {
            final Method m = AdapterView.class.getDeclaredMethod(
                               "setNextSelectedPositionInt",int.class);
            m.setAccessible(true);
            m.invoke(this,-1);

            final Method n = AdapterView.class.getDeclaredMethod(
                               "setSelectedPositionInt",int.class);
            n.setAccessible(true);
            n.invoke(this,-1);
        } 
        catch( Exception e ) {
            throw new RuntimeException(e);
        }
    }

    protected SpinnerAdapter newProxy(SpinnerAdapter obj) {
        return (SpinnerAdapter) java.lang.reflect.Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),
                new Class[]{SpinnerAdapter.class},
                new SpinnerAdapterProxy(obj));
    }



    /**
     * Intercepts getView() to display the prompt if position < 0
     */
    protected class SpinnerAdapterProxy implements InvocationHandler {

        protected SpinnerAdapter obj;
        protected Method getView;


        protected SpinnerAdapterProxy(SpinnerAdapter obj) {
            this.obj = obj;
            try {
                this.getView = SpinnerAdapter.class.getMethod(
                                 "getView",int.class,View.class,ViewGroup.class);
            } 
            catch( Exception e ) {
                throw new RuntimeException(e);
            }
        }

        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
            try {
                return m.equals(getView) && 
                       (Integer)(args[0])<0 ? 
                         getView((Integer)args[0],(View)args[1],(ViewGroup)args[2]) : 
                         m.invoke(obj, args);
            } 
            catch (InvocationTargetException e) {
                throw e.getTargetException();
            } 
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        protected View getView(int position, View convertView, ViewGroup parent) 
          throws IllegalAccessException {

            if( position<0 ) {
                final TextView v = 
                  (TextView) ((LayoutInflater)getContext().getSystemService(
                    Context.LAYOUT_INFLATER_SERVICE)).inflate(
                      android.R.layout.simple_spinner_item,parent,false);
                v.setText(getPrompt());
                return v;
            }
            return obj.getView(position,convertView,parent);
        }
    }
}

7
@emmby คุณมีความคิดวิธีล้างการเลือกหลังจากที่ผู้ใช้ตั้งค่าหรือไม่? ฉันลองเรียกการเรียกสองครั้งใหม่ () ไปยังเมธอด clearSelection () แต่มันไม่ได้ผลจริงๆ แม้ว่ารายการป๊อปอัพจะแสดงรายการที่เลือกไว้ก่อนหน้านี้ว่าไม่ได้เลือกวิดเจ็ตสปินเนอร์จะยังคงแสดงตามที่เลือกและหากผู้ใช้เลือกรายการเดิมอีกครั้ง onItemSelected () จะไม่ถูกเรียก
Qwertie

3
บางคนสามารถอธิบายวิธีใช้ข้างต้นคลาสได้หรือไม่
Bishan

4
โซลูชันนี้ไม่สมบูรณ์แบบ 100% สำหรับ Android 4.2 (Cyanogenmod 10.1) โดยใช้ Android: รายการ ความสูงของ TextView ที่สูงขึ้นนั้นเล็กกว่าความสูงของทรัพยากรใดก็ตามที่อะแด็ปเตอร์เริ่มต้นขยายตัว ดังนั้นเมื่อคุณเลือกตัวเลือกจริงความสูงจะเพิ่มขึ้น ~ 10px ใน Galaxy S ซึ่งไม่เป็นที่ยอมรับ ฉันได้ลองหลายสิ่งหลายอย่าง (แรงโน้มถ่วงการขยายระยะขอบ ฯลฯ ) และไม่มีใครทำงานได้อย่างน่าเชื่อถือในอุปกรณ์ดังนั้นฉันจะเลือกใช้วิธีอื่น
Mariments

3
@DavidDoria คุณต้องใช้คลาส NoDefaultSpinner ในไฟล์เลย์เอาต์ของคุณ คัดลอกแหล่งที่มาจากด้านบนลงในโครงการของคุณเช่นลงในแพ็คเกจ com.example.customviews ขณะนี้อยู่ในเลย์เอาท์ xml ของคุณแทนที่จะใช้ <Spinner ... > ให้ใช้ <com.example.customviews.NoDefaultSpinner ... > ส่วนที่เหลือของรหัสสามารถคงเดิมได้ อย่าลืมเพิ่มแอตทริบิวต์ android: prompt ในมุมมอง <com.example.customviews.NoDefaultSpinner> ในเค้าโครงของคุณ
Ridcully

2
@emmby spinnerBrand.setSelection (-1); ไม่ทำงาน
Sachin C

291

สิ่งที่คุณสามารถทำได้คือตกแต่งของคุณ SpinnerAdapterด้วยมุมมอง 'เลือกตัวเลือก ... ' เริ่มแรกเพื่อให้สปินเนอร์แสดงโดยไม่ได้เลือกอะไรเลย

นี่คือตัวอย่างการทำงานที่ผ่านการทดสอบสำหรับ Android 2.3 และ 4.0 (มันใช้อะไรในห้องสมุดความเข้ากันได้ดังนั้นจึงควรจะปรับสักครู่) เนื่องจากเป็นมัณฑนากร, มันควรจะเป็นเรื่องง่ายที่จะ retrofit รหัสที่มีอยู่และมันทำงานได้ดีกับCursorLoaderยัง (สลับเคอร์เซอร์บนแผ่นปิดcursorAdapterแน่นอน ... )

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

หมายเหตุของรหัส: 2 ตัวสร้าง

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

คุณกำหนดเลย์เอาต์เพื่อ 'ดู' เช่นพร้อมต์ตัวอย่างเป็นสีเทา ...

ไม่มีการเลือกเริ่มต้น

ใช้พรอมต์มาตรฐาน (สังเกตว่าไม่มีการเลือกอะไร):

ด้วยพรอมต์มาตรฐาน

หรือด้วยพรอมต์และบางสิ่งบางอย่างแบบไดนามิก (อาจไม่มีพรอมต์):

พรอมต์และไม่มีอะไรแถวที่เลือก

การใช้งานในตัวอย่างด้านบน

Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.planets_array, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setPrompt("Select your favorite Planet!");

spinner.setAdapter(
      new NothingSelectedSpinnerAdapter(
            adapter,
            R.layout.contact_spinner_row_nothing_selected,
            // R.layout.contact_spinner_nothing_selected_dropdown, // Optional
            this));

contact_spinner_row_nothing_selected.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    style="?android:attr/spinnerItemStyle"
    android:singleLine="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:textSize="18sp"
    android:textColor="#808080"
    android:text="[Select a Planet...]" />

NothingSelectedSpinnerAdapter.java

import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.SpinnerAdapter;

/**
 * Decorator Adapter to allow a Spinner to show a 'Nothing Selected...' initially
 * displayed instead of the first choice in the Adapter.
 */
public class NothingSelectedSpinnerAdapter implements SpinnerAdapter, ListAdapter {

    protected static final int EXTRA = 1;
    protected SpinnerAdapter adapter;
    protected Context context;
    protected int nothingSelectedLayout;
    protected int nothingSelectedDropdownLayout;
    protected LayoutInflater layoutInflater;

    /**
     * Use this constructor to have NO 'Select One...' item, instead use
     * the standard prompt or nothing at all.
     * @param spinnerAdapter wrapped Adapter.
     * @param nothingSelectedLayout layout for nothing selected, perhaps
     * you want text grayed out like a prompt...
     * @param context
     */
    public NothingSelectedSpinnerAdapter(
      SpinnerAdapter spinnerAdapter,
      int nothingSelectedLayout, Context context) {

        this(spinnerAdapter, nothingSelectedLayout, -1, context);
    }

    /**
     * Use this constructor to Define your 'Select One...' layout as the first
     * row in the returned choices.
     * If you do this, you probably don't want a prompt on your spinner or it'll
     * have two 'Select' rows.
     * @param spinnerAdapter wrapped Adapter. Should probably return false for isEnabled(0)
     * @param nothingSelectedLayout layout for nothing selected, perhaps you want
     * text grayed out like a prompt...
     * @param nothingSelectedDropdownLayout layout for your 'Select an Item...' in
     * the dropdown.
     * @param context
     */
    public NothingSelectedSpinnerAdapter(SpinnerAdapter spinnerAdapter,
            int nothingSelectedLayout, int nothingSelectedDropdownLayout, Context context) {
        this.adapter = spinnerAdapter;
        this.context = context;
        this.nothingSelectedLayout = nothingSelectedLayout;
        this.nothingSelectedDropdownLayout = nothingSelectedDropdownLayout;
        layoutInflater = LayoutInflater.from(context);
    }

    @Override
    public final View getView(int position, View convertView, ViewGroup parent) {
        // This provides the View for the Selected Item in the Spinner, not
        // the dropdown (unless dropdownView is not set).
        if (position == 0) {
            return getNothingSelectedView(parent);
        }
        return adapter.getView(position - EXTRA, null, parent); // Could re-use
                                                 // the convertView if possible.
    }

    /**
     * View to show in Spinner with Nothing Selected
     * Override this to do something dynamic... e.g. "37 Options Found"
     * @param parent
     * @return
     */
    protected View getNothingSelectedView(ViewGroup parent) {
        return layoutInflater.inflate(nothingSelectedLayout, parent, false);
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        // Android BUG! http://code.google.com/p/android/issues/detail?id=17128 -
        // Spinner does not support multiple view types
        if (position == 0) {
            return nothingSelectedDropdownLayout == -1 ?
              new View(context) :
              getNothingSelectedDropdownView(parent);
        }

        // Could re-use the convertView if possible, use setTag...
        return adapter.getDropDownView(position - EXTRA, null, parent);
    }

    /**
     * Override this to do something dynamic... For example, "Pick your favorite
     * of these 37".
     * @param parent
     * @return
     */
    protected View getNothingSelectedDropdownView(ViewGroup parent) {
        return layoutInflater.inflate(nothingSelectedDropdownLayout, parent, false);
    }

    @Override
    public int getCount() {
        int count = adapter.getCount();
        return count == 0 ? 0 : count + EXTRA;
    }

    @Override
    public Object getItem(int position) {
        return position == 0 ? null : adapter.getItem(position - EXTRA);
    }

    @Override
    public int getItemViewType(int position) {
        return 0;
    }

    @Override
    public int getViewTypeCount() {
        return 1;
    }

    @Override
    public long getItemId(int position) {
        return position >= EXTRA ? adapter.getItemId(position - EXTRA) : position - EXTRA;
    }

    @Override
    public boolean hasStableIds() {
        return adapter.hasStableIds();
    }

    @Override
    public boolean isEmpty() {
        return adapter.isEmpty();
    }

    @Override
    public void registerDataSetObserver(DataSetObserver observer) {
        adapter.registerDataSetObserver(observer);
    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {
        adapter.unregisterDataSetObserver(observer);
    }

    @Override
    public boolean areAllItemsEnabled() {
        return false;
    }

    @Override
    public boolean isEnabled(int position) {
        return position != 0; // Don't allow the 'nothing selected'
                                             // item to be picked.
    }

}

52
นี่เป็นวิธีการแก้ไขปัญหาที่หรูหรา รหัสทำงานเหมือนกับ copy'n'pasted ในโครงการของฉัน +1 สำหรับการสะท้อนที่ไม่จำเป็น
Richard Le Mesurier

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

6
นี่เป็นทางออกที่ยอดเยี่ยมสำหรับปัญหาที่ไม่ควรมีอยู่ (ลองใช้การพัฒนา iPhone) ยอดเยี่ยมและขอบคุณ! ดีใจที่มีคนจำรูปแบบอื่น ๆ ได้
1700737

3
@prashantwosti รหัสได้รับการปรับปรุงให้ทำงานกับ Lollipop getItemViewType () และ getViewTypeCount () โดยเฉพาะ
aaronvargas

3
@aaronvargas เมื่อเลือกไอเท็มจากสปินเนอร์แล้วฉันสามารถยกเลิกและเลือก "[เลือกดาวเคราะห์]" ได้ไหม?
modabeckham

130

ฉันลงเอยด้วยการใช้Buttonแทน ในขณะที่Buttonไม่ใช่Spinner , พฤติกรรมนั้นง่ายต่อการปรับแต่ง

สร้างอะแดปเตอร์ก่อนตามปกติ:

String[] items = new String[] {"One", "Two", "Three"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_spinner_dropdown_item, items);

โปรดทราบว่าฉันใช้simple_spinner_dropdown_itemเป็นรูปแบบ id วิธีนี้จะช่วยสร้างรูปลักษณ์ที่ดีขึ้นเมื่อสร้างกล่องโต้ตอบการแจ้งเตือน

ในตัวจัดการ onClick สำหรับปุ่มของฉันฉันมี:

public void onClick(View w) {
  new AlertDialog.Builder(this)
  .setTitle("the prompt")
  .setAdapter(adapter, new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {

      // TODO: user specific action

      dialog.dismiss();
    }
  }).create().show();
}

และนั่นมัน!


10
ฉันเห็นด้วยกับคำตอบนั้น นอกจากนี้ปุ่มยังมีสไตล์ที่ง่ายกว่าสปินเนอร์
Romain Piel

@HRJ ฉันได้ดำเนินการตามที่คุณแนะนำ แต่รายการที่เลือกไว้ก่อนที่จะไม่ได้รับการเน้น (หมายความว่าปุ่มวิทยุจะต้อง higlighted ด้วยจุด greeen ตรงกลาง) .... ฉันจะได้วิธีนี้ใน OnClick () วิธีการ ปุ่ม. โปรดช่วยฉัน HRJ .....
Avadhani Y

2
ปุ่มที่มีเลย์เอาต์นี้สมบูรณ์แบบ <ปุ่ม android: id = "@ + id / เมือง" android: layout_width = "match_parent" android: layout_height = "wrap_content" android: layout_margin = "5dp" android: gravity = "left" android: พื้นหลัง = "@ android: drawable / btn_dropdown" android: text = "@ string / city_prompt" />
kml_ckr

จากนั้นคุณจะปรับปรุงข้อความของปุ่มเพื่อสะท้อนการเลือกเช่นเดียวกับที่เกิดขึ้นในสปินเนอร์อย่างไร
ชิม

3
การแก้ปัญหา: เพียงแค่ใช้ SetAdapter แทน SetSingleChoiceItems
Grzegorz Dev

67

ก่อนอื่นคุณอาจสนใจpromptคุณสมบัติของSpinnerชั้นเรียน ดูภาพด้านล่าง "เลือกแพลนเน็ต" คือพรอมต์ที่สามารถตั้งค่าใน XML android:prompt=""ที่มี

ป้อนคำอธิบายรูปภาพที่นี่

ฉันจะแนะนำ subclassing Spinnerซึ่งคุณสามารถรักษาสองอะแดปเตอร์ภายใน หนึ่งอะแดปเตอร์ที่มีตัวเลือก "เลือกหนึ่ง" และอะแดปเตอร์จริงอื่น ๆ(ที่มีตัวเลือกจริง) จากนั้นใช้OnClickListenerสวิตช์เพื่อสลับอะแดปเตอร์ก่อนที่จะแสดงกล่องโต้ตอบตัวเลือก อย่างไรก็ตามหลังจากลองใช้ความคิดนั้นฉันได้ข้อสรุปว่าคุณไม่สามารถรับOnClickกิจกรรมสำหรับวิดเจ็ตได้

คุณสามารถปิดสปินเนอร์ในมุมมองที่แตกต่างกันตัดการคลิกที่มุมมองแล้วบอกให้คุณCustomSpinnerเปลี่ยนอะแดปเตอร์ แต่ดูเหมือนแฮ็คที่แย่มาก

คุณต้องการแสดง "เลือกหนึ่งรายการ" จริง ๆ หรือไม่


36
มันไม่ได้เป็นเพียงเรื่องของการจำเป็นต้องแสดง "เลือกหนึ่ง" แต่ยังอยู่ในกรณีที่ค่าสปินเนอร์อาจเลือกที่จะว่างเปล่า
greg7gkb

5
ด้วยตัวเลือกนี้ Earth จะแสดงเป็นตัวเลือกบนสปินเนอร์ก่อนที่จะมีการเลือกอะไรสำหรับแอพของฉันฉันต้องการให้ผู้ใช้สามารถบอกได้ว่าพวกเขายังไม่ได้เลือกอะไรเลย
dylan murphy

2
นี่ไม่ได้ตอบคำถามจริงๆ ผู้คนกำลังมองหาวิธีที่จะมีตัวหมุนโดยการแสดงเริ่มต้น "เลือกหนึ่ง" มากกว่ารายการแรกในรายการของดาวเคราะห์ในตัวอย่างนี้
JMRboosties

58

รหัสนี้ได้รับการทดสอบและใช้งานบน Android 4.4

ป้อนคำอธิบายรูปภาพที่นี่

Spinner spinner = (Spinner) activity.findViewById(R.id.spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity, android.R.layout.simple_spinner_dropdown_item) {

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {

                View v = super.getView(position, convertView, parent);
                if (position == getCount()) {
                    ((TextView)v.findViewById(android.R.id.text1)).setText("");
                    ((TextView)v.findViewById(android.R.id.text1)).setHint(getItem(getCount())); //"Hint to be displayed"
                }

                return v;
            }       

            @Override
            public int getCount() {
                return super.getCount()-1; // you dont display last item. It is used as hint.
            }

        };

        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        adapter.add("Daily");
        adapter.add("Two Days");
        adapter.add("Weekly");
        adapter.add("Monthly");
        adapter.add("Three Months");
        adapter.add("HINT_TEXT_HERE"); //This is the text that will be displayed as hint.


        spinner.setAdapter(adapter);
        spinner.setSelection(adapter.getCount()); //set the hint the default selection so it appears on launch.
        spinner.setOnItemSelectedListener(this);

getItem(getCount())ขีดเส้นใต้สีแดงสำหรับฉัน ไม่สามารถแก้ไขเมธอด setHint
Zapnologica

ฉันมีข้อสงสัยเห็นวิธีแก้ปัญหาต่าง ๆ ในกระทู้นี้ แต่ทำไมทุกคนเพิ่มคำใบ้ไปยังหน้าสุดท้าย การเพิ่มคำใบ้ในแถวแรกผิดหรือเปล่า
akashPatra

ฉันไม่สามารถตั้งค่า 'setOnItemSelectedListener (this);' ทำให้ฉันใช้ 'ใช้ NavigationViewOnNavigationItemSelectedListener' ฉันจะลบ 'setOnItemSelectedListener (this);' ไม่มีปัญหาใด ๆ ?
CGR

@akashpatra เหตุผลที่พวกเขาเพิ่มคำใบ้สุดท้ายคือ ArrayAdapter สำหรับสปินเนอร์อาจได้รับค่าจากแหล่งต่าง ๆ ในเวลาทำงาน
VinKrish

สิ่งนี้ช่วยฉันได้จริงๆ
อาทิตย์

31

ฉันพบวิธีแก้ปัญหานี้:

String[] items = new String[] {"Select One", "Two", "Three"};
Spinner spinner = (Spinner) findViewById(R.id.mySpinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);

spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id) {
        items[0] = "One";
        selectedItem = items[position];
    }

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

เพียงแค่เปลี่ยนอาร์เรย์ [0] ด้วย "เลือกหนึ่ง" จากนั้นใน onItemSelected เปลี่ยนชื่อเป็น "หนึ่ง"

ไม่ใช่วิธีที่ดีงาม แต่ใช้งานได้: D


5
สิ่งนี้ไม่ได้ผลสำหรับฉัน หลังจากเลือกรายการ "หนึ่ง" ยังคงระบุว่า "เลือกหนึ่งรายการ"
Leo Landau

สิ่งนี้จะไม่ทำงานเพราะอินเทอร์เฟซ onItemSelected จะโทรเป็นครั้งแรกเสมอ
Vaibhav Kadam

20

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

List<Object> objects = new ArrayList<Object>();
objects.add(firstItem);
objects.add(secondItem);
// add hint as last item
objects.add(hint);

HintAdapter adapter = new HintAdapter(context, objects, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

Spinner spinnerFilmType = (Spinner) findViewById(R.id.spinner);
spinner.setAdapter(adapter);

// show hint
spinner.setSelection(adapter.getCount());

แหล่งอะแดปเตอร์:

public class HintAdapter
        extends ArrayAdapter<Objects> {

    public HintAdapter(Context theContext, List<Object> objects) {
        super(theContext, android.R.id.text1, android.R.id.text1, objects);
    }

    public HintAdapter(Context theContext, List<Object> objects, int theLayoutResId) {
        super(theContext, theLayoutResId, android.R.id.text1, objects);
    }

    @Override
    public int getCount() {
        // don't display last item. It is used as hint.
        int count = super.getCount();
        return count > 0 ? count - 1 : count;
    }
}

แหล่งต้นฉบับ


R.id.text1 คืออะไร มันเป็นเค้าโครงหรือมุมมองใด ๆ โปรดอธิบายคำตอบของคุณอย่างละเอียด
Anand Savjani

มันควรจะเป็นandroid.R.id.text1
Yakiv Mospan

ฉันมีข้อสงสัยเห็นวิธีแก้ปัญหาต่าง ๆ ในกระทู้นี้ แต่ทำไมทุกคนเพิ่มคำใบ้ไปยังหน้าสุดท้าย การเพิ่มคำใบ้ในแถวแรกผิดหรือเปล่า
akashPatra

@ akashpatra ฉันจำไม่ได้ แต่ดูเหมือนว่ามีปัญหาบางอย่างเมื่อฉันพยายามทำให้มันเป็นรายการแรก อย่างไรก็ตามคุณสามารถลองและแสดงความคิดเห็นได้ที่นี่ความมหัศจรรย์ทั้งหมดที่นี่เป็นgetCountวิธีรอบตัว
Yakiv Mospan

@ YakivMospan ฉันได้รับ NPE เมื่อฉันใช้สิ่งนี้อาจเป็นเพราะ Reflection เมื่อใช้ ProGuard คุณรู้วิธีแก้ไขปัญหานี้หรือไม่?
อลัน

17

มีคำตอบมากมายที่นี่ แต่ฉันแปลกใจที่ไม่มีใครแนะนำวิธีแก้ปัญหาง่ายๆ: วาง TextView ไว้บนสปินเนอร์ ตั้งฟังการคลิกบน TextView ซึ่งซ่อน TextView แสดง Spinner และเรียก spinner.performClick ()


9

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

Spinner lCreditOrDebit = (Spinner)lCardPayView.findViewById(R.id.CARD_TYPE);
spinneradapter lAdapter = 
  new spinneradapter(
    BillPayScreen.this, 
    ndroid.R.layout.simple_spinner_item,getResources().getStringArray(R.array.creditordebit));
lAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
lCreditOrDebit.setAdapter(lAdapter);

ที่นี่ spinneradapter เป็นการปรับแต่งเล็ก ๆ สำหรับ arrayadapter ดูเหมือนว่านี้:

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;

public class spinneradapter extends ArrayAdapter<String>{
    private Context m_cContext;
    public spinneradapter(Context context,int textViewResourceId, String[] objects) {
        super(context, textViewResourceId, objects);
        this.m_cContext = context;
    }

    boolean firsttime = true;
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(firsttime){
            firsttime = false;
            //Just return some empty view
            return new ImageView(m_cContext);
        }
        //Let the array adapter take care of it this time.
        return super.getView(position, convertView, parent);
    }
}

6
ปัญหาเกี่ยวกับวิธีการนี้คือมันยังคงเลือกรายการแรกในรายการเมื่อรายการปรากฏขึ้น เนื่องจากตัวเลือกนี้ถูกเลือกไว้แล้วคุณไม่สามารถแตะเพื่อเลือก - มันทำหน้าที่เหมือนไม่มีการเลือกเกิดขึ้น
jwadsack


6

ไฟล์ XML:

<Spinner android:id="@+id/locationSpinner"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:prompt="@string/select_location" />

กิจกรรม:

private Spinner featuresSelection;
private ArrayAdapter<CharSequence> featuresAdapter;
private List<CharSequence> featuresList;

onCreate:

featuresList = new ArrayList<CharSequence>();
featuresAdapter = new ArrayAdapter<CharSequence>(this,
  android.R.layout.simple_spinner_item, featuresList);
featuresAdapter.setDropDownViewResource(
  android.R.layout.simple_spinner_dropdown_item);
featuresSelection = ((Spinner) yourActivity.this
  .findViewById(R.id.locationSpinner));
featuresSelection.setAdapter(featuresAdapter);
featuresSelection.setOnItemSelectedListener(
  new MyOnItemSelectedListener());

ฟังก์ชั่นบางอย่าง (เพิ่มสิ่งต่าง ๆ ลงในอะแดปเตอร์โดยทางโปรแกรม)>

featuresAdapter.add("some string");

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


นอกจากนี้ไม่จำเป็นต้องแจ้งให้ทราบ DataSetChanged () ตามที่ควรตั้งค่าเป็นจริง
trgraglia

4

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

ประกาศเป็นตัวแปรส่วนกลางการแจ้งเตือนไดอะล็อกและค่าเริ่มต้น ..

AlertDialog d;
static int default_value = 0;
final Button btn = (Button) findViewById(R.id.button1);
btn .setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v)
{
    //c.show();
    final CharSequence str[] = {"Android","Black Berry","Iphone"};
        AlertDialog.Builder builder =
          new AlertDialog.Builder(TestGalleryActivity.this).setSingleChoiceItems(
            str, default_value,new  DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int position)
            {
                Toast.makeText(TestGalleryActivity.this,
                               "" + position,
                               Toast.LENGTH_SHORT).show();
                default_value = position;
                btn.setText(str[position]);
                if(d.isShowing())
                    d.dismiss();
            }
        }).setTitle("Select Any");
        d = builder.create();
        d.show();
    }
});

4

นี่คือวิธีของฉัน:

List<String> list = new ArrayList<String>();
list.add("string1");
list.add("string2");
list.add("string3");
list.add("[Select one]");
final int listsize = list.size() - 1;
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, list) {
 @Override
public int getCount() {
    return(listsize); // Truncate the list
}
};
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);

mySpinner.setSelection(listsize); // Hidden item to appear in the spinner

สิ่งนี้จะเปิดเครื่องปั่นด้ายที่ตำแหน่งด้านล่าง
SpyZip

3

ลองดูที่แอป iosched สำหรับโซลูชันเอนกประสงค์เพื่อเพิ่มองค์ประกอบไว้ด้านบนสุดของรายการ โดยเฉพาะถ้าคุณใช้ CursorAdapter ให้ดูที่TracksAdapter.javaซึ่งขยายคำจำกัดความดังกล่าวเพื่อให้วิธีการ "setHasAllItem" และรหัสที่เกี่ยวข้องเพื่อจัดการจำนวนรายการที่จะจัดการกับรายการพิเศษที่ด้านบน

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


3

ฉันมีสปินเนอร์บน main.xml และรหัสคือ @+id/spinner1

นี่คือสิ่งที่ฉันเขียนในฟังก์ชั่น OnCreate ของฉัน:

spinner1 = (Spinner)this.findViewById(R.id.spinner1);
final String[] groupes = new String[] {"A", "B", "C", "D", "E", "F", "G", "H"};
ArrayAdapter<CharSequence> featuresAdapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, new ArrayList<CharSequence>());
featuresAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner1.setAdapter(featuresAdapter);
for (String s : groupes) featuresAdapter.add(s);

spinner1.setOnItemSelectedListener(new OnItemSelectedListener() {
     public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id) {
         // Here go your instructions when the user chose something
         Toast.makeText(getBaseContext(), groupes[position], 0).show();
     }
     public void onNothingSelected(AdapterView<?> arg0) { }
});

ไม่จำเป็นต้องมีการนำไปใช้ในชั้นเรียน


3

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

ดังนั้นฉันจึงใส่รายการคำใบ้ไว้ที่จุดเริ่มต้นของรายการ และซ่อนรายการแรกในรายการแบบหล่นลง

private void loadSpinner(){

    HintArrayAdapter hintAdapter = new HintArrayAdapter<String>(context, 0);

    hintAdapter.add("Hint to be displayed");
    hintAdapter.add("Item 1");
    hintAdapter.add("Item 2");
            .
            .
    hintAdapter.add("Item 30");

    spinner1.setAdapter(hintAdapter);

    //spinner1.setSelection(0); //display hint. Actually you can ignore it, because the default is already 0
    //spinner1.setSelection(0, false); //use this if don't want to onItemClick called for the hint

    spinner1.setOnItemSelectedListener(yourListener);
}

private class HintArrayAdapter<T> extends ArrayAdapter<T> {

    Context mContext;

    public HintArrayAdapter(Context context, int resource) {
        super(context, resource);
        this.mContext = context
    }

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(android.R.layout.simple_spinner_item, parent, false);
        TextView texview = (TextView) view.findViewById(android.R.id.text1);

        if(position == 0) {
            texview.setText("");
            texview.setHint(getItem(position).toString()); //"Hint to be displayed"
        } else {
            texview.setText(getItem(position).toString());
        }

        return view;
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        View view;

        if(position == 0){
            view = inflater.inflate(R.layout.spinner_hint_list_item_layout, parent, false); // Hide first row
        } else {
            view = inflater.inflate(android.R.layout.simple_spinner_dropdown_item, parent, false);
            TextView texview = (TextView) view.findViewById(android.R.id.text1);
            texview.setText(getItem(position).toString());
        } 

        return view;
    }
}

กำหนดเค้าโครงด้านล่างใน @Override getDropDownView () เมื่อตำแหน่งเป็น 0 เพื่อซ่อนแถวคำใบ้แรก

R.layout.spinner_hint_list_item_layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

</LinearLayout>

3

นอกจากนี้ยังมีเคล็ดลับง่ายๆในการแสดงค่าเริ่มต้น:

คุณสามารถเพิ่มค่าเริ่มต้นในรายการของคุณแล้วเพิ่มคอลเลกชันทั้งหมดของคุณโดยใช้ list.addAll(yourCollection);

ตัวอย่างรหัสที่ใช้การได้ที่นี่:

List<FuelName> fuelList = new ArrayList<FuelName>();
                    fuelList.add(new FuelName(0,"Select One"));
                    fuelList.addAll(response.body());
                    ArrayAdapter adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_item, fuelList);
                    //fuelName.setPrompt("Select Fuel");
                    fuelName.setAdapter(adapter);

หวังว่ามันจะกู้คืนความซับซ้อนของคุณ การเข้ารหัสที่มีความสุข!


2

ฉันคิดว่าวิธีที่ง่ายที่สุดคือการสร้างรายการจำลองในดัชนี 0 บอกว่า "เลือกหนึ่งรายการ" และจากนั้นในการบันทึกอาจตรวจสอบว่าการเลือกไม่ใช่ 0


4
สิ่งที่เกี่ยวกับการดูรายการ? คุณต้องการที่จะเห็นตำแหน่ง "เลือกหนึ่ง" ที่ด้านบนหรือไม่ มันไม่ใช่แค่เรื่องของการออม
Krzysztof Wolny

@KrzysztofWolny Spinner ตามค่าเริ่มต้นจะแสดงรายการที่ตำแหน่ง 0
rds

2

นี่คือตัวอย่างสุดท้ายของฉัน "all-in" สำหรับปุ่มสปินเนอร์

ในactivity_my_form.xml

    <Button
        android:id="@+id/btnSpinnerPlanets"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="left|center_vertical"
        android:singleLine="true"
        android:text="@string/selectAPlanet"
        android:textSize="10sp"
        android:background="@android:drawable/btn_dropdown">
    </Button>

ในstrings.xml

<string name="selectAPlanet">Select planet&#8230;</string>

<string-array name="planets__entries">
    <item>The Sun with a name very long so long long long long longThe Sun with a name very long so long long long long longThe Sun with a name very long so long long long long long</item>
    <item>Mercury</item>
    <item>Venus</item>
    <item>Earth</item>
    <item>Mars</item>
    <item>Jupiter</item>
    <item>Saturn</item>
    <item>Uranus</item>
    <item>Neptune</item>
</string-array>

ในMyFormActivity.java

public class MyFormActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ((Button) findViewById(R.id.btnSpinnerPlanets)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                final String[] items = view.getResources().getStringArray(R.array.planets__entries);
                ArrayAdapter<String> adapter = new ArrayAdapter<String>(MyFormActivity.this, android.R.layout.simple_spinner_dropdown_item, items);
                new AlertDialog.Builder(MyFormActivity.this).setTitle("the prompt").setAdapter(adapter, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ((Button) findViewById(R.id.btnSpinnerPlanets)).setText(items[which]);
                        dialog.dismiss();
                    }
                }).create().show();
            }
        });     

    }

}   

ในที่สุดฉันก็ได้ขนาดแบบอักษรที่กำหนดได้ไม่มีรายการปุ่มหมุนตัวแรกที่เลือกได้ !!! ขอบคุณ HRJ


1

เมื่อขยายSpinnerAdapterคุณแทนที่สองViewวิธี -producing, และgetView(int, View, ViewGroup) getDropDownView(int, View, ViewGroup)คนแรกที่Viewใส่เข้าไปในSpinnerตัวของมันเอง; รายการที่สองส่งมอบViewในรายการแบบหล่นลง (ตามชื่อที่แนะนำ) คุณสามารถลบล้างสิ่งgetView(...)นั้นได้จนกว่าจะมีการเลือกรายการใด ๆ มันจะแสดงTextViewข้อความแจ้งที่มี จากนั้นเมื่อคุณตรวจพบรายการที่ถูกเลือกคุณจะเปลี่ยนมันเพื่อแสดงรายการที่TextViewเกี่ยวข้อง

public class PromptingAdapter extends SpinnerAdapter {

    //... various code ...

    private boolean selectionmade = false;

    //call this method from the OnItemSelectedListener for your Spinner
    public setSelectionState(boolean b) {
        selectionmade = b;
    }

    @Override
    public View getView(int position, View recycle, ViewGroup container) {
        if(selectionmade) {
            //your existing code to supply a View for the Spinner
            //you could even put "return getDropDownView(position, recycle, container);"
        }
        else {
            View output;
            if(recycle instanceof TextView) {
                 output = recycle;
            }
            else {
                 output = new TextView();
                 //and layout stuff
            }
            output.setText(R.string.please_select_one);
            //put a string "please_select_one" in res/values/strings.xml
            return output;
        }
    }

//...
}

1
ฉันค้นพบข้อบกพร่องในวิธีนี้: สปินเนอร์เลือกรายการโดยอัตโนมัติทันที ฉันจะหาทางรอบนี้ในไม่ช้า
Andrew Wyld

ฉันพูดเร็วเกินไป อย่างไรก็ตามฉันยังไม่ยอมแพ้ โปรดทราบว่าตามSpinnerบทช่วยสอน (ซึ่งคาดคะเนว่าจะแสดงToastหลังจากคุณได้เลือกรายการ) OUGHT นี้เพื่อใช้งาน: developer.android.com/resources/tutorials/views/…
Andrew Wyld

1

สำหรับผู้ที่ใช้ Xamarin นี่คือ C # เทียบเท่ากับคำตอบของ aaronvargas ด้านบน

using Android.Content;
using Android.Database;
using Android.Views;
using Android.Widget;
using Java.Lang;

namespace MyNamespace.Droid
{ 
  public class NothingSelectedSpinnerAdapter : BaseAdapter, ISpinnerAdapter, IListAdapter
  {
    protected static readonly int EXTRA = 1;
    protected ISpinnerAdapter adapter;
    protected Context context;
    protected int nothingSelectedLayout;
    protected int nothingSelectedDropdownLayout;
    protected LayoutInflater layoutInflater;

    public NothingSelectedSpinnerAdapter(ISpinnerAdapter spinnerAdapter, int nothingSelectedLayout, Context context) : this(spinnerAdapter, nothingSelectedLayout, -1, context)
    {
    }

    public NothingSelectedSpinnerAdapter(ISpinnerAdapter spinnerAdapter, int nothingSelectedLayout, int nothingSelectedDropdownLayout, Context context)
    {
      this.adapter = spinnerAdapter;
      this.context = context;
      this.nothingSelectedLayout = nothingSelectedLayout;
      this.nothingSelectedDropdownLayout = nothingSelectedDropdownLayout;
      layoutInflater = LayoutInflater.From(context);
    }

    protected View GetNothingSelectedView(ViewGroup parent)
    {
      return layoutInflater.Inflate(nothingSelectedLayout, parent, false);
    }

    protected View GetNothingSelectedDropdownView(ViewGroup parent)
    {
      return layoutInflater.Inflate(nothingSelectedDropdownLayout, parent, false);
    }

    public override Object GetItem(int position)
    {
      return position == 0 ? null : adapter.GetItem(position - EXTRA);
    }

    public override long GetItemId(int position)
    {
      return position >= EXTRA ? adapter.GetItemId(position - EXTRA) : position - EXTRA;
    }

    public override View GetView(int position, View convertView, ViewGroup parent)
    {
      // This provides the View for the Selected Item in the Spinner, not
      // the dropdown (unless dropdownView is not set).
      if (position == 0)
      {
        return GetNothingSelectedView(parent);
      }

      // Could re-use the convertView if possible.
      return this.adapter.GetView(position - EXTRA, null, parent);
    }

    public override int Count
    {
      get
      {
        int count = this.adapter.Count;
        return count == 0 ? 0 : count + EXTRA;
      }
    }

    public override View GetDropDownView(int position, View convertView, ViewGroup parent)
    {
      // Android BUG! http://code.google.com/p/android/issues/detail?id=17128 -
      // Spinner does not support multiple view types
      if (position == 0)
      {
        return nothingSelectedDropdownLayout == -1 ?
          new View(context) :
          GetNothingSelectedDropdownView(parent);
      }

      // Could re-use the convertView if possible, use setTag...
      return adapter.GetDropDownView(position - EXTRA, null, parent);
    }

    public override int GetItemViewType(int position)
    {
      return 0;
    }

    public override int ViewTypeCount => 1;

    public override bool HasStableIds => this.adapter.HasStableIds;

    public override bool IsEmpty => this.adapter.IsEmpty;

    public override void RegisterDataSetObserver(DataSetObserver observer)
    {
      adapter.RegisterDataSetObserver(observer);
    }

    public override void UnregisterDataSetObserver(DataSetObserver observer)
    {
      adapter.UnregisterDataSetObserver(observer);
    }

    public override bool AreAllItemsEnabled()
    {
      return false;
    }

    public override bool IsEnabled(int position)
    {
      return position > 0;
    }
  }
}

1

ฉันยังแก้ไขปัญหานี้โดยใช้รหัสต่อไปนี้ สมมติว่าคุณมีรายการสิ่งของเช่น

ArrayList<Item> itemsArrayList = new ArrayList<Item>();
Item item1 = new Item();
item1.setId(1);
item1.setData("First Element");
Item item2 = new Item();
item2.setId(2);
Item2.setData("Second Element");
itemsArrayList.add(item1);
itemsArrayList.add(item2);

ตอนนี้เราต้องจัดเตรียมสปินเนอร์เพราะสปินเนอร์ไม่สามารถเข้าใจวัตถุได้ ดังนั้นเราจะสร้างรายการอาร์เรย์ใหม่ที่มีรายการสตริงเช่นนี้ ->

ArrayList<String> itemStringArrayList = new ArrayList<String>();
for(Item item : itemsArrayList) {
    itemStringArrayList.add(item.getData());
}

ตอนนี้เรามีitemStringArrayListรายการอาร์เรย์ที่มีสองรายการสตริง และเราต้องแสดงข้อความ "เลือกรายการ" เป็นรายการแรก itemStringArrayListดังนั้นเราจึงมีการแทรกสตริงใหม่เข้าสู่

itemStringArrayList.add("Select Item");

ตอนนี้เรามีรายการอาร์เรย์itemsArrayListและเราต้องการแสดงองค์ประกอบสองรายการในรายการแบบหล่นลง แต่เงื่อนไขที่นี่คือ ... หากเราไม่เลือกอะไรก็Select Itemควรปรากฏเป็นองค์ประกอบแรกซึ่งจะไม่เปิดใช้งาน

ดังนั้นเราสามารถใช้ฟังก์ชั่นนี้ได้เช่นนี้ หากคุณต้องการโหลดรายการในอาเรย์ลงในเครื่องปั่นด้าย Android ดังนั้นคุณจะต้องใช้อะแดปเตอร์ ArrayAdapterดังนั้นที่นี่ฉันจะใช้ เราสามารถใช้อะแดปเตอร์ปรับแต่งได้เช่นกัน

ArrayAdapter<String> itemsArrayAdapter = new ArrayAdapter<String>(getApplicationContext(), R.layout.spinner_item, itemsArrayList){
        @Override
        public boolean isEnabled(int position) {
            if(position == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        @Override
        public View getDropDownView(int position, View convertView,
                                    ViewGroup parent) {
            View view = super.getDropDownView(position, convertView, parent);
            TextView tv = (TextView) view;
            if(position == 0){
                // Set the hint text color gray
                tv.setTextColor(Color.GRAY);
            }
            else {
                tv.setTextColor(Color.BLACK);
            }
            return view;
        }
    };

itemsArrayAdapter.setDropDownViewResource(R.layout.spinner_item);
your_spinner_name.setAdapter(itemsArrayAdapter);

ที่นี่ในรหัสนี้ R.layout.spinner_itemเรากำลังใช้กำหนดเองปั่นรูปแบบคือ มันเป็นมุมมองข้อความที่เรียบง่าย

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp"
    android:textStyle="italic"
    android:fontFamily="sans-serif-medium"
    />

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


0

ฉันจะใช้ RadioGroup กับ RadioButtons ถ้าคุณมีสามตัวเลือกเท่านั้นคุณสามารถยกเลิกการเลือกทั้งหมดในตอนแรก


0

ไม่มีคำตอบที่ส่งไปก่อนหน้านี้ทำงานได้จริงอย่างที่ฉันต้องการแก้ไขปัญหานี้ สำหรับฉันทางออกที่ดีที่สุดจะให้“ เลือกหนึ่ง” (หรือข้อความเริ่มต้นใด ๆ ) เมื่อสปินเนอร์ปรากฏขึ้นเป็นครั้งแรก เมื่อผู้ใช้แตะที่สปินเนอร์ข้อความเริ่มต้นไม่ควรเป็นส่วนหนึ่งของดรอปดาวน์ที่แสดงขึ้นมา

เพื่อเพิ่มความซับซ้อนให้กับสถานการณ์เฉพาะของฉันข้อมูลสปินเนอร์ของฉันจะมาเป็นเคอร์เซอร์ที่โหลดผ่านการเรียกกลับ LoaderManager

หลังจากการทดลองจำนวนมากฉันคิดวิธีแก้ปัญหาต่อไปนี้ขึ้นมา:

public class MyFragment extends Fragment implements
LoaderManager.LoaderCallbacks<Cursor>{

private static final String SPINNER_INIT_VALUE = "Select A Widget";
private Spinner mSpinner;
private int mSpinnerPosition;
private boolean mSpinnerDropDownShowing = false;
private View mSpinnerDropDown;

private MyCursorAdapter mCursorAdapter;

...

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
...

mCursorAdapter = new MyCursorAdapter(getActivity());

mSpinner = (Spinner) rootView.findViewById(R.id.theSpinner);
mSpinner.setOnTouchListener(mSpinnerTouchListener);
mSpinner.setAdapter(mCursorAdapter);

...
}

//Capture the touch events to toggle the spinner's dropdown visibility
private OnTouchListener mSpinnerTouchListener = new View.OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        if(mSpinnerDropDown != null && mSpinnerDropDownShowing == false){
            mSpinnerDropDownShowing = true;
            mSpinnerDropDown.setVisibility(View.VISIBLE);
        }
        return false;
    }
};

//Capture the click event on the spinner drop down items
protected OnClickListener spinnerItemClick = new OnClickListener(){

    @Override
    public void onClick(View view) {
        String widget = ((TextView) view.findViewById(android.R.id.text1)).getText().toString();

        if(!widget.equals(SPINNER_INIT_VALUE)){
            if(mCursorAdapter != null){
                Cursor cursor = mCursorAdapter.getCursor();
                if(cursor.moveToFirst()){
                    while(!cursor.isAfterLast()){
                        if(widget.equals(cursor.getString(WidgetQuery.WIDGET_NAME))){

                            ...

                            //Set the spinner to the correct item
                            mSpinnerPosition = cursor.getPosition() + 1;
                            mSpinner.setSelection(mSpinnerPosition);
                            break;
                        }
                        cursor.moveToNext();
                    }
                }
            }
        }

        //Hide the drop down. Not the most elegent solution but it is the only way I could hide/dismiss the drop down
        mSpinnerDropDown = view.getRootView();
        mSpinnerDropDownShowing = false;
        mSpinnerDropDown.setVisibility(View.GONE);
    }
};

private class MyCursorAdapter extends CursorAdapter {

    private final int DISPLACEMENT = 1;
    private final int DEFAULT_ITEM_ID = Integer.MAX_VALUE;

    private Activity mActivity;

    public MyCursorAdapter(Activity activity) {
            super(activity, null, false);
            mActivity = activity;
    }

    //When loading the regular views, inject the defualt item
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(position == 0){
            if(convertView == null){
                convertView = mActivity.getLayoutInflater().inflate(R.layout.list_item_widget, parent, false);
            }
            return getDefaultItem(convertView);
        }
        return super.getView(position - DISPLACEMENT, convertView, parent);
    }

    //When loading the drop down views, set the onClickListener for each view
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent){
        View view = super.getDropDownView(position, convertView, parent);
        view.setOnClickListener(spinnerItemClick);
        return view;
    }

    //The special default item that is being injected
    private View getDefaultItem(View convertView){
        TextView text = (TextView) convertView.findViewById(android.R.id.text1);
        text.setText(SPINNER_INIT_VALUE);
        return convertView;
    }

    @Override
    public long getItemId(int position) {
        if (position == 0) {
            return DEFAULT_ITEM_ID;
        }
        return super.getItemId(position - DISPLACEMENT);
    }

    @Override
    public boolean isEnabled(int position) {
        return position == 0 ? true : super.isEnabled(position - DISPLACEMENT);
    }

    @Override
    public int getViewTypeCount() {
        return super.getViewTypeCount() + DISPLACEMENT;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return super.getViewTypeCount();
        }

        return super.getItemViewType(position - DISPLACEMENT);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return mActivity.getLayoutInflater().inflate(R.layout.list_item_widget, parent, false);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor){

        if(cursor.isAfterLast()){
            return;
        }

        TextView text = (TextView) view.findViewById(android.R.id.text1);
        String WidgetName = cursor.getString(WidgetQuery.WIDGET_NAME);
        text.setText(WidgetName);
    }
}
}

0

ฉันจัดการสิ่งนี้โดยใช้ปุ่มแทนสปินเนอร์ ฉันมีโครงการตัวอย่างบน GitHub

ในโครงการฉันกำลังแสดงทั้งปินเนอร์และปุ่มเพื่อแสดงว่าพวกเขามีลักษณะเหมือนกัน ยกเว้นปุ่มที่คุณสามารถตั้งค่าข้อความเริ่มต้นเป็นสิ่งที่คุณต้องการ

กิจกรรมมีลักษณะดังนี้:

package com.stevebergamini.spinnerbutton;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Spinner;

public class MainActivity extends Activity {

    Spinner spinner1;
    Button button1;
    AlertDialog ad;
    String[] countries;

    int selected = -1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        spinner1 = (Spinner) findViewById(R.id.spinner1);
        button1 = (Button) findViewById(R.id.button1);

        countries = getResources().getStringArray(R.array.country_names);

        //  You can also use an adapter for the allert dialog if you'd like
        //  ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, countries);        

        ad = new AlertDialog.Builder(MainActivity.this).setSingleChoiceItems(countries, selected,  
                new  DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            button1.setText(countries[which]);
                            selected = which;
                            ad.dismiss();

                        }}).setTitle(R.string.select_country).create(); 


        button1.setOnClickListener( new OnClickListener(){

            @Override
            public void onClick(View v) {
                ad.getListView().setSelection(selected);
                ad.show();              
            }});

    }

}

หมายเหตุ: ใช่ฉันรู้ว่าสิ่งนี้ขึ้นอยู่กับธีมที่นำไปใช้และรูปลักษณ์จะแตกต่างกันเล็กน้อยหากใช้ Theme.Holo อย่างไรก็ตามหากคุณใช้หนึ่งในธีมดั้งเดิมเช่น Theme.Black คุณก็พร้อมที่จะไป


0

หากคุณประสบปัญหานี้เมื่อรายการของคุณถูกเติมจากฐานข้อมูลเคอร์เซอร์ ,

ทางออกที่ง่ายที่สุดที่ฉันพบในคำตอบ SO นี้ :

ใช้ UNION ในเคียวรีอะแดปเตอร์เคอร์เซอร์ของคุณและเพิ่มรายการเพิ่มเติมด้วย id = -1 ให้กับผลลัพธ์การสืบค้นโดยไม่ต้องเพิ่มลงในฐานข้อมูลจริงๆ:

สิ่งที่ต้องการ:

db.rawQuery ("SELECT iWorkerId เป็น _id, nvLastName เป็นชื่อจากผู้ทำงาน w UNION SELECT -1 เป็น _id, '' เป็นชื่อ", null);

หากรายการที่เลือกคือ -1 แสดงว่าเป็นค่าเริ่มต้น มิฉะนั้นจะเป็นบันทึกจากตาราง


0

ดูเหมือนเป็นวิธีแก้ปัญหา แต่ฉันมักจะใส่ TextView ไว้ด้านหน้าสปินเนอร์ Xml ทั้งหมดมีลักษณะเช่นนี้ (เฮ้พวกอย่ายิงฉันฉันรู้ว่าบางคนไม่ชอบการแต่งงานแบบนี้):

<FrameLayout
    android:id="@+id/selectTypesLinear"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <Spinner
        android:id="@+id/spinnerExercises"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:entries="@array/exercise_spinner_entries"
        android:prompt="@string/exercise_spinner_prompt"
     />                         
    <TextView
        android:id="@+id/spinnerSelectText"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Hey! Select this guy!"
        android:gravity="center"
        android:background="#FF000000" />


</FrameLayout>

จากนั้นฉันซ่อน TextView เมื่อรายการถูกเลือก เห็นได้ชัดว่าสีพื้นหลังของ TextView ควรจะเหมือนกับสปินเนอร์ ทำงานบน Android 4.0 ไม่ทราบรุ่นที่เก่ากว่า

ใช่. เนื่องจากการเรียกสายสปินเนอร์ setOnItemSelectedListener ที่จุดเริ่มต้นการซ่อนมุมมองข้อความอาจยุ่งยากเล็กน้อย แต่สามารถทำได้ด้วยวิธีนี้:

    Boolean controlTouched;

    exerciseSpinner.setOnTouchListener(new OnTouchListener() {


        @Override
        public boolean onTouch(View v, MotionEvent event) {
            controlTouched = true; // I touched it but but not yet selected an Item.
            return false;
        }

    });
    exerciseSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> arg0, View arg1,
                int arg2, long arg3) {
            if (controlTouched) { // Are you sure that I touched it with my fingers and not someone else  ?
                spinnerSelText.setVisibility(View.GONE);
            }
        }

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

    });

0

สำหรับฉันมันทำงานอะไรเช่นนี้ มีการปรับปรุงที่เปลี่ยนแปลงข้อความในตัวเลือก SOME เท่านั้นไม่ใช่ทั้งหมด

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

public void rellenarSpinnerCompeticiones(){
        spinnerArrayCompeticiones = new ArrayList<String>();
        for(Competicion c: ((Controlador)getApplication()).getCompeticiones()){
            spinnerArrayCompeticiones.add(c.getNombre());
        }
        //ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(this,R.layout.spinner_item_competicion,spinnerArrayCompeticiones);
        ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(this, R.layout.spinner_item_competicion, spinnerArrayCompeticiones){
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                final View v = vi.inflate(R.layout.spinner_item_competicion, null);
                final TextView t = (TextView)v.findViewById(R.id.tvCompeticion);
                if(spinnerCompeticion.getSelectedItemPosition()>0){
                    t.setText(spinnerArrayCompeticiones.get(spinnerCompeticion.getSelectedItemPosition()));
                }else{
                    t.setText("Competiciones");
                }
                return v;
            }
        };
        spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinnerCompeticion.setAdapter(spinnerArrayAdapter);
    }

0

นี่คือหนึ่งที่เรียบง่าย

    private boolean isFirst = true;
private void setAdapter() {
    final ArrayList<String> spinnerArray = new ArrayList<String>();     
    spinnerArray.add("Select your option");
    spinnerArray.add("Option 1");
    spinnerArray.add("Option 2");
    spin.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
            TextView tv = (TextView)selectedItemView;
            String res = tv.getText().toString().trim();
            if (res.equals("Option 1")) {
            //do Something
        } else if (res.equals("Option 2")) {
            //do Something else
        }
        }

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

    });

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.my_spinner_style,spinnerArray) {
         public View getView(int position, View convertView, ViewGroup parent) {
             View v = super.getView(position, convertView, parent);
             int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25, getResources().getDisplayMetrics());                  
             ((TextView) v).setTypeface(tf2);
             ((TextView) v).getLayoutParams().height = height;
             ((TextView) v).setGravity(Gravity.CENTER);
             ((TextView) v).setTextSize(TypedValue.COMPLEX_UNIT_SP, 19);
             ((TextView) v).setTextColor(Color.WHITE);
             return v;
         }

         public View getDropDownView(int position, View convertView,
                 ViewGroup parent) {
             if (isFirst) {
                 isFirst = false;
                 spinnerArray.remove(0);
             }
             View v = super.getDropDownView(position, convertView, parent);                  
             ((TextView) v).setTextColor(Color.argb(255, 70, 70, 70));
             ((TextView) v).setTypeface(tf2);
             ((TextView) v).setGravity(Gravity.CENTER);
             return v;
         }
     };
     spin.setAdapter(adapter);
}

0

อ้างถึงหนึ่งในคำตอบข้างต้น: https://stackoverflow.com/a/23005376/1312796

ฉันเพิ่มรหัสเพื่อแก้ไขข้อผิดพลาดเล็กน้อย ที่ซึ่งไม่มีการดึงข้อมูล .. วิธีการแสดงข้อความแจ้ง .. !

นี่คือเคล็ดลับของฉัน ... มันใช้ได้ดีกับฉัน !

ลองวางสปินเนอร์ใน Relative_layout และจัดแนว Textview กับสปินเนอร์ของคุณและเล่นกับทัศนวิสัยของ Textview (SHOW / HIDE) ทุกครั้งที่อแด็ปเตอร์ของสปินเนอร์โหลดหรือเปล่า ..

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="20dp"
android:background="#ededed"
android:orientation="vertical">



    <TextView
        android:id="@+id/txt_prompt_from"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:textColor="@color/gray"
        android:textSize="16sp"
        android:layout_alignStart="@+id/sp_from"
        android:text="From"
        android:visibility="gone"/>

    <Spinner
        android:id="@+id/sp_from"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        />

นี่คือรหัส:

  txt__from = (TextView) rootView.findViewById(R.id.txt_prompt_from);

เรียกวิธีนี้หลังจากและก่อนที่จะโหลดอะแดปเตอร์ปินเนอร์และเปล่า

setPromptTextViewVisibility (); //True or fales 

public void setPromptTextViewVisibility (boolean visible )
{
    if (visible)
    {
        txt_from.setVisibility(View.VISIBLE);
    }
    else
    {
        txt_from.setVisibility(View.INVISIBLE);
    }

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