How do I set the maximum length for a spinner's drop-down list?
Asked Answered
H

5

22

I have a spinner which currently obscures some text below the spinner when opened. I need to limit the maximum drop-down length of the spinner, either through java code or through XML, so that it does not obscure this text.

The current design is the left example while the desired design is on the right. explanation

How do I go about limiting how far the spinner drops down to when opened? At present, it drops down to fill the entire portion of screen below it.

Hakenkreuz answered 29/1, 2014 at 8:45 Comment(2)
#2390918Cacodyl
That is a different situation keshav; I am asking about limiting the maximum height but that question deals with the maximum width of text elements within the spinner.Hakenkreuz
K
11

One way to achieve this is to use ActionBarSherlock's IcsSpinner. I made the needed modifications to limit the size of the spinner and that seems to work nicely.

Make the following modifications to IcsListPopupWindow.

Add an instance variable:

private int mDropDownVerticalOffsetBottom;

Add a method to set this variable:

public void setVerticalOffsetBottom(int offsetBottom) {
    mDropDownVerticalOffsetBottom = offsetBottom;
}

Alter the call to getMaxAvailableHeight to (mDropDownVerticalOffsetBottom was added):

final int maxHeight = /*mPopup.*/getMaxAvailableHeight(
        mDropDownAnchorView, mDropDownVerticalOffset, mDropDownVerticalOffsetBottom, ignoreBottomDecorations);

Change the method's signature to include that variable:

private int getMaxAvailableHeight(View anchor, int yOffset, int yOffsetBottom, boolean ignoreBottomDecorations) {

And consider that offset when computing the distance to the bottom:

final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset - yOffsetBottom;

Now modify IcsSpinner.java to implement the setter method for the offset:

private DropdownPopup mPopup;   // <- needs to be a DropdownPopup instead of a SpinnerPopup

public void setVerticalOffsetBottom(int offsetBottom) {
    mPopup.setVerticalOffsetBottom(offsetBottom);
}

Now "all" you need to do is to set the offset to the correct value. Here's how I did it (I tested it and it worked on two test devices):

final View root = findViewById(R.id.root_layout);
final View view = findViewById(R.id.view_not_to_be_obscured);
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    public void onGlobalLayout() {
        root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        int[] locations = new int[2];
        root.getLocationOnScreen(locations);
        int y1 = locations[1];
        int h1 = root.getHeight();
        view.getLocationOnScreen(locations);
        int y2 = locations[1];
        int offset = y1 + h1 - y2;
        // here we initialize the spinner
    }
});

The assumption is that root_layout fills the whole window excluding all decorating elements.

The final step is to create the spinner itself:

    // actionDropDownStyle is an attribute set in the theme to e.g. @style/Widget.Sherlock.Spinner.DropDown.ActionBar or @style/Widget.Sherlock.Light.Spinner.DropDown.ActionBar for light theme
    IcsSpinner spinner = new IcsSpinner(context, null, R.attr.actionDropDownStyle);

    // yes here we set the offset!
    spinner.setVerticalOffsetBottom(offset);

    spinner.setPadding(spinner.getPaddingLeft(), 0, spinner.getPaddingRight(), 0);
    spinner.setId(R.id.some_id);
    spinner.setAdapter(yourAdapter); // you can use a simple ArrayAdapter or whatever you need

    // create ICS LinearLayout
    LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
    IcsLinearLayout linearLayout = (IcsLinearLayout) inflater.inflate(R.layout.abs__action_bar_tab_bar_view, null);
    linearLayout .setPadding(listNavLayout.getPaddingLeft(), 0, listNavLayout.getPaddingRight(), 0);
    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
    params.gravity = Gravity.CENTER;
    linearLayout .addView(spinner, params);

Now that might look complicated but as someone else mentioned, you'll not be able to achieve this without your own spinner implementation and since ActionBarSherlock comes with one, why not use it? It's certainly less work than writing your own one. If you don't use the library for the ActionBar strip away all resource files you don't need and use Proguard to strip away all unused classes. You could probably achieve the same using AppCompat (see here: https://github.com/android/platform_frameworks_support/tree/master/v7/appcompat/src/android/support).

Keitel answered 12/6, 2014 at 3:0 Comment(0)
G
3

I've searched quite a bit and it seems that I can't find a solution to this anywhere. You may have to go a slightly different route by recreating the functionality of a spinner, but using buttons and dialogs intead.

Assuming you're using an ArrayAdapter for the spinner, try this:

Replace your Spinner with a button that has a down arrow icon.

Create a custom Dialog or AlertDialog and populate a ListView with the ArrayAdapter. For the onClick method, push the value of the selected option and populate a static variable and the text of the button with the chosen value. This way, you can make the size of the dialog box any size you want and the excess choices will always be scrollable.

I know it's not ideal, but I haven't found any way to change the height of the "dropdown" portion of a spinner.

Sorry if this wasn't what you were hoping for.

EDIT: Someone else asked the exact same question here before and it turns out they went with the exact method I just described. You can see the sample code they used here: Check out the question/code

Gupta answered 12/6, 2014 at 1:55 Comment(0)
S
1

in Kotlin, This what i've done to set dropdwon spinner height. Replace "spinner_empresas" with your spinner id.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    view.setOnClickListener(this)
    binding?.spinnerEmpresas?.adapter = ArrayAdapter(this, 
    android.R.layout.simple_spinner_item, arrayOf("items"))
        
        val popup: Field =            
        AppCompatSpinner::class.java.getDeclaredField("mPopup") 
        popup.isAccessible = true
        val popupWindow = popup[spinner_empresas] as 
        androidx.appcompat.widget.ListPopupWindow
        popupWindow.height = 700
    }
}
Sayers answered 27/8, 2022 at 11:26 Comment(0)
M
0

you can use relfection.

 Spinner spinner = (Spinner) findViewById(R.id.spinner);
try {
    Field popup = Spinner.class.getDeclaredField("mPopup");
    popup.setAccessible(true);

    // Get private mPopup member variable and try cast to ListPopupWindow
    android.widget.ListPopupWindow popupWindow = (android.widget.ListPopupWindow) popup.get(spinner);

    // Set popupWindow height to 500px
    popupWindow.setHeight(500);
}
catch (NoClassDefFoundError | ClassCastException | NoSuchFieldException | IllegalAccessException e) {
    // silently fail...
} 

But if you are using androidx.appCompat 1.1.0 then you have to use ->

I can't use API reflection on androidx.appcompat:appcompat:1.1.0

Regards

Married answered 12/5, 2020 at 20:42 Comment(0)
C
-3

I think you have to use custom spinner for your requirement .There is not any inbuilt method or xml property that can help.

See this example for help .

https://github.com/ankitthakkar/DropDownSpinner

Hope this will work.

Note: This is changed answer.

Cacodyl answered 29/1, 2014 at 9:24 Comment(4)
#9923769Cacodyl
That successfully changes the width of it but not how far it drops down.Hakenkreuz
#20239013Cacodyl
@Hakenkreuz Please see the changes to answer .Cacodyl

© 2022 - 2024 — McMap. All rights reserved.