Выбор и поиск одновременно через пользовательский список не работает - PullRequest
0 голосов
/ 06 июня 2018

Я новичок в Android, и я работаю над приложением со списком продуктов, и у меня возникли некоторые проблемы.

В настоящее время я создал собственный просмотр списка, который содержит изображение и поле имени.Я также создал пользовательский объект GroceryItem, который заполняет listViews.

Я хочу, чтобы пользователь мог выбирать GroceryItems из listView, а также осуществлять поиск по списку.Вот мой специальный адаптер для моего 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», он показывает мне «Виноград», но больше не выбирается.

Еще одна похожая проблема.Еще раз, начав с яблок, бананов, винограда и манго, если я выберу Виноград и начну поиск «b», я получу банан невыбранным.Отлично.Теперь, когда я выбираю Банан, он показывает его как выбранный.Но, как только я возвращаюсь, я возвращаюсь к полному списку предметов, и выбираются только Виноград.

Я написал функцию fixToggling () для итерации по каждому представлению и исправления цвета фона при необходимости.Я также провел некоторую отладку, чтобы узнать, что isSelected Boolean из каждого groceryItem правильно записывается, поэтому не проблема, что приложение не запоминает, какие из них должны быть выбраны или не выбраны.По некоторым причинам переключение просто выключено.

Кто-нибудь может помочь?Я просто хочу позволить пользователям одновременно использовать функции поиска и выбора элементов.

1 Ответ

0 голосов
/ 08 июня 2018

Попробуйте этот код адаптера:

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

Надеюсь, это поможет!

...