Фильтрация ListView с помощью пользовательского (объектного) адаптера

#android #listview #filter #adapter

#Android #listview #Фильтр #адаптер

Вопрос:

Я пытаюсь реализовать фильтрацию ListView, в котором используется пользовательский объектный адаптер, но я не могу найти никаких полезных примеров. Прилагаемый код очень упрощен, поэтому нет — имейте в виду, что я не могу использовать обычный ArrayAdapter. У меня есть EditText над ListView, и когда пользователь вводит текст в виджет EditText, я хотел бы отфильтровать ListView по тексту, написанному в EditText. Любые предложения будут высоко оценены!

Вот фрагмент из класса activity:

 public class management_objects extends Activity {

private static List<User> UserList;
private EfficientAdapter adapter = null;
private ListView objectListView = null;
private EditText SearchText = null;

private static class EfficientAdapter extends BaseAdapter implements Filterable{
    private LayoutInflater mInflater;   

    public EfficientAdapter(Context context) {
        mInflater = LayoutInflater.from(context);
    }

    public int getCount() {
        return UserList.size();
    }

    public Object getItem(int position) {
        return position;
    }

    public long getItemId(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder; 
        if (convertView == null) { 
            convertView = mInflater.inflate(R.layout.imagelayout_2lines, null);
            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.managementObjectText);
            holder.subtext = (TextView) convertView.findViewById(R.id.managementObjectSubText);
            holder.icon = (ImageView) convertView.findViewById(R.id.managementObjectIcon);
            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.text.setText(UserList.get(position).getFirstName());
        holder.subtext.setText(UserList.get(position).getLastName());
        holder.icon.setImageResource(R.drawable.user);

        return convertView;
    }

    static class ViewHolder { 
        TextView text;
        TextView subtext;
        ImageView icon;
    }

    @Override
    public Filter getFilter() {
        return null;
    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.adobjectlist);
    Bundle extras = getIntent().getExtras();

    SearchText = (EditText) findViewById(R.id.SearchBox);    
    SearchText.addTextChangedListener(filterTextWatcher);

    objectListView = (ListView) findViewById(R.id.ObjectList);
    objectListView.setOnItemClickListener(Item_Click);
    adapter = new EfficientAdapter(this);
    ComputerName = extras.getString("COMPUTER_NAME");

    //Get User list from webservice
    ShowUsers();
}
  

Вот класс User:

  public class User {
  private int UserId;
  private String FirstName;
  private String LastName;

    public int getUserId() {
        return UserId;
    }
    public void setUserId(int UserId) {
        this.UserId = UserId;
    }
    public String getFirstName() {
        return FirstName;
    }
    public void setFirstName(String FirstName) {
        this.FirstName = FirstName;
    }
    public String getLastName() {
        return LastName;
    }
    public void setLastName(String LastName) {
        this.LastName = LastName;
    }
}
  

Ответ №1:

Вам нужно сделать несколько вещей:

1) В своем действии зарегистрируйтесь для прослушивателя изменения текста в вашем EditText, который содержит значение, введенное пользователем:

mSearchValue.addTextChangedListener (searchTextWatcher);

2) Создайте свой searchTextWatcher и попросите его что-нибудь сделать:

 private TextWatcher searchTextWatcher = new TextWatcher() {
    @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // ignore
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // ignore
        }

        @Override
        public void afterTextChanged(Editable s) {
            Log.d(Constants.TAG, "*** Search value changed: "   s.toString());
            adapter.getFilter().filter(s.toString());
        }
    };
  

3) Переопределите getFilter() в вашем пользовательском адаптере и попросите его отфильтровать результаты и уведомить listview об изменении набора данных.

     @Override
    public Filter getFilter() {
        return new Filter() {
            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                Log.d(Constants.TAG, "**** PUBLISHING RESULTS for: "   constraint);
                myData = (List<MyDataType>) results.values;
                MyCustomAdapter.this.notifyDataSetChanged();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                Log.d(Constants.TAG, "**** PERFORM FILTERING for: "   constraint);
                List<MyDataType> filteredResults = getFilteredResults(constraint);

                FilterResults results = new FilterResults();
                results.values = filteredResults;

                return results;
            }
        };
    }
  

Комментарии:

1. Почему вы говорите «переопределить» в 3)? В BaseAdapter нет getFilter().

2. Хорошая уловка — не требуется. Мой пользовательский класс адаптера (фактически наследующий AmazingAdapter от виджета AmazingListView с открытым исходным кодом) реализует android.widget. Фильтруемый интерфейс, для которого требуется getFilter().

3. Этот урок мне помог. В ответе были некоторые неясные вещи, или, возможно, я не прочитал его с достаточной концентрацией.

4. @Dustin что это за метод getFilteredResults (constraint)?. я попробовал ваш ответ. но этот метод не поддерживает ne.

5. @anuruddhika, это просто ваш метод для выполнения любой необходимой вам фильтрации, который вернет результаты. Это не метод Android, который вы переопределяете.

Ответ №2:

Вот интересный пример

 public Filter getFilter() {
    return new Filter() {

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            final FilterResults oReturn = new FilterResults();
            final ArrayList<station> results = new ArrayList<station>();
            if (orig == null)
                orig = items;
            if (constraint != null) {
                if (orig != null amp;amp; orig.size() > 0) {
                    for (final station g : orig) {
                        if (g.getName().toLowerCase()
                                .contains(constraint.toString()))
                            results.add(g);
                    }
                }
                oReturn.values = results;
            }
            return oReturn;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint,
                FilterResults results) {
            items = (ArrayList<station>) results.values;
            notifyDataSetChanged();
        }
    };
}

public void notifyDataSetChanged() {
    super.notifyDataSetChanged();
    notifyChanged = true;
}
  

Комментарии:

1. отличный подход при использовании исходной переменной для сохранения исходного экземпляра. Это сработало хорошо, спасибо.

Ответ №3:

Для тех, кому не нужен Filterable интерфейс, есть гораздо более простое решение. Это также notifyDataSetChanged() корректно обрабатывает те случаи, когда другие решения терпят неудачу. Обратите внимание, что вам нужно добавить getArray() функцию к BaseAdapter , которая просто возвращает объект массива, который был передан конструктору.

 public abstract class BaseFilterAdapter<T> extends BaseAdapter<T> {

    private List<T> original;
    private String lastFilter;

    public BaseFilterAdapter(Context context, List<T> array) {
        super(context, new LinkedList<T>());
        original = array;
        filter("");
    }

    protected abstract Boolean predicate(T element, String filter);

    public void filter(String filter) {
        lastFilter = filter;
        super.getArray().clear();
        for (T element : original)
            if (predicate(element, filter))
                super.getArray().add(element);
        super.notifyDataSetChanged();
    }

    @Override
    public List<T> getArray() {
        return original;
    }

    @Override
    public void notifyDataSetChanged() {
        filter(lastFilter);
    }
}
  

Комментарии:

1. в моем классе BaseAdapter нет метода getArray для переопределения.

Ответ №4:

Добавьте переопределение toString в свой базовый класс. Например

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

Приведенный выше список выглядит как список строк. Таким образом, вы можете использовать:

 your_edit_text.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        YourActivity.this.YourAdapter.getFilter().filter(arg0);
    }

    @Override
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
    }

    @Override
    public void afterTextChanged(Editable arg0) {    
    }
});
  

Комментарии:

1. Это быстрый ответ для тех, кто не хочет переопределять getFilter функцию адаптера