android 手动实现可输入下拉框Spinner控件(工具类)
一、前言
一丝感想
一直想着多写几篇博客,一个为了提升自己,也是给需要的人提供帮助。可惜,项目太近,琐事太多,实在闲不下来,唉。不管怎样,还是想抽点时间出来完成这篇博客!
进入正题
android 是自带有下拉框spinner控件的,但是android原生的Spinner控件是不支持用户输入的(据我所知),仅仅支持在数据列表确定的情况下进行选择。所以要实现一个手动输入的下拉框,我们需要自己手动实现。
在这里要感谢该博客给的灵感:https://blog.csdn.net/u013700040/article/details/52914070
大致思想是这样:通过一个EditText和一个ImageView的组合来实现控件,然后采用popupWindow进行界面弹出。
PopupWindow不了解的可以先去了解一下,这里不做深究,PopupWindow和AlertDialog相似,都是android用于与用户交互的对话框界面。
二、可输入Spinner实现
1、实现SpinnerPopupWindow类
- 作用:该类用于加载Spinner控件的下拉框界面,当点击自定义的Spinner时,会像android原生的Spinner那样弹出下拉供选择项。
- 代码:
/* * @ Description: * @ Time: 2019/5/18 18:40:24 * @ Author: lgy */public class SpinnerPopupWindow<T> extends PopupWindow { private List<T> datas;//listview数据 private NormalAdapter adapter; private LayoutInflater inflater; private ListView mListView; private Context mContext; // 上下文参参数 public SpinnerPopupWindow(Context context, List<T> datas, AdapterView.OnItemClickListener clickListener){ super(context); mContext=context; inflater = LayoutInflater.from(context); this.datas = datas; init(clickListener); } private void init(AdapterView.OnItemClickListener clickListener){ View view = inflater.inflate(R.layout.spinner_window_layout, null); setContentView(view); setWidth(LinearLayout.LayoutParams.WRAP_CONTENT); setHeight(LinearLayout.LayoutParams.WRAP_CONTENT); setFocusable(true); ColorDrawable dw = new ColorDrawable(0x00); setBackgroundDrawable(dw); mListView = (ListView) view.findViewById(R.id.popup_listview); mListView.setAdapter(adapter = new NormalAdapter(mContext,datas,R.layout.spinner_popup_item)); mListView.setOnItemClickListener(clickListener); }}
- 说明:代码比较简单,不做详细说明。将SpinnerPopupWindow定义为模板类,是为了方便扩展。在定义该类时传入一个类型即可。在构造函数中,传入一个 AdapterView.OnItemClickListener,该类型是适配器,也就是ListView的子项点击消息响应,以回调的形式传入。
- 界面:写完后,我们要手写一个界面来供该popupWindow加载(相当于Activity加载xml界面一样),命名为:spinner_window_layout,放在layout文件夹下。该界面代码应该只包含一个listView。代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/popup_listview" android:scrollbars="none" android:background="@drawable/round_edge_radius_tv_bl" android:layout_width="match_parent" android:layout_height="wrap_content"/>LinearLayout>
- 资源背景:好吧,差点忘记了给出round_edge_radius_tv_bl资源文件了,这个资源文件是设置ListView的背景,可以自己设计,以下给出代码:
文件命名:round_edge_radius_tv_bl.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#ffffff" /> <stroke android:width="1dp" android:color="#30000000" /> <corners android:radius="5dp" />shape>
- 适配器:既然定义了ListView,那么我们要定义一个适配器给该列表提供数据源。文件命名为NormalAdapter,代码如下:
/* * @ Description: * @ Time: 2019/5/21 17:00:22 * @ Author: lgy */public class NormalAdapter<T> extends BaseAdapter { protected Context context; private List<T> datas; protected LayoutInflater inflater; protected int layoutId; public NormalAdapter(Context context, List<T> datas, int layoutId) { this.context = context; this.datas = datas; inflater = LayoutInflater.from(context);//关联布局 this.layoutId = layoutId; } @Override public int getCount() { return datas.size(); } @Override public Object getItem(int position) { return datas.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if(convertView == null){ viewHolder = new ViewHolder(); convertView = inflater.inflate(R.layout.spinner_popup_item,null); viewHolder.textTv = (TextView) convertView.findViewById(R.id.tv_item); convertView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.textTv.setText(getItem(position).toString()); return convertView; } private class ViewHolder{ private TextView textTv; }}
到这里,PopupWindow的实现大致就结束了。popupWindow主要是定义了一个弹出界面,也就是一个ListView。
2、定义一个Spinner工具类
- 作用:在定义完PopupWindow之后,我们需要定义一个Spinner(下拉框),通过点击Spinner上的某个控件来实现PopupWindow的弹出。 前面已经说过,Spinner使用一个EditText和ImageView的组合来实现。那么他们的作用是什么呢?想你应该能猜到。
EditText:用来显示用户的选择数据和支持用户输入
ImageView:接收用户点击消息(点击弹出PopupWindow),当然你也可以使用Button,TextView,背景设为图片就行等。
类命名为:CommonSpinner,定义成模板类,便于扩展 - 实现类代码:
/* * @ Description:通用下拉框工具类 * @ Time: 2019/5/19 13:36:43 * @ Author: lgy */public class CommonSpinner<T> extends RelativeLayout { private Context context;//上下文 private List<T> datas;//下拉菜单数据集 private View Spinner; private SpinnerPopupWindow<T> popupWindow; private RelativeLayout rl_text; private EditText et_text; private ImageView iv_text; private String showTitle; private String showText; //接口,消息响应 private Spinner_ClickListener spinner_ClickListener; //用于显示popupWindow private View.OnClickListener clickListener= new OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.common_iv: popupWindow.setWidth(rl_text.getWidth()); popupWindow.showAsDropDown(rl_text); // setTextImage(R.mipmap.arrow_down_bl); break; } } }; //用于popupWindow中ListView的子项点击事件 private AdapterView.OnItemClickListener itemClickListener=new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { popupWindow.dismiss(); et_text.setText(datas.get(position).toString()); showText = datas.get(position).toString(); //这个接口是用来在其他界面做点击操作的时候负责调用的 if(spinner_ClickListener != null){ spinner_ClickListener.ClickListener(datas.get(position).toString()); } } }; public CommonSpinner(Context context, String showTitle, List<T> datas) { super(context); this.context=context; this.showTitle = showTitle; this.datas=datas; init(); } //设置接口 public void setSpinner_ClickListener(Spinner_ClickListener spinner_ClickListener){ this.spinner_ClickListener = spinner_ClickListener; } private void init() { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); Spinner = inflater.inflate(R.layout.common_spinner_layout, this); rl_text=(RelativeLayout)Spinner.findViewById(R.id.common_rl); et_text = (EditText) Spinner.findViewById(R.id.common_et); iv_text=(ImageView) Spinner.findViewById(R.id.common_iv); et_text.setText(showTitle); //设置TextView点击事件 iv_text.setOnClickListener(clickListener); //初始化popupWindow popupWindow = new SpinnerPopupWindow<>(getContext(), datas, itemClickListener); popupWindow.setOnDismissListener(dismissListener); } /** * 监听popupwindow取消 */ private PopupWindow.OnDismissListener dismissListener = new PopupWindow.OnDismissListener() { @Override public void onDismiss() { // setTextImage(R.mipmap.spinner_up); } }; /** * 给TextView右边设置图片 * @param resId */ private void setTextImage(int resId) { Drawable drawable = getResources().getDrawable(resId); drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());// 必须设置图片大小,否则不显示 et_text.setCompoundDrawables(null, null, drawable, null); } //用于外部获取TextView中的值 public String getText(){ return et_text.getText().toString().trim(); } /** * @Description: 在外部获取Spinner界面上的EditText * @Author:lgy * @Date:2019/5/20 15:22 */ public EditText getEditView(){ return et_text; }}
说明:定义一个Spinner工具类,继承自RelativeLayout,加载一个布局xml文件即可,文件命名为:common_spinner_layout(仅包含一个EditText和ImageView)。在下拉框工具类中,构造函数传入一个
①View.OnClickListener:控件的点击响应,用在ImageView上弹出PopupWindow。
②AdapterView.OnItemClickListener:ListView的子项点击响应函数,当点击ListView上的子项时,将数据显示到EditText上。
请重点考虑一下这行代码:
Spinner = inflater.inflate(R.layout.common_spinner_layout, this);
这句话的作用是将定义的界面布局加载到定义了工具类(RelativeLayout)上。
- 界面代码:文件命名为common_spinner_layout
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/common_rl" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/round_edge_radius_tv_bl"> <EditText android:id="@+id/common_et" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="2dp" android:layout_centerVertical="true" android:paddingLeft="@dimen/dp_2" android:background="@color/purewhite" android:text="下拉框" android:textSize="12sp"/> <ImageView android:id="@+id/common_iv" android:layout_width="30dp" android:layout_height="30dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:paddingTop="@dimen/dp_10" android:paddingBottom="@dimen/dp_10" android:paddingLeft="@dimen/dp_10" android:paddingRight="@dimen/dp_5" android:src="@mipmap/arrow_down_bl"/>RelativeLayout>
界面只包含一个EditText和右边的一个ImageView,ImageView加载图片为下箭头,代表下拉框标志。
图片资源:arrow_down_bl(可以自己去Iconfont上找矢量图,然后引用即可)
到这里,Spinner工具类也封装完成,那么下面该到Activity里面来引用这个工具类了。
3、引用写好的CommonSpinner类:
在activity里定义一个布局,这里我采用的是LinearLayout线性布局,代码如下:
这里只定义一个线性布局,通过这个布局来加载我们写好的工具类CommonSpinner。
<LinearLayout android:id="@+id/geology_age" android:layout_width="0dp" android:layout_height="30dp" android:layout_weight="1" android:layout_marginLeft="@dimen/dp_5" android:layout_marginRight="@dimen/dp_20" android:layout_gravity="center_vertical" android:gravity="center" android:textSize="12sp" android:background="@drawable/round_edge_radius_tv_bl" android:orientation="horizontal"> LinearLayout>
活动代码:
List<String> data_list; String[] strings=getResources().getStringArray(R.array.geology_factor); data_list= Arrays.asList(strings); spinner_factor=new CommonSpinner<>(GeotechnicalDescription.this,"请选择",data_list); spinner_factor.setSpinner_ClickListener(new Spinner_ClickListener() { @Override public void ClickListener(String data) { if(data.equals(getResources().getString(R.string.DIY))){ spinner_factor.getEditView().setText(""); EditText et=spinner_factor.getEditView(); et.setFocusable(true); et.setFocusableInTouchMode(true); InputMethodManager inputManager =(InputMethodManager)et.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); if(inputManager!=null) { et.requestFocus(); inputManager.showSoftInput(et, 0); } } } });
下拉框中的数据是已经在string.xml文件中定义好的:如
<string name="DIY">"自定义"string> <string-array name="geology_factor"> <item>alitem> <item>al+plitem> <item>hitem> <item>自定义item> string-array>
这里根据自己的需要定义即可。
到这里整个下拉框工具类的封装就完成了,一个很简单的demo。相信android初学者都能看懂,毕竟笔者也才是android入门级的。
4、效果图
差点忘了添加效果图:
三、总结
好吧,到这里这篇博客的内容大致就写完了。我一直觉得写博客的目的一个是为了分享知识,给需要的人提供帮助,但是最主要的应该还是自己对知识的巩固吧。网上这方面的知识太多,因为笔者也是参照了先者的成果,然后根据自己的需求进行改造。希望对看到这篇博客的你能提供你所需要的帮助,那也达到了我写博客的目的。如果有什么错误或者纰漏,或者不懂的地方,欢迎留言交流。
一起学习,一起成长。
---------------------------------------------------------------------分割线------------------------------------------------------------------------------------------忘记说了,在CommonSpinner类中用到了一个接口 【 private Spinner_ClickListener spinner_ClickListener;】,这个接口用来将用户在下拉框中选中的数据显示在输入框中。
新定义一个接口
public interface Spinner_ClickListener { public void ClickListener(String data);
其中ClickListener为接口函数,负责将下拉框的数据显示在输入框中。
然后在CommonSpinner类中引入该接口即可。
更多相关文章
- AS下如何生成自定义的.jks签名文件, 以及如何生成数字签名
- Android自定义字体类库Calligraphy--快速实现自定义应用字体
- Android 手机跳转到权限管理界面汇总
- Android 底部弹出自定义Dialog(支付宝微信选择)
- android实现自定义控件及如何在其他项目中使用
- Android 界面滑动实现---Scroller类 从源码和开发文档中学习(让你
- android 自定义状态栏和导航栏分析与实现
- 第三部分:Android 应用程序接口指南---第二节:UI---第一章 用户界