Android自定义Spinner弹出选择器

安卓中有Spinner这样一种控件,可以实现点击旁边的一个选择型的 > 来进行用户选择自己需要的属性,然后展示保存等等操作,在各种APP中都是非常常见的,但安卓自带的Spinner控件太过于死板,又难看。我们需要改风格,弹出动画,以及样式大小用安卓原生的Spinner是无法实现的。所以我简单介绍一下如何自定义Spinner,把它变成自己想要的样子。下面是一个简单的效果图,其中布局界面等等都可随意更改。

效果图.gif

实现原理
根据自己实际需求,写出Spinner在界面展示的布局,一般情况下都有一个TextView的文字展示选择后的元素,旁边有个可点击的按钮,点击后利用PopupWindow弹窗,弹出一条ListView的列表,以供用户选择,点击Item后再返回给TextView显示。我这里演示最简单的一种布局实现原理。

1.定义好所需要的布局##

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      
 android:layout_width="match_parent"    
 android:layout_height="match_parent"  
 android:gravity="center"   
 android:id="@+id/spinnerid"   
 android:orientation="horizontal">
        <TextView      
          android:id="@+id/text_spinner"     
          android:layout_width="wrap_content"   
          android:layout_height="wrap_content"  
          android:text="2016年6月" />  
      <ImageView     
         android:id="@+id/image_spinner"     
         android:src="@mipmap/spinner1"   
         android:layout_width="wrap_content"   
         android:layout_height="wrap_content" />
</LinearLayout>

上面是自定义Spinner的Xml布局代码,就两个基本的TextView和ImageView,可以根据需要添加更多随意选择样式图片,用于界面展示效果。

<LinearLayout     
  android:layout_width="fill_parent"    
  android:layout_height="fill_parent"    
  android:orientation="vertical"     
  xmlns:android="http://schemas.android.com/apk/res/android">    
<ListView        
  android:background="@color/material_white"        
  android:id="@+id/listView"        
  android:layout_width="fill_parent"        
  android:layout_height="fill_parent"        
  android:divider="@null"        
  android:dividerHeight="1px">    
</ListView>
</LinearLayout>

这里设置PopupWindow弹窗的布局,我采用的是ListView,用于演示简单的Item选择,下面是List里面的Item,也是非常简单的布局。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/material_white"
    android:orientation="vertical">
        <TextView
            android:id="@+id/date_text" 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical" />
</LinearLayout>

2.配置ListView的Adapter##

public class MySpinnerAdapter extends BaseAdapter {
    LayoutInflater inflater;
    Context context;
    ArrayList<String> list;
    public MySpinnerAdapter(Context context, ArrayList<String> list) {
        super();
        this.context = context;
        this.list = list;
        inflater = LayoutInflater.from(context);
    }
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return list.size();
    }
    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return list.get(position);    }
    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = null;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.item_daka_spinner, null);
            viewHolder = new ViewHolder();
            viewHolder.textView = (TextView) convertView.findViewById(R.id.date_text);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.textView.setText(list.get(position));
        return convertView;
    }
    public class ViewHolder {
        TextView textView;
    }
    public void refresh(List<String> l) {
        this.list.clear();
        list.addAll(l);
        notifyDataSetChanged();
    }
    public void add(String str) {
        list.add(str);
        notifyDataSetChanged();
    }
    public void add(ArrayList<String> str) {
        list.addAll(str);
        notifyDataSetChanged();
    }
}

简单的使用BaseAdapter ,然后设置了构造函数,用于传入数据,并展示在View中。

3.自定义Spinner类##

直接继承LinearLayout,定义好构造函数,找到之前为Spinner定义好的布局以及子View。

public class SpinnerView extends LinearLayout{    
private Context context;    
private LinearLayout layout;    
private ListView listView;    
private MySpinnerAdapter adapter;    
private PopupWindow popupWindow;    
private TextView mSpinnerText;    
private ImageView mSpinnerImag;    
private ArrayList<String> listData = new ArrayList<>();    
public SpinnerView(Context context) {
        super(context);
        this.context = context;
    }    
public SpinnerView(final Context context, AttributeSet attrs) { 
       super(context, attrs);
        this.context = context;
        LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.include_my_spinnerview,this);
        mSpinnerText = (TextView)findViewById(R.id.text_spinner);
        mSpinnerImag = (ImageView)findViewById(R.id.image_spinner);
    }    
public SpinnerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
    }

然后对外开放一个方法,用于传入数据给Adapter ,初始化适配器数据,构造方法根据自己需求定义,我这里用的最简单的List容器。当数据接收到后,初始化控件属性,设置监听。

public void setMyData(ArrayList<String> data){
    this.listData = data;
    adapter = new MySpinnerAdapter(context, listData);    // 默认设置下拉框的标题为数据的第一个
    mSpinnerText.setText((CharSequence) adapter.getItem(0));    // 点击右侧按钮,弹出下拉框
    mSpinnerImag.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) { 
           showWindow(v);
        }
    });
}

然后设置PopupWindow的弹框:

public void showWindow(View v) {
    // 找到布局文件
    layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.mypinner_dropdown, null);
    // 实例化listView
    listView = (ListView) layout.findViewById(R.id.listView);
    // 设置listView的适配器
    listView.setAdapter(adapter);
    // 实例化一个PopuWindow对象
    popupWindow = new PopupWindow(v);
    // 设置弹框的宽度为布局文件的宽
    popupWindow.setWidth(getWidth());
    // 高度设置的300
    popupWindow.setHeight(300);
    // 设置一个透明的背景,不然无法实现点击弹框外,弹框消失
    popupWindow.setBackgroundDrawable(new BitmapDrawable());
    // 设置点击弹框外部,弹框消失
    popupWindow.setOutsideTouchable(true);
    // 设置焦点
    popupWindow.setFocusable(true);
    // 设置所在布局
    popupWindow.setContentView(layout);
    // 设置弹框出现的位置,在v的正下方横轴偏移textview的宽度
    popupWindow.showAsDropDown(v, -mSpinnerText.getWidth(), 0);
    // listView的item点击事件
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            mSpinnerText.setText(listData.get(arg2));// 设置所选的item作为下拉框的标题
            // 弹框消失
            popupWindow.dismiss();
            popupWindow = null;
        }
    });
}

弹窗的样式,以及大小宽高等,直接可以设置,想要酷炫一点就加入动画,这里只列出部分属性。需要知道更多的去查popupWindow的API。

4.引入##

在XML中直接使用布局即可

<com.customview.MySpinner.SpinnerView
    android:id="@+id/spinnerid"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#fff"/>

在代码中,记得添加数据。自己也可以拿到选中的属性

mySpinner = (SpinnerView) findViewById(R.id.spinnerid);
persons = new ArrayList<String>();
for (int i = 16; i <= 20; i++) {
    for (int r = 1; r <= 12; r++) {
        persons.add("20" + i + "年" + r + "月");
    }
}
mySpinner.setMyData(persons);

一个自定义的Spinner就完成了。

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

推荐阅读更多精彩内容