Как использовать Android Spinner как выпадающий список

Мне потребовалось довольно много времени, чтобы обойти Android Spinner. После нескольких неудачных попыток реализации и после прочтения многих вопросов, частично похожих на мои собственные, но без удовлетворительных ответов, а некоторые вообще без ответов, например здесь и здесь, Я, наконец, понимаю, что" spinner "в Android не означает то же самое, что" выпадающий список " из настольных приложений или выбор в HTML. Однако, что мое приложение (и я угадывание приложений всех других плакатов, чьи вопросы похожи) - это то, что работает как раскрывающийся список, а не как счетчик.

мои две проблемы с тем, что я сначала считал идиосинкразиями OnItemSelectedListener (я видел их как отдельные вопросы на этом сайте, но не как один):

  1. первоначальный выбор первого элемента списка, запускается автоматически, без вмешательства пользователя.
  2. когда элемент, который был уже выбран, снова выбран пользователем, он игнорируется.

теперь я понимаю, что когда вы думаете об этом, это имеет смысл, чтобы это произошло на счетчик - он должен начинаться с выбранного значения по умолчанию, и вы вращаете его только для изменения этого значения, а не для" повторного выбора " значения -документация фактически говорит:"этот обратный вызов вызывается только тогда, когда вновь выбранная позиция отличается от ранее выбранной позиции". И Я видел ответы, предполагающие, что вы установили флаг игнорировать первый автоматический выбор - я думаю, я мог бы жить с этим, если нет другого способа.

но поскольку то, что я действительно хочу, - это раскрывающийся список, который ведет себя как раскрывающийся список (и как пользователи могут и должны ожидать), мне нужно что-то как счетчик, который ведет себя как раскрывающийся список, как комбо-бокс. Меня не волнует автоматический предварительный выбор (это должно произойти без запуск моего слушателя), и я хочу знать о выбор, даже если он такой же, как и раньше (в конце концов, пользователь избранные тот же пункт еще раз).

Так... есть ли что-то в Android, что может это сделать, или какой-то обходной путь, чтобы заставить счетчик вести себя как раскрывающийся список? Если есть и вопрос, подобный этому на этом сайте, который я не нашел, и который имеет удовлетворительный ответ, Пожалуйста, дайте мне знать (в этом случае я искренне извиняюсь за повторение вопроса).

6 ответов


+1 к ответу Дэвида. Однако вот предложение по реализации, которое не включает в себя копирование-вставку кода из источника (который, кстати, выглядит точно так же, как Дэвид опубликовал в 2.3 так же):

@Override
void setSelectionInt(int position, boolean animate) {
    mOldSelectedPosition = INVALID_POSITION;
    super.setSelectionInt(position, animate);
}

таким образом, вы будете обманывать родительский метод, думая, что это новая позиция каждый раз.

кроме того, вы можете попробовать установить положение недействительным, когда счетчик нажат и установить его обратно в onNothingSelected. Этот это не так приятно, потому что пользователь не увидит, какой элемент выбран во время диалога.


хорошо, я думаю, что я придумал решение для своей собственной ситуации с помощью ответа Дэвида и Феликса (я считаю, что Дэвид помог Феликсу, который, в свою очередь, помог мне). Я думал, что опубликую его здесь вместе с образцом кода, если кто-то еще найдет этот подход полезным. Он также решает обе мои проблемы (как нежелательный автоматический выбор, так и желаемый триггер повторного выбора).

то, что я сделал, добавлено"выберите " фиктивный элемент как первый элемент в моем списке (сначала просто обойти проблему автоматического выбора, чтобы я мог игнорировать, когда он был выбран без взаимодействия с пользователем), а затем, когда другое элемент выбран, и я обработал выбор, я просто сброс счетчик к фиктивному элементу (который игнорируется). Подумайте об этом, я должен был подумать об этом давно, прежде чем решать опубликовать свой вопрос на этом сайте, но вещи всегда более очевидны задним числом... и я оказалось, что написание моего вопроса действительно помогло мне подумать о том, чего я хотел достичь.

очевидно, что если наличие фиктивного элемента не соответствует вашей ситуации, это может быть не идеальным решением для вас, но так как я хотел, чтобы вызвать действие, когда пользователь выбрал значение (и значение остается выбранным не требуется в моем конкретном случае), это работает просто отлично. Я попытаюсь добавить упрощенный пример кода (может не компилироваться как есть, я вырвал несколько бит из моего рабочий код и переименованные вещи перед вставкой, но, надеюсь, вы получите идею) ниже.

во-первых, активность списка (в моем случае), содержащая счетчик, назовем его MyListActivity:

public class MyListActivity extends ListActivity {

    private Spinner mySpinner;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // TODO: other code as required...

        mySpinner = (Spinner) findViewById(R.id.mySpinner);
        mySpinner.setAdapter(new MySpinnerAdapter(this));
        mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> aParentView,
                        View aView, int aPosition, long anId) {
                if (aPosition == 0) {
                    Log.d(getClass().getName(), "Ignoring selection of dummy list item...");
                } else {
                    Log.d(getClass().getName(), "Handling selection of actual list item...");
                    // TODO: insert code to handle selection
                    resetSelection();
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> anAdapterView) {
                // do nothing
            }
        });
    }

    /**
     * Reset the filter spinner selection to 0 - which is ignored in
     * onItemSelected() - so that a subsequent selection of another item is
     * triggered, regardless of whether it's the same item that was selected
     * previously.
     */
    protected void resetSelection() {
        Log.d(getClass().getName(), "Resetting selection to 0 (i.e. 'please select' item).");
        mySpinner.setSelection(0);
    }
}

и код адаптера spinner может выглядеть примерно так (на самом деле может быть внутренним классом в приведенном выше списке действий, если вы предпочитаете):

public class MySpinnerAdapter extends BaseAdapter implements SpinnerAdapter {

    private List<MyListItem> items; // replace MyListItem with your model object type
    private Context context;

    public MySpinnerAdapter(Context aContext) {
        context = aContext;
        items = new ArrayList<MyListItem>();
        items.add(null); // add first dummy item - selection of this will be ignored
        // TODO: add other items;
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public Object getItem(int aPosition) {
        return items.get(aPosition);
    }

    @Override
    public long getItemId(int aPosition) {
        return aPosition;
    }

    @Override
    public View getView(int aPosition, View aView, ViewGroup aParent) {
        TextView text = new TextView(context);
        if (aPosition == 0) {
            text.setText("-- Please select --"); // text for first dummy item
        } else {
            text.setText(items.get(aPosition).toString());
            // or use whatever model attribute you'd like displayed instead of toString()
        }
        return text;
    }
}

Я думаю (не пробовал это) тот же эффект может быть достигнут с помощью setSelected(false) вместо setSelection(0), но переустановка на "Пожалуйста, выберите"подходит для моих целей. И: "Смотри, Ма, нет флага!"(Хотя, я думаю, игнорируя 0 выбор не так уж отличается.)

надеюсь, это может помочь кому-то еще с аналогичным случаем использования. :- ) Для других случаев использования ответ Феликса может быть более подходящим (спасибо Феликс!).


посмотреть. Я не знаю, поможет ли это вам, но поскольку вы, кажется, устали искать ответ без особого успеха, эта идея может помочь вам, кто знает...

на Spinner класс является производным от AbsSpinner. Внутри этого есть такой метод:

void setSelectionInt(int position, boolean animate) {
        if (position != mOldSelectedPosition) {
            mBlockLayoutRequests = true;
            int delta  = position - mSelectedPosition;
            setNextSelectedPositionInt(position);
            layout(delta, animate);
            mBlockLayoutRequests = false;
        }
    }

это AFAIK взято из 1.5 источник. Возможно, вы могли бы проверить этот источник, посмотреть, как работает Spinner/AbsSpinner, и, возможно, расширить этот класс достаточно, чтобы поймать правильный метод и не проверить, если position != mOldSelectedPosition.

Я имею в виду... это огромное " может быть "с большим количеством" если " (Android versioning приходит на ум и т. д.), но поскольку вы кажетесь разочарованным (и я был там с Android много раз), возможно, это может дать вам некоторый "свет". И я предполагаю, что нет других очевидных ответов на предыдущие исследования.

желаю Вам удачи!


изменение Spinner полезно, если вы хотите иметь несколько вариантов одновременно в одном действии. Если вы хотите, чтобы у пользователя был только иерархический выбор, например:

Что вы хотите съесть?

фруктовое

  • яблоки
  • бананы
  • апельсины

быстро Еда!--3-->

  • котлеты
  • картошка
  • хот-доги,

тут ExpandableListView может быть лучше для вас. Он позволяет пользователю перемещаться по иерархии различных групп и выбирать дочерний элемент. Это было бы похоже на наличие нескольких блесен для пользователя на выбор - если вы не хотите одновременного выбора, то есть.


вот альтернативное решение для различения любых (намеренных или непреднамеренных) программных и пользовательских изменений:

создайте своего слушателя для spinner как OnTouchListener и OnItemSelectedListener

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) { 
            // Your selection handling code here
            userSelect = false;
        }
    }

}

добавить прослушиватель в счетчик регистрации для обоих типов событий

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

это не будет обрабатывать случай, когда повторный выбор того же элемента пользователем не вызывает метод onItemSelected (который Я не наблюдал), но я думаю, что это можно было бы обработать, добавив некоторый код в метод onTouch.

https://stackoverflow.com/a/25070696/4556980.

Я проработал несколько проблем, упомянутых в этой теме, прежде чем понял, что PopupMenu виджет-это то, что я действительно хотел. Это было легко реализовать без хаков и обходных путей, необходимых для изменения функциональности блесны. PopupMenu был относительно новым, когда этот поток был запущен в 2011, но я надеюсь, что это поможет кому-то искать подобную функциональность сейчас.