I took part in the 2021 popular creators, for me, please vote for the precious vote address rank. Juejin. Cn/rank / 2021/3… Thank you very much!

preface

Spinner is a drop-down selection component. It is very convenient to use the system’s own spinner. First, define an array (strings.xml) as follows:

<array name="grade">
    <item>In grade one</item>
    <item>Second grade</item>
    <item>The third grade</item>
    <item>In fourth grade</item>
    <item>The fifth grade</item>
    <item>The sixth grade</item>
</array>
Copy the code

The code is as follows:

Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.grade, android.R.layout.simple_spinner_item);
spinner.setAdapter(adapter);
Copy the code

This results in a simple spinner that looks like this:

But this is not the style and effect I want, let’s change it a little bit.

(1) Change the initial layout

That is, the popover style, first customize a layout, as follows:

spinner_layout.xml


      
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:textColor="#6d6d6d"
    android:textSize="15sp"
    android:drawableRight="@drawable/arrow"
    android:drawablePadding="5dp"
    tools:text="First grade">

</TextView>
Copy the code

Then replace createFromResource as follows:

ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.grade, R.layout.spinner_layout);
Copy the code

This is not enough, as there is also a background with arrows, just set the background to transparent like this:

spinner.setBackgroundColor(0x0);
Copy the code

This renders the initial layout as spinner_layout.

(2) Change the layout of list item

After the above modification, the layout of the list item in the popover is also changed to spinner_layout. Check the constructor of ArrayAdapter, we can see that there are two variables: mResource and mDropDownResource, where mResource is the initial layout. And mDropDownResource is the layout of the list item.

In createFromResource, mResource and mDropDownResource are assigned the same value. But ArrayAdapter also has a setDropDownViewResource function.

Start by defining a layout as follows:

spinner_item.xml


      
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:textColor="#6d6d6d"
    android:textSize="15sp"
    android:padding="8dp"
    android:gravity="center_horizontal"
    tools:text="First grade">

</TextView>
Copy the code

Then use the setDropDownViewResource function as follows:

adapter.setDropDownViewResource(R.layout.spinner_item);
Copy the code

(3) Change the popover background and position

In the first animation you can see that the popover is covered, and we want it to be underneath, and it’s rounded with arrows.

This will need to use the two functions of spinner setPopupBackgroundResource and setDropDownVerticalOffset.

However, note that these two functions need to be on android4.1 or above. Since there are few versions below 4.1 at present, we only consider above 4.1. The code is as follows:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    spinner.setPopupBackgroundResource(R.drawable.bg_spinner);
    spinner.setDropDownVerticalOffset(dip2px(20));
}
Copy the code

(4) Add the selected effect

After the above processing, we have the desired style. But it’s not nearly as good. The popover list is missing the selected styles. For example, if I currently select “grade 2”, in the popover, the corresponding item font should be darker and bolder. We searched spinner’s source code and found no corresponding function or solution, so we’ll do it ourselves.

The spinner uses a adapter to load the list, and the createFromResource function creates a new adapter automatically. We can create a custom adapter as follows:

public class SpinnerAdapter<T> extends ArrayAdapter<T> {

    private int selectedPostion;

    public void setSelectedPostion(int selectedPostion) {
        this.selectedPostion = selectedPostion;
    }

    public SpinnerAdapter(@NonNull Context context, int resource, @NonNull T[] objects) {
        super(context, resource, objects);
    }

    @Override
    public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        View view = super.getDropDownView(position, convertView, parent);
        TextView textView = (TextView)view;
        if(selectedPostion == position){
            textView.setTextColor(0xff373741);
            textView.getPaint().setFakeBoldText(true);
        }
        else{
            textView.setTextColor(0xff6d6d6d);
            textView.getPaint().setFakeBoldText(false);
        }
        return view;
    }

    public static @NonNull SpinnerAdapter<CharSequence> createFromResource(@NonNull Context context,
                                                                      @ArrayRes int textArrayResId, @LayoutRes int textViewResId) {
        final CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
        return newSpinnerAdapter<>(context, textViewResId, strings); }}Copy the code

Note that the getDropDownView function in the ArrayAdapter gets the view used by the popover item, not the getView function.

In the meantime we’re going to rewrite a createFromResource function.

Replace the adapter used previously with the custom one and set the listener for the spinner. The code is as follows:

Spinner spinner = (Spinner) findViewById(R.id.spinner);
adapter = SpinnerAdapter.createFromResource(this, R.array.grade, R.layout.spinner_layout);
adapter.setDropDownViewResource(R.layout.spinner_item);
spinner.setBackgroundColor(0x0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    spinner.setPopupBackgroundResource(R.drawable.bg_spinner);
    spinner.setDropDownVerticalOffset(dip2px(20));
}
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<? > parent, View view,int position, long id) {
        adapter.setSelectedPostion(position);
    }
 
    @Override
    public void onNothingSelected(AdapterView
        parent) {}});Copy the code

The final effect is as follows:

We can implement more complex effects and styles by defining the Adapter ourselves, but we will discuss this later.

Pay attention to the public number: BennuCTech, get more dry goods!