1. ホーム
  2. android

[解決済み] 初期テキストが「Select One」のAndroidスピナーを作るには?

2022-03-16 12:09:16

質問

スピナーを使いたいのですが、初期状態(ユーザーがまだ選択していない状態)では、"Select One"というテキストが表示されます。ユーザーがスピナーをクリックすると、項目のリストが表示され、ユーザーは選択肢の中から一つを選択します。ユーザーが選択した後は、"Select One"の代わりに選択された項目がSpinnerに表示されます。

Spinnerを作成するために以下のコードを用意しました。

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

このコードでは、初期状態では、項目 "One" が表示されています。新しい項目 "Select One" を項目に追加すればいいのですが、そうすると "Select One" も最初の項目としてドロップダウンリストに表示されてしまい、これは私が望んでいるものではありません。

どうすればこの問題を解決できますか?

解決方法は?

をオーバーライドする一般的な解決策を紹介します。 Spinner ビューを表示します。これは setAdapter() を使用して初期位置を -1 に設定し、提供された SpinnerAdapter を使用して、0 未満の位置でプロンプト文字列を表示する。

Android 1.5から4.2まででテストしていますが、買い手は要注意です! この解決策はリフレクションに依存しているため、プライベートな AdapterView.setNextSelectedPositionInt()AdapterView.setSelectedPositionInt() 将来のOSアップデートでの動作は保証されていません。動作する可能性は高いと思われますが、決して保証されているわけではありません。

通常、私はこのようなことを容認しませんが、この質問は何度もされており、十分に合理的な要求であると思われるので、私の解決策を投稿しようと思いました。

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