Android Spinner с разными макетами для раскрывающегося состояния и закрытого состояния?

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

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

Любой, кто может мне помочь?


person dbm    schedule 10.01.2011    source источник
comment
См. мой ответ в stackoverflow .com/questions/4880413/   -  person Bhavinkumar Patel    schedule 27.02.2016
comment
stackoverflow.com/questions/ 4880413/   -  person Bhavinkumar Patel    schedule 27.02.2016


Ответы (6)


Вам необходимо создать собственный класс Adapter для Spinner и перезаписать два методы getView() для обычного закрытого вида и getDropDownView() для представления раскрывающегося списка. Оба метода должны возвращать объект View для одного элемента.

Ознакомьтесь с этим руководством. может помочь вам начать работу.

person Flo    schedule 10.01.2011
comment
Долго не мог найти, но это именно то, что я искал! - person Rabbott; 27.09.2012
comment
Отличный совет, это то, что я искал! О, мой добрый :О Большое спасибо - person TiagoM; 29.03.2013

У меня тоже была проблема. Вместо того, чтобы переопределять класс, у меня есть более простой способ сделать это.

Но сначала нужно понять разницу между идентификатором ресурса в конструкторе адаптера и другим в setDropDownViewResource(...). Например,

SimpleAdapter adapter =
    new SimpleAdapter(ab.getThemedContext(), data, R.layout.actionbar_dropdown, new String[] { "EventID", "Icon" },
        new int[] { R.id.event_id, R.id.icon });

adapter.setDropDownViewResource(R.layout.actionbar_list_item);

R.layout.actionbar_dropdown — это стиль для spinner, а R.layout.actionbar_list_item — для каждого отдельного элемента списка.

Здесь я использовал SimpleAdapter, поскольку, если я использую ArrayAdapter, xml может быть только одним TextView.

R.layout.actionbar_list_item содержит TextView с идентификатором event_id и ImageView с идентификатором icon.

R.layout.actionbar_dropdown почти точно такой же, как actionbar_list_item, но для видимости ImageView последнего установлено значение GONE.

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

person AstraChen    schedule 30.06.2013
comment
Это действительно должен быть правильный ответ, поскольку его гораздо проще реализовать, чем ответ Фло (это решение тоже работает и может понадобиться в определенных случаях, просто не обязательно для вопроса ОП). - person Kevin; 27.07.2016
comment
Отлично - рекомендую! - person RMcGuigan; 28.04.2017

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

public class CustomSpinnerAdapter extends ArrayAdapter<String> {

Context mContext;
int mTextViewResourceId;
String[] mObjects;
String[] mShortNameObjects;

public CustomSpinnerAdapter(Context context, int textViewResourceId,
                            String[] objects, String[] shortNameObjects) {
    super(context, textViewResourceId, objects);
    mContext = context;
    mTextViewResourceId = textViewResourceId;
    mObjects = objects;
    mShortNameObjects = shortNameObjects;
}

@Override
public View getDropDownView(int position, View convertView,
                            ViewGroup parent) {
    LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    TextView row = (TextView) inflater.inflate(mTextViewResourceId, parent, false);
    row.setText(mObjects[position]);

    return row;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    return getCustomView(position, convertView, parent);
}

public View getCustomView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    TextView row = (TextView) inflater.inflate(mTextViewResourceId, parent, false);
    row.setText(mShortNameObjects[position]);

    return row;
}
}

И использование внутри фрагмента:

CustomSpinnerAdapter mSpinnerAdapter = new CustomSpinnerAdapter(getActivity(), R.layout.spinner_item, getResources().getStringArray(R.array.action_filter), getResources().getStringArray(R.array.action_filter_short_names));

Наконец, макет элемента счетчика:

spinner_item.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="18dip"
    android:gravity="left"
    android:textColor="@color/navdraw_list_item_background_default"
    android:padding="5dip" />
person AlvaroSantisteban    schedule 18.06.2014
comment
Вы должны использовать параметр convertView в своих методах getView() и getDropDownView(). - person Gus; 22.12.2015

Только устанавливает ресурс раскрывающегося списка с вашим альтернативным макетом:

ArrayAdapter<String> genderAdapter = new ArrayAdapter<>(this, R.layout.adapter_spinner_white, Constants.GENDER);
genderAdapter.setDropDownViewResource(R.layout.adapter_spinner_white_dropdown);
view.setAdapter(genderAdapter);

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

person Joao Polo    schedule 27.02.2016
comment
Работает как шарм, ура! Другие решения слишком сложны. - person TaylanKammer; 07.01.2019

Просто вызовите метод setUpSpinner() после получения ссылки на счетчик

// here is setUpSpinner method

private void setupSpinner() {

    // Create adapter for spinner. The list options are from the String array it will use
    // the spinner will use the default layout
    ArrayAdapter spinnerAdapter = ArrayAdapter.createFromResource(this,
            R.array.array_dropdown_options, android.R.layout.simple_spinner_item);

    // Specify dropdown layout style - simple list view with 1 item per line
    spinnerAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);

    // Apply the adapter to the spinner
    spinner.setAdapter(spinnerAdapter);
   // spinner is referenced spinner by finViewById.
    
    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            String selection = (String) parent.getItemAtPosition(position);
            if (!TextUtils.isEmpty(selection)) {
                if (selection.equals(getString(R.string.item_a))) {
                    // your code for selected item whose id equals to id to R.string.item_a
                } else if (selection.equals(getString(R.string.item_b))) {
                    // your code
                } else {
                    // your code
                }
            }
        }

        // Because AdapterView is an abstract class, onNothingSelected must be defined
        @Override
        public void onNothingSelected(AdapterView<?> parent) {
            // code for nothing selected in dropdown
        }
    });
}
person Chetan Pawar    schedule 14.07.2018

Я сделал расширение ArrayAdapter, которое максимально сохраняет его функциональность, просто устанавливает длинное описание для выбранного элемента.

Во-первых, нам нужна новая модель для питания адаптера. Обратите внимание, что toString() возвращает shortDescription, так что ArrayAdapter показывает краткое описание в раскрывающемся списке:

data class DescriptiveArrayItem(val shortDescription: String, val longDescription: String) {
    override fun toString(): String {
        return shortDescription
    }
}

Во-вторых, мы создаем наш собственный адаптер, который переопределяет getView() и показывает длинное описание при выборе элемента (к сожалению, многие поля и методы в ArrayAdapter являются приватными, поэтому мне пришлось их скопировать):

class DescriptiveArrayAdapter : ArrayAdapter<DescriptiveArrayItem> {
    private var mResource: Int
    private val mFieldId: Int
    private val mContext: Context
    private val mInflater: LayoutInflater

    constructor(context: Context, resource: Int) : super(context, resource) {
        mContext = context
        mFieldId = 0
        mInflater = LayoutInflater.from(context)
        mResource = resource
    }
    constructor(context: Context, resource: Int, textViewResourceId: Int) : super(context, resource, textViewResourceId) {
        mContext = context
        mFieldId = textViewResourceId
        mInflater = LayoutInflater.from(context)
        mResource = resource
    }
    constructor(context: Context, resource: Int, objects: Array<out DescriptiveArrayItem>) : super(context, resource, objects) {
        mContext = context
        mFieldId = 0
        mInflater = LayoutInflater.from(context)
        mResource = resource
    }
    constructor(context: Context, resource: Int, textViewResourceId: Int, objects: Array<out DescriptiveArrayItem>) : super(context, resource, textViewResourceId, objects) {
        mContext = context
        mFieldId = textViewResourceId
        mInflater = LayoutInflater.from(context)
        mResource = resource
    }
    constructor(context: Context, resource: Int, objects: List<DescriptiveArrayItem>) : super(context, resource, objects) {
        mContext = context
        mFieldId = 0
        mInflater = LayoutInflater.from(context)
        mResource = resource
    }
    constructor(context: Context, resource: Int, textViewResourceId: Int, objects: List<DescriptiveArrayItem>) : super(context, resource, textViewResourceId, objects) {
        mContext = context
        mFieldId = textViewResourceId
        mInflater = LayoutInflater.from(context)
        mResource = resource
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        return createViewFromResource(mInflater, position, convertView, parent, mResource)
    }

    private fun createViewFromResource(inflater: LayoutInflater, position: Int,
                                       convertView: View?, parent: ViewGroup, resource: Int): View {
        val text: TextView?
        val view: View = convertView ?: inflater.inflate(resource, parent, false)
        try {
            if (mFieldId == 0) {
                //  If no custom field is assigned, assume the whole resource is a TextView
                text = view as TextView
            } else {
                //  Otherwise, find the TextView field within the layout
                text = view.findViewById(mFieldId)
                if (text == null) {
                    throw RuntimeException("Failed to find view with ID "
                            + mContext.resources.getResourceName(mFieldId)
                            + " in item layout")
                }
            }
        } catch (e: ClassCastException) {
            Log.e("ArrayAdapter", "You must supply a resource ID for a TextView")
            throw IllegalStateException(
                    "ArrayAdapter requires the resource ID to be a TextView", e)
        }
        val item: DescriptiveArrayItem? = getItem(position)
        text.text = item?.longDescription
        return view
    }
}
person Miloš Černilovský    schedule 24.06.2020