Android 自定义Spinner的基本使用

1、新建封装数据的类,UserModel.java

class UserModel {
    String username;
    UserModel(String username){
        this.username = username;
    }
}

2、Activity的布局文件,activity_spinner.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/spinner_layout"
    tools:context=".SpinnerActivity">

    <ImageView
        android:id="@+id/spinner_actionBar_bg"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:background="@color/colorModel"/>

    <ImageView
        android:id="@+id/spinner_back_bt"
        android:layout_width="25dp"
        android:layout_height="25dp"
        app:layout_constraintBottom_toBottomOf="@+id/spinner_actionBar_bg"
        app:layout_constraintLeft_toLeftOf="@+id/spinner_actionBar_bg"
        android:layout_marginLeft="5dp"
        android:layout_marginBottom="7dp"
        android:padding="3dp"
        android:src="@mipmap/back"
        android:onClick="spinner_backClick"/>

    <TextView
        android:id="@+id/spinner_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="@+id/spinner_actionBar_bg"
        app:layout_constraintBottom_toBottomOf="@+id/spinner_actionBar_bg"
        app:layout_constraintRight_toRightOf="@+id/spinner_actionBar_bg"
        android:layout_marginBottom="5dp"
        android:text="@string/SpinnerActivity_title"
        android:textColor="@color/colorWhite"
        android:textSize="12pt"/>

</androidx.constraintlayout.widget.ConstraintLayout>

3、新建spinner_item.xml来自定义下拉框列表项的样式布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/spinnerAdapter_title"
        android:layout_width="wrap_content"
        android:layout_height="35dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginLeft="10dp"
        android:gravity="center_vertical"
        android:textColor="@color/colorWhite"/>

</androidx.constraintlayout.widget.ConstraintLayout>

4、新建login_spinner_bg.xml来自定义spinner的背景边框和下三角箭头样式

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <layer-list>
            <item>
                <shape>
                    <stroke
                        android:width="1dp"
                        android:color="#FFFFFF" />
                </shape>
            </item>
            <item>
                <bitmap android:gravity="right" android:src="@drawable/down"/>
            </item>
        </layer-list>
    </item>
</selector>

5、新建CustomSpinnerAdapter.java来绑定数据

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.List;

public class CustomSpinnerAdapter extends BaseAdapter {
    private List<UserModel> list;
    private LayoutInflater layoutInflater;
    CustomSpinnerAdapter(Context context, List<UserModel> list){
        this.layoutInflater = LayoutInflater.from(context.getApplicationContext());
        this.list = list;
    }


    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int i) {
        return list.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder holder;
        if (view==null){
            view = layoutInflater.inflate(R.layout.spinner_item,viewGroup,false);
            holder = new ViewHolder(view);
            view.setTag(holder);
        }else {
            holder = (ViewHolder) view.getTag();
        }
        UserModel model = list.get(i);
        holder.usernameTextView.setText(model.username);
        return view;
    }

    static class ViewHolder{
        TextView usernameTextView;
        ViewHolder(View view){
            usernameTextView = view.findViewById(R.id.spinnerAdapter_title);
        }
    }

}

6、新建自定义的Spinner,CustomSpinner.java

package com.example.demo;

import android.content.Context;
import android.util.AttributeSet;

/**
 * 自定义的Spinner可以重复点击统一item触发点击事件
 */
class CustomSpinner extends androidx.appcompat.widget.AppCompatSpinner {

//    public boolean isDropDownMenuShown=false;//标志下拉列表是否正在显示

    public CustomSpinner(Context context) {
        super(context);
    }

    public CustomSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomSpinner(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void
    setSelection(int position, boolean animate) {
        boolean sameSelected = position == getSelectedItemPosition();
        super.setSelection(position, animate);
        if (sameSelected) {
            if (getOnItemSelectedListener()!=null){
                // 如果选择项是Spinner当前已选择的项,则 OnItemSelectedListener并不会触发,因此这里手动触发回调
                getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
            }
        }
    }

//    @Override
//    public boolean performClick() {
//        this.isDropDownMenuShown = true;
//        return super.performClick();
//    }
//
//    public boolean isDropDownMenuShown(){
//        return isDropDownMenuShown;
//    }
//
//    public void setDropDownMenuShown(boolean isDropDownMenuShown){
//        this.isDropDownMenuShown=isDropDownMenuShown;
//    }

    @Override
    public void
    setSelection(int position) {
        boolean sameSelected = position == getSelectedItemPosition();
        super.setSelection(position);
        if (sameSelected) {
            if (getOnItemSelectedListener()!=null){
                getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
            }
        }
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
    }
}

7、在res/values下新建一个ids.xml文件,用于在Activity中使用代码创建的Spinner绑定id

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="customSpinner" type="id"/>
</resources>

8、最后在SpinnerActivity.java中加载自定义的Spinner

import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Toast;

import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;

import java.util.ArrayList;
import java.util.List;

public class SpinnerActivity extends FatherActivity {

    CustomSpinner spinner;//自定义的spinner
    List<UserModel> list;//装载数据源的数组
    CustomSpinnerAdapter adapter;//自定义spinner的数据适配器
    String[] strArray = {"MPH11","MPH22","MPH33","MPH44","MPH55","MPH66"};//数据源
    boolean isInit;//spinner初始化自动触发点击事件的标志

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spinner);
        isInit = true;
        initView();
    }
    /**
     * 初始化视图
     */
    private void initView(){
        //初始化数据源
        list = new ArrayList<>();
        for (String s : strArray) {
            UserModel model = new UserModel(s);
            list.add(model);
        }
        adapter = new CustomSpinnerAdapter(this,list);

        //代码方式布局添加控件
        ConstraintLayout spinnerLayout = findViewById(R.id.spinner_layout);//获取布局
        ConstraintSet set = new ConstraintSet();//新建约束
        set.clone(spinnerLayout);//复制布局的约束
        //新建Spinner控件
        spinner = new CustomSpinner(this);
        spinner.setDropDownVerticalOffset(dpToPx(35));
        spinner.setBackgroundResource(R.drawable.login_spinner_bg);
        spinner.setAdapter(adapter);
        spinner.setId(R.id.customSpinner);//代码创建控件不会自动生成资源id,在res/values目录中新建一个资源文件ids.xml添加控件的id,最后进行手动绑定
        //往布局里添加控件
        spinnerLayout.addView(spinner);
        //设置该控件的约束
        set.constrainWidth(spinner.getId(),dpToPx(0));
        set.constrainHeight(spinner.getId(),dpToPx(35));
        set.connect(spinner.getId(),ConstraintSet.TOP,R.id.spinner_actionBar_bg,ConstraintSet.BOTTOM,dpToPx(20));
        set.connect(spinner.getId(),ConstraintSet.LEFT,ConstraintSet.PARENT_ID,ConstraintSet.LEFT,dpToPx(35));
        set.connect(spinner.getId(),ConstraintSet.RIGHT,ConstraintSet.PARENT_ID,ConstraintSet.RIGHT,dpToPx(35));
        //把新的约束添加到布局里
        set.applyTo(spinnerLayout);

        //设置监听spinner的点击事件
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                if (isInit){//这里做个判断,作用是忽略spinner初始化时自动触发点击第一个item
                    isInit = false;
                    return;
                }
                Toast.makeText(SpinnerActivity.this,"Click"+strArray[i],Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });

    }

    /**
     *返回按钮点击事件
     */
    public void spinner_backClick(View view){
        finish();
    }
    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public int dpToPx(int dp) {
        final float scale = this.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }


}

上面的spinner使用代码的方式创建并添加到布局中,简略说下步骤:
(1)获取需要改变或者要往里添加新控件的布局
(2)新建一个约束,ConstraintSet的实例对象
(3)利用约束的clone()方法,复制布局的约束
(4)新建控件,并把这个控件添加到布局里
(5)设置控件的约束
(6)最后新的约束添加到布局里
核心代码:

        //代码方式布局添加控件
        ConstraintLayout spinnerLayout = findViewById(R.id.spinner_layout);//获取布局
        ConstraintSet set = new ConstraintSet();//新建约束
        set.clone(spinnerLayout);//复制布局的约束
        //新建Spinner控件
        spinner = new CustomSpinner(this);
        spinner.setDropDownVerticalOffset(dpToPx(35));
        spinner.setBackgroundResource(R.drawable.login_spinner_bg);
        spinner.setAdapter(adapter);
        spinner.setId(R.id.customSpinner);//代码创建控件不会自动生成资源id,在res/values目录中新建一个资源文件ids.xml添加控件的id,最后进行手动绑定
        //往布局里添加控件
        spinnerLayout.addView(spinner);
        //设置该控件的约束
        set.constrainWidth(spinner.getId(),dpToPx(0));
        set.constrainHeight(spinner.getId(),dpToPx(35));
        set.connect(spinner.getId(),ConstraintSet.TOP,R.id.spinner_actionBar_bg,ConstraintSet.BOTTOM,dpToPx(20));
        set.connect(spinner.getId(),ConstraintSet.LEFT,ConstraintSet.PARENT_ID,ConstraintSet.LEFT,dpToPx(35));
        set.connect(spinner.getId(),ConstraintSet.RIGHT,ConstraintSet.PARENT_ID,ConstraintSet.RIGHT,dpToPx(35));
        //把新的约束添加到布局里
        set.applyTo(spinnerLayout);

这里简单介绍下ConstraintSet设置约束的方法:
(1)constrainWidth(int viewId,int width),设置控件在布局中的宽
(2)constrainHeight(int viewId,int height),设置控件在布局中的高
(3)connect(int startID,int startSide,int endID,int endSide,int margin),设置控件的相对位置:
a、第一个参数为控件的id,代码创建的控件要先利用setId()来设置好,然后再通过getId()获取id;
b、第二个参数即是控件的top或left等,比如top就是ConstraintSet.TOP;c、第三个参数被控件用作位置参考物的控件ID,如果是父控件即是ConstraintSet.PARENT_ID;
d、第四个参数跟第二个参数类似,代表位置(top、left、right、bottom);e、最后一个参数是margin,边距。
(4)connect(int startID,int startSide,int endID,int endSide),这个跟上面的方法相同,只是少了设置边距。
注:记得最后必须必须要把新的约束添加的布局里才能生效,applyTo(ConstraintLayout constraintlayout)

9、Spinner的基本属性
Spinner有两种显示形式,一种是下拉菜单,一种是弹出框,默认就是下拉菜单。

android:spinnerMode="dropdown" //下拉菜单
android:spinnerMode="dialog" //弹出框

其他属性

android:entries="@arrays/example" //绑定数据源
android:prompt="@string/xxx" //弹出对话框时的标题
android:dropDownVerticalOffset="40dp" //下拉框垂直偏移量
android:dropDownWidth="wrap_content" //下拉框的宽度
android:popupBackground="@drawable/xxx" //设置下拉框的背景

另外如果在xml中绑定数据源,那么xml中绑定的数据源必须在values文件夹下创建,arrays.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="example">
        <item>1</item>
        <item>2</item>
        <item>3</item>
        <item>4</item>
        <item>5</item>
    </string-array>
</resources>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容