当前位置: 首页 > article >正文

【Android】模糊搜索与数据处理

【Android】模糊搜索与数据处理

本篇博客主要以根据输入内容动态获取城市为例进行讲解。

获取城市

这一部分主要是根据输入的信息去动态获取城市信息

首先定义了一个名为 NetUtil 的类,主要用于通过 HTTP 请求获取城市信息。

public class NetUtil {
    
    private static final String URL_CITY_DAY = "https://geoapi.qweather.com/v2/city/lookup?";
    private static final String API_KEY = "";

    public static String doGet(String urlStr) throws IOException {
        String result = "";
        try {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url(urlStr)
                    .build();
            Response response = client.newCall(request).execute();
            result = response.body().string();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            return result;
        }
    }
    public static String getCitys(String name) throws IOException {
        String weatherUrl = URL_CITY_DAY + "location=" + name + "&key=" + API_KEY + "&range=cn";
        String cityResult = doGet(weatherUrl);
        return cityResult;
    }
}

URL 定义

  • URL_WEATHER 是一个常量,定义了用于获取天气信息的 API URL。这个 API 来自“易客天气”平台。
  • URL_CITY_DAY 是另一个常量,定义了查询城市的 API URL。这个 API 来自“和风天气”平台。
  • API_KEY 用于存放和风天气的 API 密钥,但是目前是空的,需要填入实际的 API 密钥才能使用。

doGet 方法

  • 这个方法用于发起 HTTP GET 请求。
  • 使用了 OkHttp 库来发送请求,urlStr 是请求的 URL。
  • 方法首先创建一个 OkHttpClient 对象,然后构建一个 Request,并使用 client.newCall(request).execute() 来执行该请求。
  • 请求的响应 (Response) 通过 response.body().string() 获取响应内容,将其存储到 result 变量中。
  • 如果在请求过程中发生异常,会捕获 IOException 并抛出一个 RuntimeException
  • 最后,无论是否有异常,都会返回 result,即请求的响应结果。

getCitys 方法

  • 这个方法用于根据城市名称查询城市的相关信息。
  • 首先,它将城市名 name 通过 URL 参数形式与 API 密钥和 range=cn 参数一起拼接成完整的 API URL,目的是查询中国范围内的城市。
  • 然后,它调用 doGet 方法来发送请求,并获取请求的响应内容 cityResult
  • 最后,记录获取到的响应数据,并返回查询结果 cityResult

下面我们要在MainActivity中去实现根据输入信息发起网络请求,并通过异步线程获取该输入文字相关的城市数据:

private void fetchCitys(String city) {
    if (city == null || city.trim().isEmpty()) {
        return;  // 如果输入为空,不进行网络请求
    }
    new Thread(() -> {
        try {
            String weatherOfCity = NetUtil.getCitys(city);
            if (weatherOfCity != null) {
                Message message = Message.obtain();
                message.what = 0;
                message.obj = weatherOfCity;
                mHandler.sendMessage(message);  // 将获取到的城市数据通过Handler发送到主线程
            } else {
                runOnUiThread(() -> Toast.makeText(SearchForCitysActivity.this, "未能获取到城市数据", Toast.LENGTH_SHORT).show());
            }
        } catch (IOException e) {
            e.printStackTrace();
            runOnUiThread(() -> Toast.makeText(SearchForCitysActivity.this, "网络请求失败:" + e.getMessage(), Toast.LENGTH_SHORT).show());
        }
    }).start();
}

private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            if (msg.what == 0) {
                String city = (String) msg.obj;
                if (city == null) {
                    Toast.makeText(SearchForCitysActivity.this, "网络请求失败!!", Toast.LENGTH_SHORT).show();
                    return;
                }
                parseToCityString(city);
                cityItemRecyclerView.getAdapter().notifyDataSetChanged();
            }
        }
    };

获取到了 JSON 格式的城市数据,下面我们定义了一个名为 parseToCityString 的方法,负责解析从 API 返回的城市信息 JSON 数据,并将其转换为一个城市信息列表:

private void parseToCityString(String cityJson) {
        List<String> cityList = new ArrayList<>();

        // 将 JSON 字符串解析为 JsonObject
        JsonObject jsonObject = JsonParser.parseString(cityJson).getAsJsonObject();

        // 获取 "location" 数组
        JsonArray jsonArray = jsonObject.getAsJsonArray("location");

        if (jsonArray == null) {
            Log.e(TAG, "Location array is null");
            runOnUiThread(() -> Toast.makeText(SearchForCitysActivity.this, "未能获取到城市信息", Toast.LENGTH_SHORT).show());
            return;
        }

        // 遍历数组中的每个元素
        for (JsonElement jsonElement : jsonArray) {
            JsonObject cityObject = jsonElement.getAsJsonObject();

            // 获取城市名称和行政区信息
            String name = cityObject.get("name").getAsString();
            String adm1 = cityObject.get("adm1").getAsString();
            String adm2 = cityObject.get("adm2").getAsString();

            // 组合为所需的格式
            String cityInfo = name + "——" + adm1 + "——" + adm2;
            cityList.add(cityInfo);
        }

        citys = cityList;
        runOnUiThread(() -> {
            SearchCityItemsAdapter adapter = (SearchCityItemsAdapter) cityItemRecyclerView.getAdapter();
            if (adapter != null) {
                adapter.updateCityList(citys);
            }
        });
    }

初始化城市列表

  • 创建一个空的 cityList,用于存储解析出来的城市信息字符串。

解析 JSON 字符串

  • 使用 JsonParser.parseString 方法将传入的 cityJson 字符串转换为 JsonObject。这个对象包含从服务器获取的城市数据。

获取 “location” 数组

  • JsonObject 中提取名为 "location" 的数组,它包含多个城市的信息。每个元素代表一个城市的数据。

检测数据有效性

  • 检查 "location" 数组是否为 null。如果是 null,表示服务器没有返回有效的城市数据。此时通过记录日志和在主线程显示 Toast 提示用户,通知未能获取到城市信息,并结束方法的执行。

解析每个城市的信息

  • 遍历 "location" 数组中的每个元素。每个元素都是一个包含城市信息的 JsonObject
  • 从每个城市的 JsonObject 中提取城市名称(name)、省级行政区(adm1)和地级行政区(adm2)。
  • 将提取的信息组合成一个字符串,格式为 城市名——省级行政区——地级行政区,并将其添加到 cityList 中。

保存解析结果

  • 将生成的 cityList 赋值给类的成员变量 citys,这使得其他部分可以访问到最新的城市数据。

更新 UI

  • 使用 runOnUiThread 将解析后的城市信息更新到 UI 中。因为 UI 操作只能在主线程上进行,所以通过这个方法确保在主线程执行更新操作。
  • 获取 RecyclerView 的适配器 SearchCityItemsAdapter,并调用适配器的 updateCityList(citys) 方法,传入最新的城市列表,更新 RecyclerView 中的城市列表显示。

现在我们就把根据输入的信息去搜索相应的城市就实现了,下面我们就要开始进行数据处理了。

数据处理与信息传递

SearchView

下面大致说一下SearchView用法:

SearchView 的主要功能是为用户提供一个搜索输入框,通常用于在应用中实现搜索功能。以下是其核心功能:

  1. 搜索输入:允许用户输入搜索关键词。
  2. 实时建议:支持根据用户输入的关键词动态提供搜索建议。
  3. 语音搜索:可以配置成支持语音输入功能(需要设备支持)。
  4. 图标化显示:可以在初始状态下以搜索图标的形式显示,用户点击图标时展开为输入框。
  5. 提交搜索请求:当用户输入完查询后,可以点击提交按钮或键盘上的回车键触发搜索请求。
  6. 显示占位提示:可以设置 queryHint 来显示输入提示,引导用户输入。

我们在使用的时候,主要使用的是监控输入的信息。

在创建searchView并获取实例后,可以设置监听事件:

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return true;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                fetchCitys(newText);  // 调用获取城市的方法
                return true;
            }
        });

onQueryTextSubmit(String query) 这个方法在用户提交搜索请求时触发,即用户输入完关键词并按下“提交”键(通常是键盘上的回车键)在这个方法中,你可以处理用户的完整搜索请求,比如开始执行搜索或查询操作。

onQueryTextChange(String newText) 这个方法在用户每次修改搜索框内容时触发,比如用户键入或删除字符时。通常用于实现实时搜索或提供动态建议。

我们在进行模糊搜索的时候就要使用的是onQueryTextChange()这个方法,这里我们调用了上面的fetchCitys()去获取相关的城市。

RecyclerView绑定

我们获取了输入信息所对应的城市之后,就需要把相应的信息显示在搜索框下面,我们这里就需要使用到RecyclerView。

当然,我们并不能仅仅只创建一个简单的RecyclerView,因为我们会对想要查询的城市进行点击,所以要设置点击事件,并且还需要把选中的城市返回到Activity方便进行其他操作。

先创建Adapter:

public class SearchCityItemsAdapter extends RecyclerView.Adapter<SearchCityItemsAdapter.SearchCityItemsViewHolder> {

    List<String> citys;
    private OnItemClickListener onItemClickListener;

    // 定义点击事件接口
    public interface OnItemClickListener {
        void onItemClick(String cityInfo);
    }

    // 构造函数中传入点击监听器
    public SearchCityItemsAdapter(List<String> citys, OnItemClickListener listener) {
        this.citys = citys;
        this.onItemClickListener = listener;
    }

    @NonNull
    @Override
    public SearchCityItemsAdapter.SearchCityItemsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.city_recyclerview_item, parent, false);
        return new SearchCityItemsViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull SearchCityItemsAdapter.SearchCityItemsViewHolder holder, int position) {
        String cityInfo = citys.get(position);
        holder.cityItem.setText(cityInfo);

        // 设置点击事件
        holder.itemView.setOnClickListener(v -> {
            if (onItemClickListener != null) {
                onItemClickListener.onItemClick(cityInfo);
            }
        });
    }

    @Override
    public int getItemCount() {
        return citys == null ? 0 : citys.size();
    }

    public class SearchCityItemsViewHolder extends RecyclerView.ViewHolder {
        TextView cityItem;

        public SearchCityItemsViewHolder(@NonNull View itemView) {
            super(itemView);
            cityItem = itemView.findViewById(R.id.cityName_item);
        }
    }

    public void updateCityList(List<String> newCityList) {
        this.citys = newCityList;
        notifyDataSetChanged();
    }
}


我们自定义了一个点击事件监听器接口 OnItemClickListener,并在 RecyclerView.Adapter 中传递了这个监听器。

上面代码我们可以实现点击RecyclerView中的item后,通过 onItemClickListener.onItemClick(cityInfo);,我们就可以在Activity中获取点击的城市名称。

Activity中实现如下:

	    cityItemRecyclerView = findViewById(R.id.search_recyclerview);
        LinearLayoutManager layoutManager1 = new LinearLayoutManager(this);
        cityItemRecyclerView.setLayoutManager(layoutManager1);
        // 创建适配器并传入点击事件监听器
        SearchCityItemsAdapter adapter = new SearchCityItemsAdapter(citys, new SearchCityItemsAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(String cityInfo) {
                Intent intent1 = new Intent(SearchForCitysActivity.this, AddCityActivity.class);
                intent1.putExtra("CityName", cityInfo);
                intent1.putStringArrayListExtra("CityNames", (ArrayList<String>) cityNames);
                startActivity(intent1);
            }
        });
        cityItemRecyclerView.setAdapter(adapter);

我们在创建RecyclerView的Adapter的时候就创建了点击事件监听器,重写了onItemClick(String cityInfo)方法,使我们在点击item后可以把城市名称传递给下一个活动。


这样我们就完整实现了模糊搜索与数据处理。


已经到底啦!!


http://www.kler.cn/a/318250.html

相关文章:

  • 干货分享之Python爬虫与代理
  • ES6字符串的新增方法
  • 基于node一键发布到服务器的js脚本
  • Java:解决因为大小写不规范导致的接收不到数据
  • 图论-代码随想录刷题记录[JAVA]
  • Linux源码阅读笔记-V4L2框架基础介绍
  • 鸿萌数据恢复服务: 修复 Windows, Mac, 手机中 “SD 卡无法读取”错误
  • Parallels Desktop 20(Mac虚拟机) v20.0.0 for Mac 最新破解版(支持M系列)
  • 江协科技STM32学习- P18 实验-PWM输入捕获测频率PWMI输入捕获模式测频率和占空比
  • QT Creator cmake 自定义项目结构, 编译输出目录指定
  • C++ STL容器(三) —— 迭代器底层剖析
  • BFS 解决最短路问题(C++)
  • Vue3操作DOM元素
  • C++信奥老师解一本通题 1164:digit函数
  • 【每日一题】LeetCode 2207.字符串中最多数目的子序列(贪心、字符串、前缀和)
  • 基于深度学习的能源消耗预测
  • linux-vim的使用
  • 【WebLogic】WebLogic 11g 控制台模式下安装记录
  • mysql中的json查询
  • 美业门店怎么提升业绩?连锁美业门店管理系统收银系统拓客系统源码
  • docker部署clickhouse
  • 计算机毕业设计之:基于微信小程序的疫苗预约系统的设计与实现(源码+文档+讲解)
  • 基于MTL的多任务视频推荐系统
  • Linux学习 重定向 管道 流
  • 前端组件库Element UI 的使用
  • 深入解析:Kubernetes 如何使用 etcd 作为配置中心和注册中心