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>