通过自定义listView同时选择和搜索不起作用

Posted

技术标签:

【中文标题】通过自定义listView同时选择和搜索不起作用【英文标题】:Selecting and searching simultaneously through custom listView not working 【发布时间】:2018-11-15 13:17:26 【问题描述】:

我是 android 新手,我正在开发一个购物清单应用程序,但遇到了一些麻烦。

现在,我制作了一个包含图像和名称字段的自定义列表视图。我还制作了一个填充 listViews 的自定义对象 GroceryItem。

我希望用户能够从 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();
    


////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////


这是我遇到的问题。我现在有四种杂货:苹果、香蕉、葡萄和芒果。如果我选择葡萄,一切正常。它以蓝色背景显示葡萄,以白色背景显示所有其他项目。当我开始在搜索栏中输入“gr”时,一切正常。我只看到葡萄,并且该项目显示为选中(蓝色背景)。当我输入“grm”时,一切都消失了,没有任何选择。但是,当我退格一个字符并返回“gr”时,它向我显示葡萄,但它不再被选中。

另一个类似的问题。再次从 Apple、Banana、Grapes 和 Mango 开始,如果我选择 Grapes 并搜索“b”,我会发现 Banana 未选中。伟大的。现在,当我选择香蕉时,它显示为已选中。但是,一旦我退格,我就会返回到完整的项目列表,并且只选择了葡萄。

我编写了 fixToggling() 函数来遍历每个视图并根据需要修复背景颜色。我还进行了一些调试,以了解每个groceryItem 中的isSelected 布尔值被正确记录,因此应用程序不记得哪些应该被选中或不应该被选中不是问题。出于某种原因,切换刚刚关闭。

有人可以帮忙吗?我只想让用户同时使用搜索功能和项目选择。

【问题讨论】:

【参考方案1】:

试试这个适配器代码:

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();
    

////////////////////////////////////////////////////////////////////////////////////////////

希望有帮助!

【讨论】:

这正是我想要的!!非常感谢你的帮助!我看到了您的代码,并看到了您在 OnClickListener 和 getView 中所做的修复。你能解释一下这些变化,这样我就不会再重复我的错误了吗?再次感谢您! 1.您的主要问题是您仅在 onClick() 中更改了视图背景颜色。所以对于过滤,视图重绘但不会相应地改变颜色。 2. 我不使用最终变量,而是使用 setTag() 方法通过可点击视图传递位置。理论上,最终变量应该可以工作,但我很久以前就看到过一个使用最终变量的问题。改成 setTag() 方法后解决。因此,我继续将它用于 ListView。这种改变可能是不必要的。我关于 ListView 的博客:programandroidlistview.blogspot.com

以上是关于通过自定义listView同时选择和搜索不起作用的主要内容,如果未能解决你的问题,请参考以下文章

片段中 ListView 的自定义适配器不起作用

Android ListView ClickEvent 不起作用

ListView 和自定义适配器在 Kotlin 中不起作用

UWP ListView 滚动和缩放不起作用

Android Studio itemsAdapter 和自定义 ListView 不起作用

Listview自定义适配器无法选择项目