Perform/Add 自定义 ListView 的搜索功能

Perform/Add search functionality to a custom ListView

我创建了所有已安装应用程序的自定义列表视图,但我不知道如何添加搜索功能,因为它有点复杂(..我的应用程序)有人可以帮我吗? (picture of the app )

App.java - 应用程序构造函数

public class App {
    private int number;
    private String name;
    private String version;
    private Drawable drawable;

    public App(int number, String name, String version, Drawable drawable){
        this.number = number;
        this.name = name;
        this.version = version;
        this.drawable = drawable;
    }
    //Getters & Setters...
}

AppAdapter.java - 列表视图适配器

public class AppAdapter extends ArrayAdapter<App> {
    Context context;
    List<App> objects;

    public AppAdapter(Context context, int resources, int textViewResources, List<App> objects){
        super(context, resources, textViewResources, objects);

        this.context = context;
        this.objects = objects;
    }

    public View getView(int position, View convertView, ViewGroup parent){
        LayoutInflater layoutInflater = ((Activity)context).getLayoutInflater();
        View view = layoutInflater.inflate(R.layout.custom_card,parent,false);

        TextView tvName =  (TextView)view.findViewById(R.id.tvName);
        TextView tvVersion =  (TextView)view.findViewById(R.id.tvVersion);
        TextView tvNumber =  (TextView)view.findViewById(R.id.tvNumber);
        ImageView ivImage = (ImageView)view.findViewById(R.id.ivImage);

        App current = objects.get(position);
        tvName.setText(String.valueOf(current.getName()));
        tvVersion.setText(String.valueOf(current.getVersion()));
        tvNumber.setText(String.valueOf(current.getNumber()));
        ivImage.setImageDrawable(current.getDrawable());

        return view;
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {   
    ArrayList<App> appList;
    ListView lv;
    AppAdapter appAdapter;
    App lastSelected;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EditText etSearch = findViewById(R.id.etSearch);
        
        PackageManager packageManager = getPackageManager();
        List<PackageInfo> mApps = packageManager.getInstalledPackages(0);
        //array strings to all packages, names and version
        final String[] arrPackages = new String[mApps.size()];
        final String[] arrVersion = new String[mApps.size()];
        String[] arrName = new String[mApps.size()];
        //array of Drawables for icons...
        Drawable[] arrIcons = new Drawable[mApps.size()];
        App[] arrApps = new App[mApps.size()];
        appList = new ArrayList<>();
        //reading all app's packages and version to the arrays
        for (int i = 0; i < mApps.size(); i++){
            arrVersion[i] = mApps.get(i).versionName;
            arrPackages[i] = mApps.get(i).packageName;
        }

        for (int i = 0; i < mApps.size(); i++){
            try {//getting app's names from theres packages
                arrName[i] = (String) packageManager.getApplicationLabel(packageManager.getApplicationInfo(arrPackages[i], PackageManager.GET_META_DATA));
            } catch (PackageManager.NameNotFoundException e) {
                arrName[i] = "Unknown";
            }

            try {//same as names for icons
                arrIcons[i] = packageManager.getApplicationIcon(arrPackages[i]);
            } catch (PackageManager.NameNotFoundException e) {
                arrIcons[i] = getDrawable(R.drawable.placeholder);
            }
            arrApps[i] = new App(i + 1, "Name: "+arrName[i], "Version: "+arrVersion[i], arrIcons[i]);
            appList.add(arrApps[i]);
        }
        //on item click open app
        appAdapter = new AppAdapter(this,0,0,appList);
        lv = findViewById(R.id.lv);
        lv.setAdapter(appAdapter);

        lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                lastSelected = appAdapter.getItem(position);
                Intent launchIntent = getPackageManager().getLaunchIntentForPackage(arrPackages[position]);
                if (launchIntent != null) {
                    startActivity(launchIntent);//null pointer check in case package name was not found
                }
            }
        });

        //(trying to..) Add Text Change Listener to EditText
        etSearch.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // Call back the Adapter with current character to Filter
                MainActivity.this.appAdapter.getFilter().filter(s.toString());
            }
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count,int after) {
            }
            @Override
            public void afterTextChanged(Editable s) {
            }
        });
    }
}

(当我尝试搜索某些内容时,它什么也没给出...)

交换这条线

App current = objects.get(position);

至此

App current = (App) getItem(position);

您不需要将 List<App> objects; 保留在您的适配器中,它在构造函数 super 调用(最后一个参数)中传递并为您保存在引擎盖下。查看 ArrayAdapter source - 传递的 ArrayList 保留为 mObjects 并在某些方法中进一步使用,例如getPositiongetCountgetItem

ArrayAdapter 已经 implements Filterable,所以有一个覆盖方法 getFilter,其中 returns ArrayFilter - 这个内部 class 是在底部声明。当您调用 getFilter().filter( 时,performFiltering 会被调用(在单独的线程中)并遍历数据的本地副本(第 588 行)。它使用 values.get(i).toString().toLowerCase() 将数组中的对象与传递的 String(实际上是 CharSequence)进行比较。所以在你的自定义 App class 覆盖 toString 方法和 return 中有一些可搜索的值,例如

@Override
public String toString(){
    return name;
}

这不是最好的方法,因为 toString 可以用在很多机制中(它的 Object 的基本方法)和上面的 toString 实现两个 Apps 具有相同的名称,但不同的 versionnumber 被威胁为同一对象,这是不正确的......也许更好的方法是 return name+version+number;,但你仍然有Drawable 在那里。这就是为什么我建议(在编辑之前的这个答案中)制作自己的 class extends BaseAdapter 并实现自己的过滤或至少使用 ArrayAdapter,但覆盖 getFilter 方法和return 你自己的 Filter 实现比较变量而不是使用 toString。然后不需要覆盖这个class,保持原样。默认情况下,它 returns kind-of 内存地址,因此它对于每个新的 App 实例都是唯一的,即使是使用完全相同的变量创建时也是如此

也在 THIS 答案中,您可以找到如何实现 Filter

的好例子