Как использовать Android Spinner как выпадающий список
Мне потребовалось довольно много времени, чтобы обойти Android Spinner. После нескольких неудачных попыток реализации и после прочтения многих вопросов, частично похожих на мои собственные, но без удовлетворительных ответов, а некоторые вообще без ответов, например здесь и здесь, Я, наконец, понимаю, что" spinner "в Android не означает то же самое, что" выпадающий список " из настольных приложений или выбор в HTML. Однако, что мое приложение (и я угадывание приложений всех других плакатов, чьи вопросы похожи) - это то, что работает как раскрывающийся список, а не как счетчик.
мои две проблемы с тем, что я сначала считал идиосинкразиями OnItemSelectedListener (я видел их как отдельные вопросы на этом сайте, но не как один):
- первоначальный выбор первого элемента списка, запускается автоматически, без вмешательства пользователя.
- когда элемент, который был уже выбран, снова выбран пользователем, он игнорируется.
теперь я понимаю, что когда вы думаете об этом, это имеет смысл, чтобы это произошло на счетчик - он должен начинаться с выбранного значения по умолчанию, и вы вращаете его только для изменения этого значения, а не для" повторного выбора " значения -документация фактически говорит:"этот обратный вызов вызывается только тогда, когда вновь выбранная позиция отличается от ранее выбранной позиции". И Я видел ответы, предполагающие, что вы установили флаг игнорировать первый автоматический выбор - я думаю, я мог бы жить с этим, если нет другого способа.
но поскольку то, что я действительно хочу, - это раскрывающийся список, который ведет себя как раскрывающийся список (и как пользователи могут и должны ожидать), мне нужно что-то как счетчик, который ведет себя как раскрывающийся список, как комбо-бокс. Меня не волнует автоматический предварительный выбор (это должно произойти без запуск моего слушателя), и я хочу знать о выбор, даже если он такой же, как и раньше (в конце концов, пользователь избранные тот же пункт еще раз).
Так... есть ли что-то в 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, но я надеюсь, что это поможет кому-то искать подобную функциональность сейчас.