文章目录

  • 问题描述
  • 页面布局
  • Activity与逻辑
  • 小结


问题描述

在项目开发的过程中,总会遇到各种关于搜索的功能开发。如果是趋于网络搜索的,则会考虑用到别人的搜索引擎。如果是趋于数据库搜索的,则需要自己写接口,由前台进行调用。那么如果是趋于本地内存搜索的呢?
本文将会以一个小案例的形式向大家介绍如何完成对安卓端手机内存文件进行搜索。

页面布局

首先编写搜索页面,效果如下图。

android api 搜索 文档 android 文件检索_内存


搜索页面由顶部的搜索栏,用于显示搜索结果的ListView和底部的确认选择的button。搜索栏与ListView还有一个TextView,用于展示当前搜索的本地路径,该TextView的数据变换在一定程度上可以缓解用户等待搜索结果的痛苦程度。哈哈哈!

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SearchActivity"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/searchEdit"
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:hint="搜索内容"/>
        <Button
            android:id="@+id/searchButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="搜索"></Button>
    </LinearLayout>
    <TextView
        android:id="@+id/searcbPath"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"></TextView>
    <ListView
        android:id="@+id/searchListView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1">
    </ListView>
    <Button
        android:id="@+id/ensureChoose"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="确定"></Button>
</LinearLayout>

Activity与逻辑

页面逻辑大致就是:
1.获取搜索词条
2.从根目录开始检索数据 ,如果文件名刚好包含搜索词条,则将该文件加入搜索列表
3.结果判断与数据渲染
具体代码如下:

package com.lvan.filescan;

import androidx.appcompat.app.AppCompatActivity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.zhch1999.filescan.adapters.FileItemAdapter;
import com.zhch1999.filescan.beans.FileBean;
import com.zhch1999.filescan.utils.FileScanUitl;

import java.io.File;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class SearchActivity extends AppCompatActivity {
    private EditText editTextSearch; //搜索框
    private ListView listViewSearch; //搜索内容显示的listview
    private Button buttonSearch,buttonEnsureChoose; // 搜索按钮 确定选择按钮
    private TextView textViewSearchPath;//当前搜索路径

    private List<FileBean> fileBeanList=new ArrayList<>(); //listview适配的数据list
    private FileItemAdapter fileItemAdapter=new FileItemAdapter(fileBeanList,SearchActivity.this); //listview 的适配器
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search);
        editTextSearch=findViewById(R.id.searchEdit);
        listViewSearch=findViewById(R.id.searchListView);
        listViewSearch.setAdapter(fileItemAdapter);
        buttonSearch=findViewById(R.id.searchButton);
        textViewSearchPath=findViewById(R.id.searcbPath);
        buttonSearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String edtContent=editTextSearch.getText().toString(); //用户输入的内容
                Toast.makeText(SearchActivity.this,edtContent,Toast.LENGTH_SHORT).show();
                SearchTask searchTask=new SearchTask();
                searchTask.execute(edtContent);
            }
        });
    }

    public class SearchTask extends AsyncTask {
        @Override
        protected void onPreExecute() {
            /**
             * 执行异步任务前操作
             */
            buttonSearch.setText("搜索中");
            super.onPreExecute();
        }

        @Override
        protected List<FileBean>  doInBackground(Object[] objects) {
            /**
             * 工作线程执行的方法 不能调用ui操作
             */
            String searchContent=objects[0].toString();
            Log.e("搜索内容",searchContent);
            String rootPath= Environment.getExternalStorageDirectory().getPath();//默认的手机内存的根路径
            File rootFile=new File(rootPath);
            List<FileBean> searchRes=new ArrayList<>();
            File[] listRootFiles=rootFile.listFiles();
            for(File file:listRootFiles){
                publishProgress(file.getAbsolutePath());
                if(file.isFile()){
                    if(file.getName().indexOf(searchContent)!=-1){
                        FileBean fileBean=new FileBean();
                        fileBean.filePath=file.getAbsolutePath();
                        fileBean.fileName=file.getName();
                        fileBean.lastModified=new FileScanUitl().getLastModified(file.getAbsolutePath());
                        fileBean.chidFileNumber=0;
                        fileBean.isDirec=false;
                        fileBean.fileSize=file.length();
                        fileBean.suffix="";
                        fileBeanList.add(fileBean);
                    }
                }else{
                    if(searchFile(file.getAbsolutePath(), searchContent)!=null){
                        searchRes.addAll(searchFile(file.getAbsolutePath(), searchContent));
                    }

                }
            }

            //File rootFile=new File(rootPath); //根路径不肯不存在吧。。。额遇到了再说
            //List<FileBean> searchRes = searchFile(rootPath, searchContent);
            Log.e("异步搜索结果", Arrays.asList(searchRes).toString());
            return searchRes;
        }

        @Override
        protected void onProgressUpdate(Object[] values) {
            /**
             * 可以在此方法内更新操作进度 需要调用publishProgress 方法才会执行
             */
            String temp=values[0].toString().split("/")[4];
            textViewSearchPath.setText("正在搜索路径:"+temp);
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Object o) {
            /**
             * 接收任务执行结果 进行ui操作
             * 工作在主线程,所以可以更新ui操作
             * doInBackground()的返回数据存储在 ojbect o 里面
             */
            List<FileBean> fileBeanList1= (List<FileBean>) o;
            if(fileBeanList1.size()!=0){
                textViewSearchPath.setText("搜索完成");
                fileBeanList.clear();
                fileBeanList.addAll(fileBeanList1);
                fileItemAdapter.notifyDataSetChanged();
            }else{
                textViewSearchPath.setText("没有搜索到文件");
            }
            buttonSearch.setText("搜索"); //异步搜索执行完成
            super.onPostExecute(o);
        }
    }
    /**
     * 遍历文件夹 搜索匹配的文件名
     */
    private List<FileBean> searchFile(String path, String content){
//        Log.e("搜索路径",path);
//        Log.e("搜索内容",content);
        List<FileBean> fileBeanList=new ArrayList<>();
        File file=new File(path);
        if(file!=null){
            File[] listFiles=file.listFiles();
            if(listFiles!=null){
                for(File fileItem:listFiles){
                    if(fileItem.isFile()){
                        if(fileItem.getName().indexOf(content)!=-1){
                            FileBean fileBean=new FileBean();
                            fileBean.filePath=fileItem.getAbsolutePath();
                            fileBean.fileName=fileItem.getName();
                            fileBean.lastModified=new FileScanUitl().getLastModified(fileItem.getAbsolutePath());
                            fileBean.chidFileNumber=0;
                            fileBean.isDirec=false;
                            fileBean.fileSize=fileItem.length();
                            fileBean.suffix="";
                            fileBeanList.add(fileBean);
                        }
                    }else{
                        if(searchFile(fileItem.getAbsolutePath(),content)!=null){
                            fileBeanList.addAll(searchFile(fileItem.getAbsolutePath(),content));
                        }
                    }
                }
                if(fileBeanList.size()==0){
                    return null;
                }else{
                    return fileBeanList;
                }
            }else{
                return null;
            }
        }else{
            return null;
        }
    }
}

其他的辅助类就不粘贴了,如果有需要的可以在下方留言!

最终运行效果:

android api 搜索 文档 android 文件检索_内存_02

小结

首先,我想到的办法是直接在主线程里面进行本地文件检索,然后结果大家能够预想,直接搜到程序卡死,然后就不动了。。后来想着用线程解决这个问题,经过比较选择了最简单的也算比较好用的异步线程(AsyncTask)来解决。使用页比较简单,具体步骤如下:
1.创建一个类继承自AsyncTask

public class SearchTask extends AsyncTask

2.在新建的类里面实现几个方法

@Override
            protected void onPreExecute() {
                /**
                 * 执行异步任务前操作
                 * 可以在里面更新UI,这个方法的执行是在主线程
                 */
                buttonSearch.setText("搜索中");
                super.onPreExecute();
            }
@Override
        protected List<FileBean>  doInBackground(Object[] objects) {
            /**
             * 工作线程执行的方法 不能调用ui操作 
             */
             //do sth here 主要执行耗时的逻辑
             return searchRes; //返回任务执行完成产生的结果
             }
@Override
        protected void onProgressUpdate(Object[] values) {
            /**
             * 可以在此方法内更新UI 需要调用publishProgress 方法才会执行
             */
            String temp=values[0].toString().split("/")[4];
            textViewSearchPath.setText("正在搜索路径:"+temp);
            super.onProgressUpdate(values);
        }
@Override
        protected void onPostExecute(Object o) {
            /**
             * 接收任务执行结果 进行ui操作
             * 工作在主线程,所以可以更新ui操作
             * doInBackground()的返回数据存储在 ojbect o 里面
             */
            List<FileBean> fileBeanList1= (List<FileBean>) o;
            if(fileBeanList1.size()!=0){
                textViewSearchPath.setText("搜索完成");
                fileBeanList.clear();
                fileBeanList.addAll(fileBeanList1);
                fileItemAdapter.notifyDataSetChanged();
            }else{
                textViewSearchPath.setText("没有搜索到文件");
            }
            buttonSearch.setText("搜索"); //异步搜索执行完成
            super.onPostExecute(o);
        }

还有一个取消的方法,但是我这里没有使用,因为用不着。上述重写的几个方法之间有逻辑调用关系。

SearchTask searchTask=new SearchTask();
searchTask.execute(edtContent); //传入参数
  1. onPreExecute()—线程执行之前会调用
  2. doInBackground(Object[] objects)—线程主体,onPreExecute()执行后才执行,在线程execute时传入的参数存储在Object[] objects之中
  3. .onProgressUpdate(Object[] values)—当进度更新时调用,嗯,在doInBackground方法中调用publishProgress(file.getAbsolutePath()); 将数据传入进来,传入的数据存储在Object[] values之中。
  4. onPostExecute(Object o) —线程执行完成时调用,即doInBackground()执行结束之后会调用该方法。doInBackground之中return的结果会被传入到Object o之中。