为自定义的ListView添加搜索功能。

huangapple go评论89阅读模式
英文:

Perform/Add search functionality to a custom ListView

问题

我创建了一个自定义的ListView来显示所有安装的应用程序但我不知道如何添加搜索功能因为它有点复杂..我的应用程序),有人可以帮我吗
[(应用程序的图片)][1]

App.java - 应用程序构造函数
```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;
    }
    //获取器和设置器...
}

AppAdapter.java - ListView 适配器

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);
        //所有包、名称和版本的字符串数组
        final String[] arrPackages = new String[mApps.size()];
        final String[] arrVersion = new String[mApps.size()];
        String[] arrName = new String[mApps.size()];
        //图标的Drawable数组...
        Drawable[] arrIcons = new Drawable[mApps.size()];
        App[] arrApps = new App[mApps.size()];
        appList = new ArrayList<>();
        //将所有应用程序的包和版本读入数组
        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 {//从包中获取应用程序的名称
                arrName[i] = (String) packageManager.getApplicationLabel(packageManager.getApplicationInfo(arrPackages[i], PackageManager.GET_META_DATA));
            } catch (PackageManager.NameNotFoundException e) {
                arrName[i] = "Unknown";
            }

            try {//与名称类似,获取图标
                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]);
        }
        //点击条目打开应用程序
        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);//在找不到包名的情况下进行空指针检查
                }
            }
        });

        //向EditText添加文本更改监听器
        etSearch.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // 使用当前字符调用适配器进行过滤
                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) {
            }
        });
    }
}

(当我尝试搜索时,什么都没有显示...)


<details>
<summary>英文:</summary>
i created a custom listView of all installed apps but i don&#39;t know how to add a search functionality because it&#39;s a little complicated(..my app) can anyone help me with that?
[(picture of the app )][1]
App.java - app constructor

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 &amp; Setters...

}

AppAdapter.java - listView Adapter

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

public AppAdapter(Context context, int resources, int textViewResources, List&lt;App&gt; 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&lt;PackageInfo&gt; 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&lt;&gt;();
//reading all app&#39;s packages and version to the arrays
for (int i = 0; i &lt; mApps.size(); i++){
arrVersion[i] = mApps.get(i).versionName;
arrPackages[i] = mApps.get(i).packageName;
}
for (int i = 0; i &lt; mApps.size(); i++){
try {//getting app&#39;s names from theres packages
arrName[i] = (String) packageManager.getApplicationLabel(packageManager.getApplicationInfo(arrPackages[i], PackageManager.GET_META_DATA));
} catch (PackageManager.NameNotFoundException e) {
arrName[i] = &quot;Unknown&quot;;
}
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, &quot;Name: &quot;+arrName[i], &quot;Version: &quot;+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&lt;?&gt; 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) {
}
});
}

}

(when i try to search something it&#39;s gives nothing...)
[1]: https://i.stack.imgur.com/nRZUP.jpg
</details>
# 答案1
**得分**: 0
将这一行代码

App current = objects.get(position);


替换为这一行代码

App current = (App) getItem(position);


你不需要在适配器内部保留 `List<App> objects;`,它在构造函数的 `super` 调用中传递(最后一个参数),并在内部为您保存。请查看 [ArrayAdapter 源代码][1] - 传递的 `ArrayList` 被保存为 `mObjects`,并在某些方法中进一步使用,例如 `getPosition`、`getCount` 和 `getItem`。
`ArrayAdapter` 已经实现了 `Filterable` 接口,因此有一个重写的 `getFilter` 方法,返回 `ArrayFilter` - 这个内部类在底部声明。当你调用 `getFilter().filter(` 时,`performFiltering` 会被调用(在一个单独的线程中),并且遍历本地数据的副本(第588行)。它使用 `values.get(i).toString().toLowerCase()` 来将数组中的对象与传递的 `String`(实际上是 `CharSequence`)进行比较。所以在你自定义的 `App` 类中重写 `toString` 方法,并在其中返回一些可搜索的值,例如:

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


这并不是最好的方法,因为 `toString` 可能会在许多机制中使用(它是 [Object][2] 的基本方法),而使用上面的 `toString` 实现时,具有相同名称但不同 `version` 或 `number` 的两个 `App` 将被视为相同的对象,这是不正确的... 也许更好的方式是 `return name+version+number;`,但是你的类中也有 `Drawable`。这就是为什么我之前建议(在编辑之前的回答中)创建一个扩展了 `BaseAdapter` 并实现自己的过滤逻辑的自定义类,或者至少使用 `ArrayAdapter`,但是重写 `getFilter` 方法并返回你自己的比较变量的 `Filter` 实现。这样就不需要重写这个类,保留它的原样。默认情况下,它返回一种类似内存地址的值,因此每个新的 `App` 实例都是唯一的,即使使用完全相同的变量创建。
此外,在[这个回答中][3],你可以找到一个很好的示例,展示了如何实现这个 `Filter`。
[1]: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/ArrayAdapter.java
[2]: http://developer.classpath.org/doc/java/lang/Object-source.html
[3]: https://stackoverflow.com/questions/10532194/filter-listview-with-arrayadapter
<details>
<summary>英文:</summary>
exchange this line
App current = objects.get(position);
to this
App current = (App) getItem(position);
you don&#39;t need to keep `List&lt;App&gt; objects;` inside your adapter, it is passed in constructors `super` call (last param) and kept under the hood for you. check out [ArrayAdapter source][1] - passed `ArrayList` is kept as `mObjects` and further used in some methods, e.g. `getPosition`, `getCount` and `getItem`
`ArrayAdapter` already `implements Filterable`, so there is an overriden method `getFilter`, which returns `ArrayFilter` - this inner class is declared on the bottom. when you call `getFilter().filter(` then `performFiltering` gets called (in a separated thread) and iterates through local copy of your data (line 588). It is using `values.get(i).toString().toLowerCase()` to compare objects from array with passed `String` (`CharSequence` in fact). so in your custom `App` class override `toString` method and return in there some searchable value, e.g.
@Override
public String toString(){
return name;
}
this is not best approach, because `toString` may be used in a lot of mechanisms (its base method of [Object][2]) and with above `toString` implementation two `App`s with same name, but different `version` or `number` are threated as same object, which isn&#39;t true... maybe better way would be to `return name+version+number;`, but still you have also `Drawable` in there. thats why I&#39;ve suggested (in this answer before edit) to make own class extends `BaseAdapter` and implement own filtering or at least use `ArrayAdapter`, but override `getFilter` method and return your own `Filter` implementation comparing variables instead of using `toString`. Then won&#39;t be needed to override this class, leave it as it is. By default it returns kind-of memory address, so it is unique for every new `App` instance, even when created with exacly same variables
also in [THIS][3] answer you can find nice example how to implement that `Filter`
[1]: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/ArrayAdapter.java
[2]: http://developer.classpath.org/doc/java/lang/Object-source.html
[3]: https://stackoverflow.com/questions/10532194/filter-listview-with-arrayadapter
</details>

huangapple
  • 本文由 发表于 2020年10月14日 19:48:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/64352594.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定