重拾Android之路之Spinner

引言

最近做的项目想仿做滴滴出行首页的悬浮框。

正文

效果参考滴滴出行;

工欲善其事,必先利其器!来......

Spinner详解

Spinner控件初始化时,会调用它的选择监听事件,默认选择第一个

一、Spinner比较好用的属性
  1. Spinnerentries属性,就可以不用设置SpinnerAdapter,也可以填充数据
<Spinner
   android:id="@+id/spCity"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:prompt="请选择城市"
   android:entries="@array/cities"/>
  1. SpinnerspinnerMode属性,Spinner显示为对话框或者是下拉框形式;
android:spinnerMode=["dialog"|"dropdown"]
prompt属性表示spinner列表上方的提示

spinnerMode="dialog"效果图,可以看到我们设置的提示符"请选择城市"

spinnerMode="dropdown"效果图

spinner_drop_item
二、设置spinner显示数据

使用Spinner布局文件使用的是系统默认的;使用创建ArrayAdapter两种方式,传入不同的数据源;

  • 使用xml文件作为数据源
private void initView(){
    city= (Spinner) findViewById(R.id.spCity);
    SpinnerAdapter  adapter=null;
    adapter=ArrayAdapter.createFromResource(this,R.array.cities,android.R.layout.simple_spinner_dropdown_item);
    city.setAdapter(adapter);
}
  • 使用数组或者是List作为数据数据源
ArrayList<String> list=new ArrayList<String>();
SpinnerAdapter adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,list);
三、spinner点击事件处理
city.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
      @Override
      public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            //这个方法里可以对点击事件进行处理
            //i指的是点击的位置,通过i可以取到相应的数据源
      }
      @Override
      public void onNothingSelected(AdapterView<?> adapterView) {
      }
})
四、用代码来点击spinner
//选择Spinner里的第二个数据;
city.setSelection(1,true);
自定义Spinner
  • spinner就是下拉选择组件,系统自带的spinner使用起来非常方便,首先定义一个array(strings.xml),如下:

<array name="grade">
  <item>一年级</item>
  <item>二年级</item>
  <item>三年级</item>
  <item>四年级</item>
  <item>五年级</item>
  <item>六年级</item>
</array>

代码如下:

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

这样就实现了一个简单的spinner,显示如下:


但这并不是我想要的样式和效果,下面我们就一点点的来改造它。

(1)改变初始布局

spinner_layout.xml

<?xml version = "1.0" encoding = "utf-8" ?>

<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 = "一年级" >
</TextView >

然后替换createFromResource中的即可,如下:

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

这样还不够,因为还有一个带箭头的背景,将背景设置为透明即可,如下:

spinner.setBackgroundColor(0x0);

这样初始布局的展示就与spinner_layout一样了。

(2)改变列表item布局

经过上面的修改后,发现弹窗中列表item的布局也变成了spinner_layout,查看ArrayAdapter的构造函数可知有mResource和mDropDownResource两个变量,其中mResource就是初始布局,而mDropDownResource则是列表item的布局。

createFromResource函数中,mResourcemDropDownResource赋值相同。但是ArrayAdapter还有一个setDropDownViewResource函数。

首先定义一个布局,如下:

spinner_layout.xml


<?xml version="1.0" encoding="utf-8"?>
<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="一年级">
</TextView>

然后使用setDropDownViewResource函数即可,如下:

adapter.setDropDownViewResource(R.layout.spinner_item);

(3)改变弹窗背景及位置

在开始的动画中可以看到弹窗会遮挡住,我们想让弹窗处于下方,同时弹窗是圆角带箭头的。

这就需要使用spinner的两个函数setPopupBackgroundResourcesetDropDownVerticalOffset

但是注意这两个函数都需要在android4.1版本及以上,鉴于目前4.1以下版本已经很少了,所以我们只考虑4.1以上即可,代码如下:

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

另外有一种方式同样也可以达到效果,在这里记录一下:
(但是很粗糙,没能满足紧靠正下方的需求)

解决方案:这是因为spinner有一个属性:android:overlapAnchor=”false”
这个属性默认是true。改为false即可。但是不知道为什么这个属性在代码提示中是没有的。所以记录下来。

(4)添加选中效果

经过上面的处理,我们已经得到想要的样式。但是还差一点,弹窗列表中缺少选中的样式。比如说我当前选择“二年级”,在弹窗中,对应的item字体应该加深加粗。在spinner源码中搜寻了一遍,发现并没有对应的函数和解决方法,那么我们自己动手吧。

其实spinner是使用adapter来加载列表的,而我们使用createFromResource函数会自动创建了adapter,我们可以自定义一个adapter,如下:

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 new SpinnerAdapter<>(context, textViewResId, strings);
    }
}

注意ArrayAdapter中的getDropDownView函数是获取弹窗item使用的view的,而不是getView函数。

同时我们要重新写一个createFromResource函数。

将之前使用的adapter替换成自定义这个,同时为spinner设置监听即可,更改后的完整代码如下:

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){
    }
});

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容