项目场景:
前两天要求在项目中添加个小功能,今天正好有时间随手写了一个小demo,过程分享给大家。以后如果有此类需求可直接移植使用。
需求是因为在下拉列表中选择一个项作为数据显示在界面上,但是所有的选项很多,下翻找很麻烦所有需要用个搜索框解决一下这个问题,下面是Demo的效果,可以先看一下。Demo做的比较简单,但是扩展性很大,需要的小伙伴可以自行改造使用,源码放在了文章的最后。
问题描述
开发前有个问题就是想用现成已有的东西放进去直接就能用了,也没有做自定义列表的东西,后面发现ListView的过滤功能不是很友好,它的过滤方式智能对数据的第一个字符进行过滤,如果是中间出现的字符它就会过滤不出来,所以又重写了适配器并且重写了它的过滤方法,最后满足了功能要求。
public Cursor swapCursor(Cursor newCursor) { if (newCursor == mCursor) { return null; } Cursor oldCursor = mCursor; if (oldCursor != null) { if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver); if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver); } mCursor = newCursor; if (newCursor != null) { if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver); if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver); mRowIDColumn = newCursor.getColumnIndexOrThrow("_id"); mDataValid = true; // notify the observers about the new cursor notifyDataSetChanged(); } else { mRowIDColumn = -1; mDataValid = false; // notify the observers about the lack of a data set notifyDataSetInvalidated(); } return oldCursor; }
源码里面的内容就是只能对列表中数据的第一个字进行过滤,这样并不能完全起到过滤的作用,下面看一下解决方式。
解决方案:
1、先创建要自定义的内容,弹窗自定义内容dialog_searchview.xml、列表自定义内容fragment_recomend_item.xml(列表自定义也可以放其他东西,如图片等)。
dialog_searchview.xml
<?xml version="1.0" encoding="utf-8"?> <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" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical"> <androidx.appcompat.widget.SearchView android:id="@+id/sv_search" android:layout_width="match_parent" android:layout_height="50dp" ></androidx.appcompat.widget.SearchView> <ListView android:id="@+id/lv_list" android:layout_width="match_parent" android:layout_height="match_parent"></ListView> </LinearLayout>
fragment_recomend_item.xml
<?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"> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="40dp" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:layout_marginLeft="15dp" android:textSize="15sp" android:text="123456" android:textColor="@color/black" android:gravity="center_vertical"></TextView> </LinearLayout>
2、创建数据Been,并且绑定到适配器。
Recomend.java
package com.example.testdemosearchview; /** * Create by HHCH on 2023/5/23 */ public class Recomend { private String title; public Recomend(String title) { this.title = title; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
RecomendAdapter.java
public class RecomendAdapter extends BaseAdapter implements Filterable { Context context; List<Recomend> data; //过滤数据 List<Recomend> backData;//用来备份原始数据 MyFilter mFilter ; public RecomendAdapter(Context context, List<Recomend> data) { this.context = context; this.data = data; backData = data; } @Override public int getCount() { return data.size(); } //获取当前数据 @Override public Object getItem(int i) { return data.get(i).getTitle(); } @Override public long getItemId(int i) { return 0; } @Override public View getView(int i, View view, ViewGroup viewGroup) { if (view ==null){ view = LayoutInflater.from(context).inflate(R.layout.fragment_recomend_item,null); } TextView tv_title = view.findViewById(R.id.tv_title); tv_title.setText(data.get(i).getTitle()); return view; } //执行setFilterText()方法时 自动执行 @Override public Filter getFilter() { if (mFilter ==null){ mFilter = new MyFilter(); } return mFilter; } //我们需要定义一个过滤器的类来定义过滤规则 class MyFilter extends Filter{ //我们在performFiltering(CharSequence charSequence)这个方法中定义过滤规则 @Override protected FilterResults performFiltering(CharSequence charSequence) { FilterResults result = new FilterResults(); List<Recomend> list ; if (TextUtils.isEmpty(charSequence)){//当过滤的关键字为空的时候,我们则显示所有的数据 list = backData; }else {//否则把符合条件的数据对象添加到集合中 list = new ArrayList<>(); for (Recomend recomend:backData){ if (recomend.getTitle().contains(charSequence)){ Log.d("","performFiltering:"+recomend.toString()); list.add(recomend); } } } result.values = list; //将得到的集合保存到FilterResults的value变量中 result.count = list.size();//将集合的大小保存到FilterResults的count变量中 return result; } //在publishResults方法中告诉适配器更新界面 @Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { data = (List<Recomend>)filterResults.values; Log.d("","publishResults:"+filterResults.count); if (filterResults.count>0){ notifyDataSetChanged();//通知数据发生了改变 Log.d("","publishResults:notifyDataSetChanged"); }else { notifyDataSetInvalidated();//通知数据失效 Log.d("","publishResults:notifyDataSetInvalidated"); } } } }
代码中有注释,大家可以看注释理解。
3、使用弹窗显示内容。
MainActivity.java
public class MainActivity extends AppCompatActivity { private String[] dic = new String[]{"框架平房", "框架楼房", "砖混平房", "砖混楼房", "砖混厦房", "砖混安架房", "砖混起脊房", "砖木房", "砖木厦房", "砖木安架房", "土木房", "土木厦房", "土木安架房", "混合房", "混合安架房", "土窑洞", "石窑洞", "砖窑洞", "混合窑洞", "砖墙彩钢瓦房", "彩钢瓦活动板房", "砖(石)木简易房", "土木简易房", "其他简易房"}; private List<Recomend> list = new ArrayList<>(); private RecomendAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { Button btn_open = findViewById(R.id.bt_open); //填充数据源 for (int i = 0; i < dic.length;i++){ list.add(new Recomend(dic[i])); } btn_open.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); View customView = View.inflate(MainActivity.this,R.layout.dialog_searchview,null); SearchView searchView = customView.findViewById(R.id.sv_search); ListView listView = customView.findViewById(R.id.lv_list); listView.setTextFilterEnabled(true); searchView.setIconifiedByDefault(false); // 设置该SearchView显示搜索图标 searchView.setSubmitButtonEnabled(true); // 设置该SearchView内默认显示的搜索文字 searchView.setQueryHint("查找"); builder.setView(customView); adapter = new RecomendAdapter(MainActivity.this,list); listView.setAdapter(adapter); //searchView输入监听 searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } //输入内容发送改变时执行下面的方法 @Override public boolean onQueryTextChange(String newText) { if (TextUtils.isEmpty(newText)){ //清除ListView的过滤 listView.clearTextFilter(); }else { //使用用户输入的内容对ListView的列表项进行过滤 listView.setFilterText(newText); } return true; } }); AlertDialog dialog = builder.create(); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { dialog.dismiss(); Toast.makeText(MainActivity.this,adapter.getItem(i).toString(),Toast.LENGTH_SHORT).show(); } }); dialog.show(); dialog.setCanceledOnTouchOutside(true);// dialog弹出后,点击界面其他部分dialog消失 } }); } }
71
整体的流程就这样,需要注意的是在使用SearchView的时候,要将数据过滤写在输入内容发生变化的方法onQueryTextChange()下。