2

I have a form with TextInputLayout+TextInputEditText and spinners. I want the spinner height wrap text but have large items in the dropdown. The problem is that the spinner's height depends on the dropdown items height (simple_spinner_dropdown_item). I set style="@style/Base.Widget.AppCompat.Spinner.Underlined" in order to add line below spinner.

Any solution?

Yamila
  • 443
  • 1
  • 9
  • 20

5 Answers5

10

Spinner like edittext

If you want spinner like edittext, it's like AutoCompleteTextView .You can custom your AppCompatAutoCompleteTextView like that:

public class AutoCompleteDropDown extends AppCompatAutoCompleteTextView {
    //    implements AdapterView.OnItemClickListener
    private static final int MAX_CLICK_DURATION = 200;
    private long startClickTime;
    private boolean isPopup;
    private int mPosition = ListView.INVALID_POSITION;

    public AutoCompleteDropDown(Context context) {
        super(context);
//        setOnItemClickListener(this);
    }

    public AutoCompleteDropDown(Context arg0, AttributeSet arg1) {
        super(arg0, arg1);
//        setOnItemClickListener(this);
    }

    public AutoCompleteDropDown(Context arg0, AttributeSet arg1, int arg2) {
        super(arg0, arg1, arg2);
//        setOnItemClickListener(this);
    }

    @Override
    public boolean enoughToFilter() {
        return true;
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction,
                                  Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        if (focused) {
            performFiltering("", 0);
            InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getWindowToken(), 0);
            setKeyListener(null);
            dismissDropDown();
        } else {
            isPopup = false;
        }
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_UP: {
                if (isPopup) {
                    dismissDropDown();
                } else {
                    requestFocus();
                    showDropDown();
                }
                break;
            }
        }

        return super.onTouchEvent(event);
    }

    @Override
    public void showDropDown() {
        super.showDropDown();
        isPopup = true;
    }

    @Override
    public void dismissDropDown() {
        super.dismissDropDown();
        isPopup = false;
    }



    @Override
    public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom) {
        Drawable dropdownIcon = ContextCompat.getDrawable(getContext(), R.drawable.ic_expand_more_black_18dp);
        if (dropdownIcon != null) {
            right = dropdownIcon;
            right.mutate().setAlpha(66);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            super.setCompoundDrawablesRelativeWithIntrinsicBounds(left, top, right, bottom);
        } else {
            super.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
        }

    }



    public int getPosition() {
        return mPosition;
    }
}

And ic_expand_more_black_18dp.png is a image like that:

Layout:

<android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColorHint="@color/gray_text_hint"
        app:hintTextAppearance="@style/TextAppearance.App.TextInputLayout.Dark">

        <yourpackage.AutoCompleteDropDown
            android:id="@+id/edtBloodType"
            style="@style/edt_dark"
            android:hint="Blood Type"
            android:inputType="textNoSuggestions" />
    </android.support.design.widget.TextInputLayout>

Result (You can set adapter for your AutoCompleteTextView)

enter image description here

RoShan Shan
  • 2,924
  • 1
  • 18
  • 38
  • Too complicated :S – Yamila May 15 '17 at 12:42
  • I searched a lot to do spinner as edittext. If you have another simple way. Please share it. – RoShan Shan May 16 '17 at 03:53
  • Worked like a charm except for the line below! I was getting a NullPointer on this line - `performFiltering("", 0);` When the view is blank, it throws a null pointer because there is no text to use for filtering. So I surrounded it with a check on the text size. `if(this.getText().length()>0) { performFiltering("", 0); }` – Surekha Oct 25 '18 at 16:01
  • 1
    Noticed a Samsung device crash on this line. Fix is here: if(this.getText().length()>0 && this.getAdapter() !=null) { performFiltering("", 0); } – Surekha Dec 06 '18 at 19:17
1

Here is an workaround:

Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<>(context, R.layout.custom_spinner_item, values);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);

Here layout android.R.layout.simple_spinner_dropdown_item is used for dropdown item and custom layout custom_spinner_item is used for spinner view to show only TextView.

custom_spinner_item.xml:

<TextView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@android:id/text1"
    android:maxLines="1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee" />

FYI, You can customize this TextView as per your needs.

Hope this will help~

Ferdous Ahamed
  • 21,438
  • 5
  • 52
  • 61
1

I'm using Support Libraries (AppCompat) and layouts like below. More info here.

<!-- editText -->
<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:hint="Hint">

    <android.support.design.widget.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:maxLines="1"
        android:singleLine="true" />

</android.support.design.widget.TextInputLayout>

<!-- spinner with label -->
<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        style="@style/TextAppearance.AppCompat.Caption"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Label"></TextView>

    <android.support.v7.widget.AppCompatSpinner
        style="@style/Widget.AppCompat.Spinner.Underlined"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:spinnerMode="dialog" />
</LinearLayout>

Update

spinner item layout:

<?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:paddingStart="0dp"
    android:paddingLeft="0dp"
    android:textSize="18sp"
    android:singleLine="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:textAlignment="inherit"/>

for dropdown view: android.R.layout.simple_spinner_dropdown_item

marioosh
  • 27,328
  • 49
  • 143
  • 192
1

I wanted to take a moment and expand on RoShan Shan's answer, having just spent nearly 2 hours getting his code actually working.

What to do

You will need to create a new class which extends AppCompatAutoCompleteTextView:

public class ComboBox extends AppCompatAutoCompleteTextView implements AdapterView.OnItemClickListener {

    private boolean isPopup;
    private int mPosition = -1;

    public ComboBox(Context context){
        super(context);
        setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_expandable_list_item_1, new String[0]));
        setOnItemClickListener(this);
        setKeyListener(null);
    }

    public ComboBox(Context context, AttributeSet attributes){
        super(context, attributes);
        setAdapter(new ComboBoxAdapter(context, attributes.getAttributeListValue("http://schemas.android.com/apk/res/android", "entries", new String[0], R.array.default_empty_list)));
        setOnItemClickListener(this);
        setKeyListener(null);
    }

    public ComboBox(Context context, AttributeSet attributes, int arg2){
        super(context, attributes, arg2);
        setAdapter(new ComboBoxAdapter(context, attributes.getAttributeListValue("http://schemas.android.com/apk/res/android", "entries", new String[0], R.array.default_empty_list)));
        setOnItemClickListener(this);
        setKeyListener(null);
    }

    public static class ComboBoxAdapter extends ArrayAdapter<String> {

        private final String[] list;

        public ComboBoxAdapter(Context context, @ArrayRes int array){
            super(context, android.R.layout.simple_expandable_list_item_1, context.getResources().getStringArray(array));
            list = context.getResources().getStringArray(array);
        }

        @Override
        public Filter getFilter(){
            return new Filter(){
                @Override
                protected FilterResults performFiltering(CharSequence constraint){
                    FilterResults out = new FilterResults();
                    out.values = list;
                    out.count = list.length;
                    return out;
                }

                @Override
                protected void publishResults(CharSequence constraint, FilterResults results){
                    notifyDataSetChanged();
                }
            };
        }

    }

    @Override
    public boolean enoughToFilter(){
        return true;
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect){
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        if (focused){
            InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getWindowToken(), 0);
            showDropDown();
        }
    }

    @Override
    public boolean performClick(){
        if (isPopup){
            dismissDropDown();
        }
        else {
            showDropDown();
        }
        return super.performClick();
    }

    @Override
    public void showDropDown(){
        super.showDropDown();
        isPopup = true;
    }

    @Override
    public void dismissDropDown(){
        super.dismissDropDown();
        isPopup = false;
    }

    @Override
    public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom){
        Drawable dropdownIcon = ContextCompat.getDrawable(getContext(), R.drawable.ic_chevron_down);
        if (dropdownIcon != null){
            right = dropdownIcon;
            right.mutate().setAlpha(66);
        }
        super.setCompoundDrawablesRelativeWithIntrinsicBounds(left, top, right, bottom);
    }

    public int getPosition(){
        return mPosition;
    }

    public String getCurrentText(){
        if (mPosition == -1){
            return "";
        }
        else {
            return getText().toString();
        }
    }

    public void registerDataSetObserver(DataSetObserver observer){
        getAdapter().registerDataSetObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer){
        getAdapter().unregisterDataSetObserver(observer);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id){
        setText(parent.getItemAtPosition(position).toString());
        mPosition = position;
    }

}

You will need to define an empty list to use as default:

<string-array name="default_empty_list"/>

And add an icon for the drop down. I recommend importing the ic_expand_more vector asset from clip art (File > New > Vector Assest > Clip Art).

Why this is better

This ComboBox buys you a few things over the original answer:

  1. Works out of the box. Create the three items above and everything will just work.

  2. You can include the ComboBox in any View or layout from the xml (see original answer, it hasn't changed) but now you can also define the list there by supplying android:entries with an array resource.

  3. Use registerDataSetObserver() to add a change listener.

  4. Retrieve the current value of the ComboBox using getPosition() (index) or getCurrentText() (string).

Good luck

Community
  • 1
  • 1
Zac
  • 88
  • 1
  • 6
-1

ANSWER: I was just setting adapter layout (tried both layouts) but there is specific dropdown layout.

ArrayAdapter<String> typeAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.spinner_item, types);
typeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

spinner_item is a custom layout that removes left padding:

<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:paddingEnd="0dp"
android:paddingLeft="0dp"
android:paddingStart="0dp"
android:singleLine="true"
android:textAlignment="inherit" />

Also, add this style to spinner:

style="@style/Widget.AppCompat.Spinner.Underlined"

And

Yamila
  • 443
  • 1
  • 9
  • 20