通过自定义 listView 同时选择和搜索不起作用
Selecting and searching simultaneously through custom listView not working
我是 Android 的新手,我正在开发一个杂货清单应用程序,但 运行 遇到了一些麻烦。
现在,我制作了一个包含图像和名称字段的自定义 listView。我还制作了一个自定义对象 GroceryItem 来填充 listViews。
我希望用户能够 select 来自 listView 的 GroceryItems,并且还可以搜索列表。这是我的 listView 的自定义适配器。
class CustomAdapter extends ArrayAdapter<GroceryItem> implements Filterable
{
private ArrayList<GroceryItem> mObjects;
private ArrayFilter mFilter;
private ArrayList<GroceryItem> mOriginalValues;
CustomAdapter(@NonNull Context context, int resource, @NonNull ArrayList<GroceryItem> inputValues) {
super(context, resource, inputValues);
mObjects = new ArrayList<>(inputValues);
}
public void setBackup(){
mOriginalValues = new ArrayList<GroceryItem>();
mOriginalValues.addAll(mObjects);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
final GroceryItem currentGroceryItem = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.custom_layout, parent, false);
}
// Lookup view for data population
ImageView groceryImage = (ImageView) convertView.findViewById(R.id.groceryImage);
TextView groceryNameText = (TextView) convertView.findViewById(R.id.groceryName);
LinearLayout overallItem = (LinearLayout) convertView.findViewById(R.id.linearLayout);
// Populate the data into the template view using data
groceryImage.setImageResource(currentGroceryItem.getImageID());
groceryNameText.setText(currentGroceryItem.getName());
// Set onClickListener for overallItem
overallItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
searchBar.clearFocus();
// Changes the selection status of the GroceryItem
currentGroceryItem.toggle();
// Changes the colour of the background accordingly (to show selection)
if(currentGroceryItem.isSelected()){
v.setBackgroundColor(0xFF83B5C7);
} else{
v.setBackgroundColor(0xFFFFFFFF);
}
}
});
// Return the completed view to render on screen
return convertView;
}
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////FOR SEARCH FUNCTIONALITY////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
public View getViewByPosition(int pos, ListView myListView) {
final int firstListItemPosition = myListView.getFirstVisiblePosition();
final int lastListItemPosition = firstListItemPosition + myListView.getChildCount() - 1;
if (pos < firstListItemPosition || pos > lastListItemPosition ) {
return getView(pos, null, myListView);
} else {
final int childIndex = pos - firstListItemPosition;
return myListView.getChildAt(childIndex);
}
}
public void fixToggling(){
runOnUiThread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < getCount(); ++i){
// Finds view at index i in listView, which is a global variable
View view = getViewByPosition(i, listView);
if(getItem(i).isSelected()){
view.setBackgroundColor(0XFF83B5C7);
} else{
view.setBackgroundColor(0xFFFFFFFF);
}
}
}
});
}
// The following function reverses the filter (sets everything to default)
public void reverseFilter(){
//Replaces mObjects with mOriginal Values
mObjects.clear();
mObjects.addAll(mOriginalValues);
//Loads mObjects (now filled with the original items) into the adapter
this.clear();
this.addAll(mObjects);
fixToggling();
}
// The following function applies a filter given a query
public void applyFilter(String query) {
if(query == null || query.length() == 0){
reverseFilter();
} else{
// Creates a new array filter
mFilter = new ArrayFilter();
// performs the filters, and publishes the result (i.e. writes the result into
// mObjects)
mFilter.publishResults(query, mFilter.performFiltering(query));
// Clears current content of the adapter
this.clear();
// Fills the adapter with the content of the filtered result
this.addAll(mObjects);
fixToggling();
}
}
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
final FilterResults results = new FilterResults();
if(mOriginalValues == null){
mOriginalValues = new ArrayList<>(mObjects);
}
// If there is no input query
if (prefix == null || prefix.length() == 0) {
// Make a copy of mOriginalValues into the list
final ArrayList<GroceryItem> list;
list = new ArrayList<>(mOriginalValues);
// Set the FilterResults value to the copy of mOriginalValues
results.values = list;
results.count = list.size();
// If there is an input query (at least one character in length)
} else {
// Converts the query to a lowercase String
final String prefixString = prefix.toString().toLowerCase();
// Makes a copy of mOriginalValues into the ArrayList "values"
final ArrayList<GroceryItem> values;
values = new ArrayList<>(mOriginalValues);
final int count = values.size();
// Makes a new empty ArrayList
final ArrayList<GroceryItem> newValues = new ArrayList<>();
// Iterates through the number of elements in mOriginalValues
for (int i = 0; i < count; i++) {
// Gets the GroceryItem element at position i from mOriginalValues' copy
final GroceryItem value = values.get(i);
// Extracts the name of the GroceryItem element into valueText
final String valueText = value.getName().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
}
else {
// Splits the one String into all its constituent words
final String[] words = valueText.split(" ");
// If any of the constituent words starts with the prefix, adds them
for (String word : words) {
if (word.startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
// Sets the FilterResult value to the newValues ArrayList. mOriginalValues is
// preserved.
results.values = newValues;
results.count = newValues.size();
// Changes mObjects from (potentially) the original items or the previously filtered
// results to the new filtered results. Needs to be loaded into the adapter still.
mObjects.clear();
mObjects.addAll(newValues);
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
notifyDataSetChanged();
}
}
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
}
这是我 运行 遇到的问题。我现在有四种杂货:苹果、香蕉、葡萄和芒果。如果我 select 葡萄,一切正常。它以蓝色背景显示 Grape,以白色背景显示所有其他项。当我开始在搜索栏中输入“gr”时,一切正常。我只看到葡萄,并且该项目显示为 selected(蓝色背景)。当我输入“grm”时,一切都消失了,什么都没有 selected。但是,当我退格一个字符并返回到“gr”时,它会显示 Grapes,但它不再是 selected。
另一个类似的问题。再次从 Apple、Banana、Grapes 和 Mango 开始,如果我 select Grapes 并搜索“b”,我会得到 Banana unselected。伟大的。现在,当我 select Banana 时,它显示为 selected。但是,一旦退格,我返回到完整的项目列表,只有葡萄 selected。
我已经编写了 fixToggling() 函数来遍历每个视图并根据需要修复背景颜色。我还进行了一些调试,以了解每个 groceryItem 的 isSelected 布尔值都已正确记录,因此应用程序不记得哪些应该 selected 或不 select 并不是问题编辑。出于某种原因,切换刚刚关闭。
有人能帮忙吗?我只想让用户同时使用搜索功能和项目 selection。
试试这个适配器代码:
class CustomAdapter2 extends ArrayAdapter<GroceryItem> implements Filterable
{
private ArrayList<GroceryItem> mObjects;
private ArrayList<GroceryItem> mOriginalValues;
private ArrayFilter mFilter;
private LayoutInflater mLayoutInflater;
CustomAdapter2(@NonNull Context context, int resource, @NonNull ArrayList<GroceryItem> inputValues) {
super(context, resource, inputValues);
mLayoutInflater = LayoutInflater.from(context);
mObjects = inputValues;
}
public void setBackup(){
mOriginalValues = new ArrayList<>();
mOriginalValues.addAll(mObjects);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
GroceryItem currentGroceryItem = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.custom_layout, parent, false);
}
// Lookup view for data population
ImageView groceryImage = (ImageView) convertView.findViewById(R.id.groceryImage);
TextView groceryNameText = (TextView) convertView.findViewById(R.id.groceryName);
LinearLayout overallItem = (LinearLayout) convertView.findViewById(R.id.linearLayout);
// Populate the data into the template view using data
groceryImage.setImageResource(currentGroceryItem.getImageID());
groceryNameText.setText(currentGroceryItem.getName());
if(currentGroceryItem.isSelected()){
overallItem.setBackgroundColor(0xFF83B5C7);
} else{
overallItem.setBackgroundColor(0xFFFFFFFF);
}
// Set onClickListener for overallItem
overallItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = (int)v.getTag();
GroceryItem currentGroceryItem = getItem(pos);
searchBar.clearFocus();
// Changes the selection status of the GroceryItem
currentGroceryItem.toggle();
// Changes the colour of the background accordingly (to show selection)
if(currentGroceryItem.isSelected()){
v.setBackgroundColor(0xFF83B5C7);
} else{
v.setBackgroundColor(0xFFFFFFFF);
}
}
});
// Return the completed view to render on screen
convertView.setTag(position);
return convertView;
}
/////////////////////////////////FOR SEARCH FUNCTIONALITY///////////////////////////////////
@NonNull
@Override
public Filter getFilter() {
if(mFilter == null){
// Make a backup copy of list
setBackup();
mFilter = new ArrayFilter();
}
return mFilter;
}
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
ArrayList<GroceryItem> list = new ArrayList<>();
// If there is no input query
if (prefix == null || prefix.length() == 0) {
// Set the FilterResults value to the copy of mOriginalValues
list = new ArrayList<>(mOriginalValues);
// If there is an input query (at least one character in length)
} else {
// Converts the query to a lowercase String
String prefixString = prefix.toString().toLowerCase();
// Iterates through the number of elements in mOriginalValues
for (int i = 0; i < mOriginalValues.size(); i++) {
// Gets the GroceryItem element at position i from mOriginalValues' copy
GroceryItem value = mOriginalValues.get(i);
// Extracts the name of the GroceryItem element into valueText
String valueText = value.getName().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
list.add(value);
}
else {
// Splits the one String into all its constituent words
String[] words = valueText.split(" ");
// If any of the constituent words starts with the prefix, adds them
for (String word : words) {
if (word.startsWith(prefixString)) {
list.add(value);
break;
}
}
}
}
}
// Sets the FilterResult value to list ArrayList.
results.values = list;
results.count = list.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
clear();
addAll((ArrayList<GroceryItem>)results.values);
notifyDataSetChanged();
}
}
////////////////////////////////////////////////////////////////////////////////////////////
}
希望对您有所帮助!
我是 Android 的新手,我正在开发一个杂货清单应用程序,但 运行 遇到了一些麻烦。
现在,我制作了一个包含图像和名称字段的自定义 listView。我还制作了一个自定义对象 GroceryItem 来填充 listViews。
我希望用户能够 select 来自 listView 的 GroceryItems,并且还可以搜索列表。这是我的 listView 的自定义适配器。
class CustomAdapter extends ArrayAdapter<GroceryItem> implements Filterable
{
private ArrayList<GroceryItem> mObjects;
private ArrayFilter mFilter;
private ArrayList<GroceryItem> mOriginalValues;
CustomAdapter(@NonNull Context context, int resource, @NonNull ArrayList<GroceryItem> inputValues) {
super(context, resource, inputValues);
mObjects = new ArrayList<>(inputValues);
}
public void setBackup(){
mOriginalValues = new ArrayList<GroceryItem>();
mOriginalValues.addAll(mObjects);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
final GroceryItem currentGroceryItem = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.custom_layout, parent, false);
}
// Lookup view for data population
ImageView groceryImage = (ImageView) convertView.findViewById(R.id.groceryImage);
TextView groceryNameText = (TextView) convertView.findViewById(R.id.groceryName);
LinearLayout overallItem = (LinearLayout) convertView.findViewById(R.id.linearLayout);
// Populate the data into the template view using data
groceryImage.setImageResource(currentGroceryItem.getImageID());
groceryNameText.setText(currentGroceryItem.getName());
// Set onClickListener for overallItem
overallItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
searchBar.clearFocus();
// Changes the selection status of the GroceryItem
currentGroceryItem.toggle();
// Changes the colour of the background accordingly (to show selection)
if(currentGroceryItem.isSelected()){
v.setBackgroundColor(0xFF83B5C7);
} else{
v.setBackgroundColor(0xFFFFFFFF);
}
}
});
// Return the completed view to render on screen
return convertView;
}
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////FOR SEARCH FUNCTIONALITY////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
public View getViewByPosition(int pos, ListView myListView) {
final int firstListItemPosition = myListView.getFirstVisiblePosition();
final int lastListItemPosition = firstListItemPosition + myListView.getChildCount() - 1;
if (pos < firstListItemPosition || pos > lastListItemPosition ) {
return getView(pos, null, myListView);
} else {
final int childIndex = pos - firstListItemPosition;
return myListView.getChildAt(childIndex);
}
}
public void fixToggling(){
runOnUiThread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < getCount(); ++i){
// Finds view at index i in listView, which is a global variable
View view = getViewByPosition(i, listView);
if(getItem(i).isSelected()){
view.setBackgroundColor(0XFF83B5C7);
} else{
view.setBackgroundColor(0xFFFFFFFF);
}
}
}
});
}
// The following function reverses the filter (sets everything to default)
public void reverseFilter(){
//Replaces mObjects with mOriginal Values
mObjects.clear();
mObjects.addAll(mOriginalValues);
//Loads mObjects (now filled with the original items) into the adapter
this.clear();
this.addAll(mObjects);
fixToggling();
}
// The following function applies a filter given a query
public void applyFilter(String query) {
if(query == null || query.length() == 0){
reverseFilter();
} else{
// Creates a new array filter
mFilter = new ArrayFilter();
// performs the filters, and publishes the result (i.e. writes the result into
// mObjects)
mFilter.publishResults(query, mFilter.performFiltering(query));
// Clears current content of the adapter
this.clear();
// Fills the adapter with the content of the filtered result
this.addAll(mObjects);
fixToggling();
}
}
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
final FilterResults results = new FilterResults();
if(mOriginalValues == null){
mOriginalValues = new ArrayList<>(mObjects);
}
// If there is no input query
if (prefix == null || prefix.length() == 0) {
// Make a copy of mOriginalValues into the list
final ArrayList<GroceryItem> list;
list = new ArrayList<>(mOriginalValues);
// Set the FilterResults value to the copy of mOriginalValues
results.values = list;
results.count = list.size();
// If there is an input query (at least one character in length)
} else {
// Converts the query to a lowercase String
final String prefixString = prefix.toString().toLowerCase();
// Makes a copy of mOriginalValues into the ArrayList "values"
final ArrayList<GroceryItem> values;
values = new ArrayList<>(mOriginalValues);
final int count = values.size();
// Makes a new empty ArrayList
final ArrayList<GroceryItem> newValues = new ArrayList<>();
// Iterates through the number of elements in mOriginalValues
for (int i = 0; i < count; i++) {
// Gets the GroceryItem element at position i from mOriginalValues' copy
final GroceryItem value = values.get(i);
// Extracts the name of the GroceryItem element into valueText
final String valueText = value.getName().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
}
else {
// Splits the one String into all its constituent words
final String[] words = valueText.split(" ");
// If any of the constituent words starts with the prefix, adds them
for (String word : words) {
if (word.startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
// Sets the FilterResult value to the newValues ArrayList. mOriginalValues is
// preserved.
results.values = newValues;
results.count = newValues.size();
// Changes mObjects from (potentially) the original items or the previously filtered
// results to the new filtered results. Needs to be loaded into the adapter still.
mObjects.clear();
mObjects.addAll(newValues);
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
notifyDataSetChanged();
}
}
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
}
这是我 运行 遇到的问题。我现在有四种杂货:苹果、香蕉、葡萄和芒果。如果我 select 葡萄,一切正常。它以蓝色背景显示 Grape,以白色背景显示所有其他项。当我开始在搜索栏中输入“gr”时,一切正常。我只看到葡萄,并且该项目显示为 selected(蓝色背景)。当我输入“grm”时,一切都消失了,什么都没有 selected。但是,当我退格一个字符并返回到“gr”时,它会显示 Grapes,但它不再是 selected。
另一个类似的问题。再次从 Apple、Banana、Grapes 和 Mango 开始,如果我 select Grapes 并搜索“b”,我会得到 Banana unselected。伟大的。现在,当我 select Banana 时,它显示为 selected。但是,一旦退格,我返回到完整的项目列表,只有葡萄 selected。
我已经编写了 fixToggling() 函数来遍历每个视图并根据需要修复背景颜色。我还进行了一些调试,以了解每个 groceryItem 的 isSelected 布尔值都已正确记录,因此应用程序不记得哪些应该 selected 或不 select 并不是问题编辑。出于某种原因,切换刚刚关闭。
有人能帮忙吗?我只想让用户同时使用搜索功能和项目 selection。
试试这个适配器代码:
class CustomAdapter2 extends ArrayAdapter<GroceryItem> implements Filterable
{
private ArrayList<GroceryItem> mObjects;
private ArrayList<GroceryItem> mOriginalValues;
private ArrayFilter mFilter;
private LayoutInflater mLayoutInflater;
CustomAdapter2(@NonNull Context context, int resource, @NonNull ArrayList<GroceryItem> inputValues) {
super(context, resource, inputValues);
mLayoutInflater = LayoutInflater.from(context);
mObjects = inputValues;
}
public void setBackup(){
mOriginalValues = new ArrayList<>();
mOriginalValues.addAll(mObjects);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
GroceryItem currentGroceryItem = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.custom_layout, parent, false);
}
// Lookup view for data population
ImageView groceryImage = (ImageView) convertView.findViewById(R.id.groceryImage);
TextView groceryNameText = (TextView) convertView.findViewById(R.id.groceryName);
LinearLayout overallItem = (LinearLayout) convertView.findViewById(R.id.linearLayout);
// Populate the data into the template view using data
groceryImage.setImageResource(currentGroceryItem.getImageID());
groceryNameText.setText(currentGroceryItem.getName());
if(currentGroceryItem.isSelected()){
overallItem.setBackgroundColor(0xFF83B5C7);
} else{
overallItem.setBackgroundColor(0xFFFFFFFF);
}
// Set onClickListener for overallItem
overallItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = (int)v.getTag();
GroceryItem currentGroceryItem = getItem(pos);
searchBar.clearFocus();
// Changes the selection status of the GroceryItem
currentGroceryItem.toggle();
// Changes the colour of the background accordingly (to show selection)
if(currentGroceryItem.isSelected()){
v.setBackgroundColor(0xFF83B5C7);
} else{
v.setBackgroundColor(0xFFFFFFFF);
}
}
});
// Return the completed view to render on screen
convertView.setTag(position);
return convertView;
}
/////////////////////////////////FOR SEARCH FUNCTIONALITY///////////////////////////////////
@NonNull
@Override
public Filter getFilter() {
if(mFilter == null){
// Make a backup copy of list
setBackup();
mFilter = new ArrayFilter();
}
return mFilter;
}
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
ArrayList<GroceryItem> list = new ArrayList<>();
// If there is no input query
if (prefix == null || prefix.length() == 0) {
// Set the FilterResults value to the copy of mOriginalValues
list = new ArrayList<>(mOriginalValues);
// If there is an input query (at least one character in length)
} else {
// Converts the query to a lowercase String
String prefixString = prefix.toString().toLowerCase();
// Iterates through the number of elements in mOriginalValues
for (int i = 0; i < mOriginalValues.size(); i++) {
// Gets the GroceryItem element at position i from mOriginalValues' copy
GroceryItem value = mOriginalValues.get(i);
// Extracts the name of the GroceryItem element into valueText
String valueText = value.getName().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
list.add(value);
}
else {
// Splits the one String into all its constituent words
String[] words = valueText.split(" ");
// If any of the constituent words starts with the prefix, adds them
for (String word : words) {
if (word.startsWith(prefixString)) {
list.add(value);
break;
}
}
}
}
}
// Sets the FilterResult value to list ArrayList.
results.values = list;
results.count = list.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
clear();
addAll((ArrayList<GroceryItem>)results.values);
notifyDataSetChanged();
}
}
////////////////////////////////////////////////////////////////////////////////////////////
}
希望对您有所帮助!