Spinner下拉列表我们经常用到,一般我们在xml文件定义好,然后在代码中设置一下adapter即可。
<FrameLayout
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_weight="1.0"
android:paddingRight="8dp" >
<Spinner
android:id="@+id/sp_mode_ddjdevwrite"
android:layout_width="fill_parent"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dp"
android:animationCache="true"
android:background="#FFFFFF"
android:drawSelectorOnTop="true"
android:prompt="@string/mode"
android:spinnerMode="dialog" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:src="@drawable/list" />
</FrameLayout>spMode = (Spinner)findViewById(R.id.sp_mode_ddjdevwrite);
String[] modeArray = new String[]{"YR710-4", "YR800-4", "YR710-6/1180", "YR800-6/1180",
"YR900-6/1180", "YR1000-6/1180", "YR1120-6/1180", "YR1250-6/1430", "YR1400-6/1430",
"YR1600-6/1430", "YR1800-6/1430", "YR2000-6/1430", "YR2240-6/1730",
"YR2500-6/1730", "YR630-8/1180", "YR710-8/1180", "YR800-8/1180", "YR900-8/1430",
"YR1000-8/1430", "YR1120-8/1430", "YR1250-8/1430", "YR1400-8/1430",
"YR1600-8/1730", "YR1800-8/1730", "YR2000-8/1730", "YR2240-8/2150",
"YR2500-8/2150", "YR500-10/1180", "YR560-10/1180", "YR630-10/1180",
"YR710-10/1180", "YR800-10/1180", "YR900-10/1430", "YR1000-10/1430",
"YR1120-10/1430", "YR1250-10/1430", "YR1400-10/1430", "YR1600-10/1730",
"YR1800-10/1730", "YR2000-10/1730", "YR2240-10/2150", "YR2500-10/2150",
"YR450-12/1430", "YR500-12/1430", "YR560-12/1430", "YR630-12/1430",
"YR710-12/1430", "YR800-12/1430", "YR900-12/1730", "YR1000-12/1730",
"YR1120-12/1730", "YR1250-12/1730", "YR1400-12/1730", "YR1600-12/1730",
"YR1800-12/1730", "YR2000-12/2150", "YR2240-12/2150", "YR2500-12/2150",
"YR450-16/1430", "YR500-16/1430", "YR560-16/1430", "YR630-16/1430", "YR710-16/1430",
"YR800-16/1430", "YR900-16/1730", "YR1000-16/1730", "YR1120-16/1730", "YR1250-16/1730",
"YR1400-16/2150"};
modelAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, modeArray);
modelAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//为Spinner设置适配器
spMode.setAdapter(modelAdapter);如果下拉的数据比较多,想要找到我们想要的数据就很不方便,如果有个筛选输入框(在输入的时候自动去匹配)就好,
有这样的需求那么接下来就实现这样的需求:
开始我是在想Spinner要是有个方法能设置它的布局就好,但是Spinner并没有提供这样的方法,于是就从Spinner的源码分析,发现没那么简单
点击Spinner时内部调用的是Spinner内部类DialogPopup的show()方法,而show()方法创建的是有一个AlertDialog。此时想扩展继承Spinner覆盖它的内部类DialogPopup同时覆盖它的show方法 那更是有难度(从JAVA的角度,java根本就没有提供内部类“覆盖”这么一种机制, 参考 http://blog.csdn.net/dksokil/article/details/4546189)
至此 想通过继承Spinner的方式来扩展是不行的啦,看来只能模仿了Spinner的内部实现。
实现思路:
(1) 创建FuzzyMatchSpinner 继承FrameLayout
(2) 重写perfomClick()方法
判断dialog 是否为空,为空则创建Dialog, 不为空则显示
(3) 提供setAdapter方法
setAdapter(SpinnerAdapter adapter,List<String> data)
setAdapter(SpinnerAdapter adapter,String[] data)
(4) 提供setOnItemSelectedListener方法
(5) 创建自定义Dialog (使用自定义layout) 该layout包含了EditText, ListView
对EditText进行输入变化的监听,变化后的内容去匹配data,匹配后的结果封装到adapter中,设置ListView的adapter
(6) 每次显示下列列表内容的时候 将上次选中的项选中并置顶
(7) 选中某项后将FuzzyMatchSpinner 的显示内容设置成该项内容
具体实现源码如下:
attrs.xml 增加自定义属性
<declare-styleable name="fuzzymatchspinner">
<attr name="prompt" format="string"></attr>
</declare-styleable>FuzzyMatchSpinner.java
package com.example.demotest.widget;
import java.util.ArrayList;
import java.util.List;
import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import com.example.demotest.R;
/**
* 带有筛选功能的Spinner
* @author Lue
* @since 2015-05-06
*/
public class FuzzyMatchSpinner extends FrameLayout
{
private String TAG = "FuzzyMatchSpinner";
private TextView tvCheckValue;
private Dialog mPopup;
private int checkPosition;
private String mPrompt;
private SpinnerAdapter listAdapter;
private String[] data;
/**
* 显示结果
*/
private ListView lvResult;
/**
* 筛选输入框
*/
private EditText etKeyWords;
private Spinner.OnItemSelectedListener onItemSelectedListener;
public FuzzyMatchSpinner(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public FuzzyMatchSpinner(Context context, AttributeSet attrs)
{
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.widget_fuzzymatchspinner,
this);
tvCheckValue = (TextView) findViewById(R.id.tvcheckvalue_fuzzymatchspinner);
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.fuzzymatchspinner);
mPrompt = a.getString(R.styleable.fuzzymatchspinner_prompt);
a.recycle();
}
public FuzzyMatchSpinner(Context context)
{
super(context);
// TODO Auto-generated constructor stub
}
@Override
public boolean performClick()
{
boolean handled = super.performClick();
if(!handled)
{
handled = true;
if(mPopup == null)
{
createAlertDialog();
}
else if(!mPopup.isShowing())
{
mPopup.show();
}
}
return handled;
}
public void setAdapter(SpinnerAdapter adapter,List<String> data)
{
if(data == null || data.size() == 0)
{
setAdapter(adapter,new String[0]);
}
else
{
String[] tmpArray = new String[data.size()];
data.toArray(tmpArray);
setAdapter(adapter,tmpArray);
}
}
public void setAdapter(SpinnerAdapter adapter,String[] data)
{
listAdapter = adapter;
this.data = data;
checkPosition = 0;
if(data != null && data.length > 0)
{
tvCheckValue.setText(data[checkPosition]);
if(onItemSelectedListener != null)
{
onItemSelectedListener.onItemSelected(lvResult, null, checkPosition, 0);
}
}
}
public void setOnItemSelectedListener(Spinner.OnItemSelectedListener onItemSelectedListener)
{
this.onItemSelectedListener = onItemSelectedListener;
}
private void createAlertDialog()
{
mPopup = new Dialog(getContext());
mPopup.show();
if(mPrompt != null)
{
mPopup.setTitle(mPrompt);
}
mPopup.setContentView(R.layout.widget_fuzzyspiner_dialog);
etKeyWords = (EditText)mPopup.findViewById(R.id.etkeywords_spinnerdialog);
lvResult = (ListView)mPopup.findViewById(R.id.lv_results_spinnerdialog);
//增加变化监听
etKeyWords.addTextChangedListener(new TextWatcher(){
@Override
public void afterTextChanged(Editable s)
{
if(s == null || s.toString() == null || "".equals(s.toString().trim()))
{
if(listAdapter != null)
{
lvResult.setAdapter(new DropDownAdapter(listAdapter));
}
}
else
{
String[] result = assembleMatchItems(s.toString());
ArrayAdapter tempAdapter = new ArrayAdapter<String>(getContext(),android.R.layout.simple_spinner_item, result);
tempAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
lvResult.setAdapter(new DropDownAdapter(tempAdapter));
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after)
{
// TODO Auto-generated method stub
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count)
{
// TODO Auto-generated method stub
}
});
if(data == null || data.length == 0 || data.length < 5)
{
etKeyWords.setVisibility(View.GONE);
}
else
{
etKeyWords.setVisibility(View.VISIBLE);
}
if(listAdapter != null)
{
lvResult.setAdapter(new DropDownAdapter(listAdapter));
lvResult.setSelection(checkPosition);
//lvResult.setSelectionFromTop(checkPosition, 0);
lvResult.setItemChecked(checkPosition, true);
}
lvResult.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
DropDownAdapter adapter = (DropDownAdapter)parent.getAdapter();
if(adapter != null)
{
String checkValue = (String)adapter.getItem(position);
if(checkPosition != position && onItemSelectedListener != null)
{
onItemSelectedListener.onItemSelected(parent, view, position, id);
}
setSelection(findCheckPosition(checkValue));
tvCheckValue.setText(checkValue);
}
mPopup.dismiss();
mPopup = null;
}
});
}
/**
* 筛选匹配
* @param keyWords 关键字
* @return
*/
private String[] assembleMatchItems(String keyWords)
{
String[] matchResult = new String[0];
if(data != null)
{
List<String> rt = new ArrayList<String>();
int len = data.length;
for(int i=0; i<len; i++)
{
if(data[i].toLowerCase().contains(keyWords.toLowerCase()))
{
rt.add(data[i]);
}
}
if(rt.size() > 0)
{
matchResult = new String[rt.size()];
rt.toArray(matchResult);
}
}
return matchResult;
}
private int findCheckPosition(String value)
{
int checkPosition = 0;
if(data != null)
{
int len = data.length;
for(int i=0; i<len; i++)
{
if(data[i].equals(value))
{
checkPosition = i;
break;
}
}
}
return checkPosition;
}
public void setSelection(int which)
{
this.checkPosition = which;
}
/**
* <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance
* into a ListAdapter.</p>
*/
private static class DropDownAdapter implements ListAdapter, SpinnerAdapter {
private SpinnerAdapter mAdapter;
private ListAdapter mListAdapter;
/**
* <p>Creates a new ListAdapter wrapper for the specified adapter.</p>
*
* @param adapter the Adapter to transform into a ListAdapter
*/
public DropDownAdapter(SpinnerAdapter adapter) {
this.mAdapter = adapter;
if (adapter instanceof ListAdapter) {
this.mListAdapter = (ListAdapter) adapter;
}
}
public int getCount() {
return mAdapter == null ? 0 : mAdapter.getCount();
}
public Object getItem(int position) {
return mAdapter == null ? null : mAdapter.getItem(position);
}
public long getItemId(int position) {
return mAdapter == null ? -1 : mAdapter.getItemId(position);
}
public View getView(int position, View convertView, ViewGroup parent) {
return getDropDownView(position, convertView, parent);
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return mAdapter == null ? null :
mAdapter.getDropDownView(position, convertView, parent);
}
public boolean hasStableIds() {
return mAdapter != null && mAdapter.hasStableIds();
}
public void registerDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.registerDataSetObserver(observer);
}
}
public void unregisterDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(observer);
}
}
/**
* If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
* Otherwise, return true.
*/
public boolean areAllItemsEnabled() {
final ListAdapter adapter = mListAdapter;
if (adapter != null) {
return adapter.areAllItemsEnabled();
} else {
return true;
}
}
/**
* If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
* Otherwise, return true.
*/
public boolean isEnabled(int position) {
final ListAdapter adapter = mListAdapter;
if (adapter != null) {
return adapter.isEnabled(position);
} else {
return true;
}
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return getCount() == 0;
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mPopup != null && mPopup.isShowing()) {
mPopup.dismiss();
}
}
}
使用FuzzyMatchSpinner
(1) 在xml 声明
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<span style="color:#ff0000;">xmlns:myViewXmlns="http://schemas.android.com/apk/res-auto/com.example.demotest"</span>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical" >
<!-- 型号 -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="48dp"
android:layout_marginLeft="36dp"
android:layout_marginTop="16dp"
android:layout_marginRight="36dp"
android:gravity="center_vertical"
android:orientation="horizontal" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:text="型号:" />
<FrameLayout
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_weight="1.0"
android:paddingRight="8dp" >
<Spinner
android:id="@+id/sp_mode_ddjdevwrite"
android:layout_width="fill_parent"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dp"
android:animationCache="true"
android:background="#FFFFFF"
android:drawSelectorOnTop="true"
android:prompt="@string/mode"
android:spinnerMode="dialog" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:src="@drawable/list" />
</FrameLayout>
</LinearLayout>
<!-- 型号 -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="48dp"
android:layout_marginLeft="36dp"
android:layout_marginTop="16dp"
android:layout_marginRight="36dp"
android:gravity="center_vertical"
android:orientation="horizontal" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:text="型号:" />
<com.example.demotest.widget.FuzzyMatchSpinner
android:id="@+id/fuzzysp_mode_ddjdevwrite"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_weight="1.0"
android:clickable="true"
<span style="color:#ff0000;">myViewXmlns:prompt="型号"</span>
/>
</LinearLayout>
</LinearLayout>注意以上 <com.example.demotest.widget.FuzzyMatchSpinner> 需要增加 android:clickable="true" 属性的,否点击的时候 performClick()方法是不会被执行到的,也就不会弹出下拉列表的,Spinner的perfomClick()方法之所以能被执行也是因为Spinner的属性文件也有android:clickable="true" 属性设置的。(这个具体原因就请看View的事件分发机制了)
(2) 代码初始化
Spinner spMode = (Spinner)findViewById(R.id.sp_mode_ddjdevwrite);
FuzzyMatchSpinner spMode2 = (FuzzyMatchSpinner)findViewById(R.id.fuzzysp_mode_ddjdevwrite);
String[] modeArray = new String[]{"YR710-4", "YR800-4", "YR710-6/1180", "YR800-6/1180",
"YR900-6/1180", "YR1000-6/1180", "YR1120-6/1180", "YR1250-6/1430", "YR1400-6/1430",
"YR1600-6/1430", "YR1800-6/1430", "YR2000-6/1430", "YR2240-6/1730",
"YR2500-6/1730", "YR630-8/1180", "YR710-8/1180", "YR800-8/1180", "YR900-8/1430",
"YR1000-8/1430", "YR1120-8/1430", "YR1250-8/1430", "YR1400-8/1430",
"YR1600-8/1730", "YR1800-8/1730", "YR2000-8/1730", "YR2240-8/2150",
"YR2500-8/2150", "YR500-10/1180", "YR560-10/1180", "YR630-10/1180",
"YR710-10/1180", "YR800-10/1180", "YR900-10/1430", "YR1000-10/1430",
"YR1120-10/1430", "YR1250-10/1430", "YR1400-10/1430", "YR1600-10/1730",
"YR1800-10/1730", "YR2000-10/1730", "YR2240-10/2150", "YR2500-10/2150",
"YR450-12/1430", "YR500-12/1430", "YR560-12/1430", "YR630-12/1430",
"YR710-12/1430", "YR800-12/1430", "YR900-12/1730", "YR1000-12/1730",
"YR1120-12/1730", "YR1250-12/1730", "YR1400-12/1730", "YR1600-12/1730",
"YR1800-12/1730", "YR2000-12/2150", "YR2240-12/2150", "YR2500-12/2150",
"YR450-16/1430", "YR500-16/1430", "YR560-16/1430", "YR630-16/1430", "YR710-16/1430",
"YR800-16/1430", "YR900-16/1730", "YR1000-16/1730", "YR1120-16/1730", "YR1250-16/1730",
"YR1400-16/2150"};
modelAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, modeArray);
modelAdapter
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//为Spinner设置适配器
spMode.setAdapter(modelAdapter);
//String[] modeArray2 = new String[]{"YR710-4", "YR800-4", "YR710-6/1180", "YR800-6/1180"};
ArrayAdapter modelAdapter2 = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, modeArray);
modelAdapter2
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spMode2.setAdapter(modelAdapter2,modeArray);运行效果图如下:
-->
这篇博客介绍了如何在Android开发中为Spinner添加筛选功能。作者通过分析Spinner的源码,发现无法直接扩展Spinner来实现需求。因此,他选择创建一个新的组件FuzzyMatchSpinner,继承自FrameLayout,并在点击时显示带筛选输入框的对话框。通过监听EditText的输入变化,动态匹配数据并更新ListView。此外,他还实现了选中项置顶和设置适配器的方法,以提高用户体验。
1183

被折叠的 条评论
为什么被折叠?



