Android 自定义弹窗 附带搜索过滤功能

简介: 前两天要求在项目中添加个小功能,今天正好有时间随手写了一个小demo,过程分享给大家。以后如果有此类需求可直接移植使用。

项目场景:

前两天要求在项目中添加个小功能,今天正好有时间随手写了一个小demo,过程分享给大家。以后如果有此类需求可直接移植使用。

需求是因为在下拉列表中选择一个项作为数据显示在界面上,但是所有的选项很多,下翻找很麻烦所有需要用个搜索框解决一下这个问题,下面是Demo的效果,可以先看一下。Demo做的比较简单,但是扩展性很大,需要的小伙伴可以自行改造使用,源码放在了文章的最后。

cac26880ec8441c79f512af0b487e2e0.gif



问题描述

开发前有个问题就是想用现成已有的东西放进去直接就能用了,也没有做自定义列表的东西,后面发现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()下。


相关文章
|
1月前
|
Android开发 开发者
安卓应用开发中的自定义视图
【9月更文挑战第37天】在安卓开发的海洋中,自定义视图犹如一座座小岛,等待着勇敢的探索者去发现其独特之处。本文将带领你踏上这段旅程,从浅滩走向深海,逐步揭开自定义视图的神秘面纱。
41 3
|
1月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
83 0
|
16天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
17天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
28 5
|
1月前
|
Android开发
Android开发表情emoji功能开发
本文介绍了一种在Android应用中实现emoji表情功能的方法,通过将图片与表情字符对应,实现在`TextView`中的正常显示。示例代码展示了如何使用自定义适配器加载emoji表情,并在编辑框中输入或删除表情。项目包含完整的源码结构,可作为开发参考。视频演示和源码详情见文章内链接。
68 4
Android开发表情emoji功能开发
|
25天前
|
安全 Android开发 iOS开发
Android vs iOS:探索移动操作系统的设计与功能差异###
【10月更文挑战第20天】 本文深入分析了Android和iOS两个主流移动操作系统在设计哲学、用户体验、技术架构等方面的显著差异。通过对比,揭示了这两种系统各自的独特优势与局限性,并探讨了它们如何塑造了我们的数字生活方式。无论你是开发者还是普通用户,理解这些差异都有助于更好地选择和使用你的移动设备。 ###
48 3
|
1月前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
2月前
|
Android开发 开发者
Android平台无纸化同屏如何实现实时录像功能
Android平台无纸化同屏,如果需要本地录像的话,实现难度不大,只要复用之前开发的录像模块的就可以,对我们来说,同屏采集这块,只是数据源不同而已,如果是自采集的其他数据,我们一样可以编码录像。
|
7天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
9天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。