有趣的问题,我尝试实现了一个简单的自定义适配器,基于
ArrayAdapter源代码。为了简洁起见,大部分未使用的代码和注释都被省略了,如果您不确定,请查看我上面链接的ArrayAdapter源代码,它有很好的注释。
操作原理非常简单,适配器的
getCount()
将实际元素数量加一。同时,
getItem(int position)
会检查是否请求了最后一个“虚拟”项目,并返回你的 “Custom…” 字符串。
createViewFromResource(...)
方法在显示最后一个“虚拟”项时也会检查,如果是,则绑定一个 onClick 监听器。
覆盖的 Filter 也会将结果计数加一,以使 AutoCompleteView 相信有匹配项,因此它保持下拉列表打开状态。
MainActivity.java
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] countries = new String[]{
"Switzerland", "Mexico", "Poland", "United States of Murica"};
String footerText = "Custom Footer....";
CustomAutoCompleteAdapter adapter = new CustomAutoCompleteAdapter(
this, android.R.layout.simple_list_item_1, countries, footerText);
adapter.setOnFooterClickListener(new CustomAutoCompleteAdapter.OnFooterClickListener() {
@Override
public void onFooterClicked() {
Toast toast = Toast.makeText(getApplicationContext(), "Yummy Toast!", Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
});
AutoCompleteTextView actv = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView1);
actv.setThreshold(0);
actv.setAdapter(adapter);
}
}
CustomAutoCompleteAdapter.java
public class CustomAutoCompleteAdapter extends BaseAdapter implements Filterable {
public interface OnFooterClickListener {
public void onFooterClicked();
}
private List<String> mObjects;
private final Object mLock = new Object();
private int mResource;
private int mDropDownResource;
private ArrayList<String> mOriginalValues;
private ArrayFilter mFilter;
private LayoutInflater mInflater;
private String mFooterText;
private OnFooterClickListener mListener;
public CustomAutoCompleteAdapter(Context context, int resource, String[] objects, String footerText) {
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mResource = mDropDownResource = resource;
mObjects = Arrays.asList(objects);
mFooterText = footerText;
}
public void setOnFooterClickListener(OnFooterClickListener listener) {
mListener = listener;
}
@Override
public int getCount() {
return mObjects.size()+1;
}
@Override
public String getItem(int position) {
if(position == (getCount()-1)) {
return mFooterText;
}
return mObjects.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(position, convertView, parent, mResource);
}
private View createViewFromResource(int position, View convertView, ViewGroup parent,
int resource) {
View view;
TextView text;
if (convertView == null) {
view = mInflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
text = (TextView) view;
} catch (ClassCastException e) {
Log.e("CustomAutoCompleteAdapter", "Layout XML file is not a text field");
throw new IllegalStateException("Layout XML file is not a text field", e);
}
text.setText(getItem(position));
if(position == (getCount()-1)) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mListener != null) {
mListener.onFooterClicked();
}
}
});
} else {
view.setOnClickListener(null);
view.setClickable(false);
}
return view;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(position, convertView, parent, mDropDownResource);
}
@Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<String>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<String> list;
synchronized (mLock) {
list = new ArrayList<String>(mOriginalValues);
}
results.values = list;
results.count = list.size()+1;
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<String> values;
synchronized (mLock) {
values = new ArrayList<String>(mOriginalValues);
}
final int count = values.size();
final ArrayList<String> newValues = new ArrayList<String>();
for (int i = 0; i < count; i++) {
final String value = values.get(i);
final String valueText = value.toString().toLowerCase();
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size()+1;
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mObjects = (List<String>) results.values;
notifyDataSetChanged();
}
}
}
layout/activity_main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
tools:context=".MainActivity">
<AutoCompleteTextView
android:id="@+id/autoCompleteTextView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
![enter image description here](https://istack.dev59.com/kyrvZ.webp)