android - 안드로이드 onquerytextchange




SearchView로 RecyclerView를 필터링하는 방법 (6)

어댑터에 하나의 orignal과 하나의 temp를 두 개의 목록으로 만들고 Filterable을 구현하기 만하면 됩니다.

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                final FilterResults oReturn = new FilterResults();
                final ArrayList<T> results = new ArrayList<>();
                if (origList == null)
                    origList = new ArrayList<>(itemList);
                if (constraint != null && constraint.length() > 0) {
                    if (origList != null && origList.size() > 0) {
                        for (final T cd : origList) {
                            if (cd.getAttributeToSearch().toLowerCase()
                                    .contains(constraint.toString().toLowerCase()))
                                results.add(cd);
                        }
                    }
                    oReturn.values = results;
                    oReturn.count = results.size();//newly Aded by ZA
                } else {
                    oReturn.values = origList;
                    oReturn.count = origList.size();//newly added by ZA
                }
                return oReturn;
            }

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(final CharSequence constraint,
                                          FilterResults results) {
                itemList = new ArrayList<>((ArrayList<T>) results.values);
                // FIXME: 8/16/2017 implement Comparable with sort below
                ///Collections.sort(itemList);
                notifyDataSetChanged();
            }
        };
    }

어디에

public GenericBaseAdapter(Context mContext, List<T> itemList) {
        this.mContext = mContext;
        this.itemList = itemList;
        this.origList = itemList;
    }

지원 라이브러리에서 SearchView 를 구현하려고합니다. 사용자가 SearchView 를 사용하여 RecyclerView 에서 영화 List 을 필터링하고 싶습니다.

지금까지 몇 가지 자습서를 수행했으며 SearchViewActionBar 추가했지만 여기서 어디로 가야할지 확실하지 않습니다. 몇 가지 예를 보았지만 입력을 시작할 때 결과가 표시되지 않습니다.

이것은 내 주요 활동입니다.

public class MainActivity extends ActionBarActivity {

    RecyclerView mRecyclerView;
    RecyclerView.LayoutManager mLayoutManager;
    RecyclerView.Adapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_view);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        mAdapter = new CardAdapter() {
            @Override
            public Filter getFilter() {
                return null;
            }
        };
        mRecyclerView.setAdapter(mAdapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

그리고 이것은 내 Adapter .

public abstract class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> implements Filterable {

    List<Movie> mItems;

    public CardAdapter() {
        super();
        mItems = new ArrayList<Movie>();
        Movie movie = new Movie();
        movie.setName("Spiderman");
        movie.setRating("92");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Doom 3");
        movie.setRating("91");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Transformers");
        movie.setRating("88");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Transformers 2");
        movie.setRating("87");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Transformers 3");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Noah");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Ironman");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Ironman 2");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Ironman 3");
        movie.setRating("86");
        mItems.add(movie);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_view_card_item, viewGroup, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        Movie movie = mItems.get(i);
        viewHolder.tvMovie.setText(movie.getName());
        viewHolder.tvMovieRating.setText(movie.getRating());
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder{

        public TextView tvMovie;
        public TextView tvMovieRating;

        public ViewHolder(View itemView) {
            super(itemView);
            tvMovie = (TextView)itemView.findViewById(R.id.movieName);
            tvMovieRating = (TextView)itemView.findViewById(R.id.movieRating);
        }
    }
}

소개

정확히 당신이 어떤 문제를 겪고 있는지 당신의 질문에서 명확하지 않기 때문에, 나는이 기능을 구현하는 방법에 대한이 빠른 연습을 작성했습니다. 여전히 궁금한 점이 있으면 언제든지 문의하십시오.

GitHub 리포지토리 에서 내가 말하는 모든 것에 대한 실제 사례가 있습니다 .
예제 프로젝트에 대한 자세한 내용을 보려면 프로젝트 홈페이지를 방문하십시오.

어쨌든 결과는 다음과 같습니다.

데모 앱으로 먼저 플레이하고 싶다면 Play 스토어에서 설치할 수 있습니다.

어쨌든 시작할 수 있습니다.

SearchView 설정

res/menu 폴더에 main_menu.xml 이라는 새 파일을 작성하십시오. 항목을 추가하고 actionViewClassandroid.support.v7.widget.SearchView 설정하십시오. 지원 라이브러리를 사용하고 있으므로 지원 라이브러리의 네임 스페이스를 사용하여 actionViewClass 속성을 설정 actionViewClass 합니다. xml 파일은 다음과 같아야합니다.

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/action_search"
          android:title="@string/action_search"
          app:actionViewClass="android.support.v7.widget.SearchView"
          app:showAsAction="always"/>

</menu>

Fragment 또는 Activity 에서이 메뉴 xml을 평소와 같이 팽창 OnQueryTextListener 합니다 OnQueryTextListener 가 들어있는 MenuItemOnQueryTextListener SearchView 입력 한 텍스트의 변경 사항을 수신하는 데 사용할 OnQueryTextListener 를 구현할 수 있습니다.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);

    final MenuItem searchItem = menu.findItem(R.id.action_search);
    final SearchView searchView = (SearchView) searchItem.getActionView();
    searchView.setOnQueryTextListener(this);

    return true;
}

@Override
public boolean onQueryTextChange(String query) {
    // Here is where we are going to implement the filter logic
    return false;
}

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

이제 SearchView 사용할 준비가되었습니다. Adapter 구현이 완료되면 나중에 onQueryTextChange() 에서 필터 로직을 구현합니다.

Adapter 설정

가장 먼저 이것은이 예제에 사용할 모델 클래스입니다.

public class ExampleModel {

    private final long mId;
    private final String mText;

    public ExampleModel(long id, String text) {
        mId = id;
        mText = text;
    }

    public long getId() {
        return mId;
    }

    public String getText() {
        return mText;
    }
}

RecyclerView 텍스트를 표시하는 것은 기본 모델 일뿐입니다. 이것은 텍스트를 표시하는 데 사용할 레이아웃입니다.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="model"
            type="com.github.wrdlbrnft.searchablerecyclerviewdemo.ui.models.ExampleModel"/>

    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackground"
        android:clickable="true">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="@{model.text}"/>

    </FrameLayout>

</layout>

보시다시피 데이터 바인딩을 사용합니다. 데이터 바인딩 작업을 해 본 적이 없다면 실망하지 마십시오! 매우 간단하고 강력하지만이 답변의 범위에서 어떻게 작동하는지 설명 할 수 없습니다.

ExampleModel 클래스의 ViewHolder 입니다.

public class ExampleViewHolder extends RecyclerView.ViewHolder {

    private final ItemExampleBinding mBinding;

    public ExampleViewHolder(ItemExampleBinding binding) {
        super(binding.getRoot());
        mBinding = binding;
    }

    public void bind(ExampleModel item) {
        mBinding.setModel(item);
    }
}

또 특별한 것은 없습니다. 위의 레이아웃 xml에서 정의한 것처럼 데이터 바인딩을 사용하여 모델 클래스를이 레이아웃에 바인딩합니다.

이제 우리는 마침내 어댑터 작성이라는 정말 흥미로운 부분에 도달 할 수 있습니다. 나는 Adapter 의 기본 구현을 건너 뛰고 대신이 답변과 관련된 부분에 집중할 것입니다.

그러나 먼저 우리가 이야기해야 할 것이 있습니다 : SortedList 클래스.

정렬 된 목록

SortedListRecyclerView 라이브러리의 일부인 완전히 놀라운 도구입니다. 데이터 세트에 대한 변경 사항을 Adapter 에 알리고 매우 효율적인 방식으로 처리합니다. 당신이해야 할 유일한 것은 요소의 순서를 지정하는 것입니다. Comparator 처럼 SortedList 두 요소를 비교하는 compare() 메소드를 구현하여이를 수행해야합니다. 그러나 List 를 정렬하는 대신 RecyclerView 에서 항목을 정렬하는 데 사용됩니다!

SortedList 는 구현해야하는 Callback 클래스를 통해 Adapter 와 상호 작용합니다.

private final SortedList.Callback<ExampleModel> mCallback = new SortedList.Callback<ExampleModel>() {

    @Override
    public void onInserted(int position, int count) {
         mAdapter.notifyItemRangeInserted(position, count);
    }

    @Override
    public void onRemoved(int position, int count) {
        mAdapter.notifyItemRangeRemoved(position, count);
    }

    @Override
    public void onMoved(int fromPosition, int toPosition) {
        mAdapter.notifyItemMoved(fromPosition, toPosition);
    }

    @Override
    public void onChanged(int position, int count) {
        mAdapter.notifyItemRangeChanged(position, count);
    }

    @Override
    public int compare(ExampleModel a, ExampleModel b) {
        return mComparator.compare(a, b);
    }

    @Override
    public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
        return oldItem.equals(newItem);
    }

    @Override
    public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
        return item1.getId() == item2.getId();
    }
}

onMoved , onInserted 등과 같은 콜백 상단의 메소드에서는 Adapter 의 동등한 알림 메소드를 호출해야합니다. 맨 아래에있는 세 가지 메소드 인 areContentsTheSameareItemsTheSame 은 표시하려는 객체의 종류와 화면에 표시 할 객체의 순서에 따라 구현해야합니다.

이 방법들을 하나씩 살펴 보자.

@Override
public int compare(ExampleModel a, ExampleModel b) {
    return mComparator.compare(a, b);
}

이것은 앞에서 이야기 한 compare() 메소드입니다. 이 예에서는 두 모델을 비교하는 Comparator 호출을 전달합니다. 화면에 항목을 알파벳 순서로 표시하려면. 이 비교기는 다음과 같습니다.

private static final Comparator<ExampleModel> ALPHABETICAL_COMPARATOR = new Comparator<ExampleModel>() {
    @Override
    public int compare(ExampleModel a, ExampleModel b) {
        return a.getText().compareTo(b.getText());
    }
};

이제 다음 방법을 살펴 보겠습니다.

@Override
public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
    return oldItem.equals(newItem);
}

이 방법의 목적은 모델의 내용이 변경되었는지 확인하는 것입니다. SortedList 는이를 사용하여 변경 이벤트를 호출해야하는지 여부, 즉 RecyclerView 가 이전 버전과 새 버전을 교차 페이드해야하는지 여부를 결정합니다. 모델 클래스에 올바른 equals()hashCode() 구현이있는 경우 일반적으로 위와 같이 구현할 수 있습니다. ExampleModel 클래스에 equals()hashCode() 구현을 추가하면 다음과 같이 보일 것입니다.

public class ExampleModel implements SortedListAdapter.ViewModel {

    private final long mId;
    private final String mText;

    public ExampleModel(long id, String text) {
        mId = id;
        mText = text;
    }

    public long getId() {
        return mId;
    }

    public String getText() {
        return mText;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ExampleModel model = (ExampleModel) o;

        if (mId != model.mId) return false;
        return mText != null ? mText.equals(model.mText) : model.mText == null;

    }

    @Override
    public int hashCode() {
        int result = (int) (mId ^ (mId >>> 32));
        result = 31 * result + (mText != null ? mText.hashCode() : 0);
        return result;
    }
}

간단한 참고 사항 : Android Studio, IntelliJ 및 Eclipse와 같은 대부분의 IDE에는 버튼을 누르면 equals()hashCode() 구현을 생성하는 기능이 있습니다! 따라서 직접 구현할 필요는 없습니다. 인터넷에서 IDE에서 어떻게 작동하는지 찾아보십시오!

이제 마지막 방법을 살펴 보자.

@Override
public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
    return item1.getId() == item2.getId();
}

SortedList 는이 방법을 사용하여 두 항목이 같은 것을 나타내는 지 확인합니다. 가장 간단한 용어 ( SortedList 작동 방식을 설명하지 않고)는 객체가 이미 List 포함되어 있는지, 애니메이션을 추가, 이동 또는 변경해야하는지 여부를 결정하는 데 사용됩니다. 모델에 ID가있는 경우 일반적으로이 방법에서 ID 만 비교합니다. 그들이 그것을 확인하는 다른 방법을 알아낼 필요가 없다면, 그러나 당신은 이것을 구현하는 것은 특정 앱에 달려 있습니다. 일반적으로 모든 모델에 ID를 제공하는 가장 간단한 옵션입니다. 예를 들어 데이터베이스에서 데이터를 쿼리하는 경우 기본 키 필드가 될 수 있습니다.

SortedList.Callback 올바르게 구현되면 SortedList.Callback 의 인스턴스를 만들 수 있습니다.

final SortedList<ExampleModel> list = new SortedList<>(ExampleModel.class, mCallback);

SortedList 생성자의 첫 번째 매개 변수로 모델 클래스를 전달해야합니다. 다른 매개 변수는 위에서 정의한 SortedList.Callback 입니다.

이제 비즈니스로 넘어 갑시다. SortedList Adapter 를 구현하면 다음과 같이 보일 것입니다.

public class ExampleAdapter extends RecyclerView.Adapter<ExampleViewHolder> {

    private final SortedList<ExampleModel> mSortedList = new SortedList<>(ExampleModel.class, new SortedList.Callback<ExampleModel>() {
        @Override
        public int compare(ExampleModel a, ExampleModel b) {
            return mComparator.compare(a, b);
        }

        @Override
        public void onInserted(int position, int count) {
            notifyItemRangeInserted(position, count);
        }

        @Override
        public void onRemoved(int position, int count) {
            notifyItemRangeRemoved(position, count);
        }

        @Override
        public void onMoved(int fromPosition, int toPosition) {
            notifyItemMoved(fromPosition, toPosition);
        }

        @Override
        public void onChanged(int position, int count) {
            notifyItemRangeChanged(position, count);
        }

        @Override
        public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
            return oldItem.equals(newItem);
        }

        @Override
        public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
            return item1.getId() == item2.getId();
        }
    });

    private final LayoutInflater mInflater;
    private final Comparator<ExampleModel> mComparator;

    public ExampleAdapter(Context context, Comparator<ExampleModel> comparator) {
        mInflater = LayoutInflater.from(context);
        mComparator = comparator;
    }

    @Override
    public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final ItemExampleBinding binding = ItemExampleBinding.inflate(inflater, parent, false);
        return new ExampleViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ExampleViewHolder holder, int position) {
        final ExampleModel model = mSortedList.get(position);
        holder.bind(model);
    }

    @Override
    public int getItemCount() {
        return mSortedList.size();
    }
}

항목을 정렬하는 데 사용되는 Comparator 는 생성자를 통해 전달되므로 항목이 다른 순서로 표시되어 있어도 동일한 Adapter 사용할 수 있습니다.

이제 거의 다 끝났습니다! 그러나 먼저 Adapter 항목을 추가하거나 제거 할 수있는 방법이 필요합니다. 이를 위해 SortedList 항목을 추가하고 제거 할 수있는 메소드를 Adapter 추가 할 수 있습니다.

public void add(ExampleModel model) {
    mSortedList.add(model);
}

public void remove(ExampleModel model) {
    mSortedList.remove(model);
}

public void add(List<ExampleModel> models) {
    mSortedList.addAll(models);
}

public void remove(List<ExampleModel> models) {
    mSortedList.beginBatchedUpdates();
    for (ExampleModel model : models) {
        mSortedList.remove(model);
    }
    mSortedList.endBatchedUpdates();
}

SortedList 이미 SortedList 을 통해이를 수행하므로 notify 메소드를 호출 할 필요가 없습니다! 그 외에도이 메소드의 구현은 모델 List 을 제거하는 remove 메소드를 제외하고는 매우 간단합니다. SortedList 에는 단일 객체를 제거 할 수있는 remove 메소드가 하나만 있으므로 목록을 반복하고 모델을 하나씩 제거해야합니다. 처음에 beginBatchedUpdates() 를 호출하면 SortedList 에 대한 모든 변경 사항이 함께 배치되어 성능이 향상됩니다. endBatchedUpdates() 호출하면 모든 변경 사항에 대해 RecyclerView 에 한 번에 알립니다.

또한 이해해야 할 것은 SortedList 객체를 추가하고 이미 SortedList 있으면 다시 추가되지 않는다는 것입니다. 대신 SortedListareContentsTheSame() 메서드를 사용하여 객체가 변경되었는지 확인하고 RecyclerView 항목이 있는지 여부를 확인합니다.

어쨌든, 내가 보통 선호하는 것은 RecyclerView 모든 항목을 한 번에 바꿀 수있는 방법입니다. List 없는 것을 모두 제거하고 SortedList 에서 누락 된 모든 항목을 추가하십시오.

public void replaceAll(List<ExampleModel> models) {
    mSortedList.beginBatchedUpdates();
    for (int i = mSortedList.size() - 1; i >= 0; i--) {
        final ExampleModel model = mSortedList.get(i);
        if (!models.contains(model)) {
            mSortedList.remove(model);
        }
    }
    mSortedList.addAll(models);
    mSortedList.endBatchedUpdates();
}

이 방법은 다시 모든 업데이트를 일괄 처리하여 성능을 향상시킵니다. 첫 번째 루프는 시작시 항목을 제거하면 해당 항목 이후에 나오는 모든 항목의 색인이 엉망이되어 데이터 불일치와 같은 문제가 발생할 수 있으므로 반대입니다. 그런 다음 addAll() 을 사용하여 ListSortedList 에 추가하여 SortedList 아직없는 모든 항목을 추가하고 위에서 설명한 것처럼 SortedList 있지만 변경 한 모든 항목을 업데이트합니다.

그리고 Adapter 가 완성되었습니다. 모든 것은 다음과 같이 보일 것입니다 :

public class ExampleAdapter extends RecyclerView.Adapter<ExampleViewHolder> {

    private final SortedList<ExampleModel> mSortedList = new SortedList<>(ExampleModel.class, new SortedList.Callback<ExampleModel>() {
        @Override
        public int compare(ExampleModel a, ExampleModel b) {
            return mComparator.compare(a, b);
        }

        @Override
        public void onInserted(int position, int count) {
            notifyItemRangeInserted(position, count);
        }

        @Override
        public void onRemoved(int position, int count) {
            notifyItemRangeRemoved(position, count);
        }

        @Override
        public void onMoved(int fromPosition, int toPosition) {
            notifyItemMoved(fromPosition, toPosition);
        }

        @Override
        public void onChanged(int position, int count) {
            notifyItemRangeChanged(position, count);
        }

        @Override
        public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
            return oldItem.equals(newItem);
        }

        @Override
        public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
            return item1 == item2;
        }
    });

    private final Comparator<ExampleModel> mComparator;
    private final LayoutInflater mInflater;

    public ExampleAdapter(Context context, Comparator<ExampleModel> comparator) {
        mInflater = LayoutInflater.from(context);
        mComparator = comparator;
    }

    @Override
    public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final ItemExampleBinding binding = ItemExampleBinding.inflate(mInflater, parent, false);
        return new ExampleViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ExampleViewHolder holder, int position) {
        final ExampleModel model = mSortedList.get(position);
        holder.bind(model);
    }

    public void add(ExampleModel model) {
        mSortedList.add(model);
    }

    public void remove(ExampleModel model) {
        mSortedList.remove(model);
    }

    public void add(List<ExampleModel> models) {
        mSortedList.addAll(models);
    }

    public void remove(List<ExampleModel> models) {
        mSortedList.beginBatchedUpdates();
        for (ExampleModel model : models) {
            mSortedList.remove(model);
        }
        mSortedList.endBatchedUpdates();
    }

    public void replaceAll(List<ExampleModel> models) {
        mSortedList.beginBatchedUpdates();
        for (int i = mSortedList.size() - 1; i >= 0; i--) {
            final ExampleModel model = mSortedList.get(i);
            if (!models.contains(model)) {
                mSortedList.remove(model);
            }
        }
        mSortedList.addAll(models);
        mSortedList.endBatchedUpdates();
    }

    @Override
    public int getItemCount() {
        return mSortedList.size();
    }
}

이제 누락 된 것은 필터링을 구현하는 것입니다!

필터 로직 구현

필터 로직을 구현하려면 먼저 가능한 모든 모델 List 을 정의해야합니다. 이 예제에서는 영화 배열에서 ExampleModel 인스턴스 List 을 만듭니다.

private static final String[] MOVIES = new String[]{
        ...
};

private static final Comparator<ExampleModel> ALPHABETICAL_COMPARATOR = new Comparator<ExampleModel>() {
    @Override
    public int compare(ExampleModel a, ExampleModel b) {
        return a.getText().compareTo(b.getText());
    }
};

private ExampleAdapter mAdapter;
private List<ExampleModel> mModels;
private RecyclerView mRecyclerView;

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    mAdapter = new ExampleAdapter(this, ALPHABETICAL_COMPARATOR);

    mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
    mBinding.recyclerView.setAdapter(mAdapter);

    mModels = new ArrayList<>();
    for (String movie : MOVIES) {
        mModels.add(new ExampleModel(movie));
    }
    mAdapter.add(mModels);
}

여기서는 특별한 일이 RecyclerView Adapter 인스턴스화하고 RecyclerView 설정하면됩니다. 그런 다음 MOVIES 배열의 영화 이름으로 모델 List 을 만듭니다. 그런 다음 모든 모델을 SortedList 추가합니다.

이제 우리는 앞서 정의한 onQueryTextChange() 로 돌아가서 필터 로직 구현을 시작할 수 있습니다.

@Override
public boolean onQueryTextChange(String query) {
    final List<ExampleModel> filteredModelList = filter(mModels, query);
    mAdapter.replaceAll(filteredModelList);
    mBinding.recyclerView.scrollToPosition(0);
    return true;
}

이것은 다시 매우 직설적입니다. filter() 메소드를 호출하고 ExampleModelExampleModel 예제를 전달합니다. 그런 다음 Adapter 에서 replaceAll() 을 호출하고 replaceAll() 에서 반환 한 필터링 된 List 전달합니다. 또한 RecyclerView 에서 scrollToPosition(0) 을 호출하여 사용자가 무언가를 검색 할 때 항상 모든 항목을 볼 수 있도록해야합니다. 그렇지 않으면 RecyclerView 가 필터링되는 동안 아래로 스크롤 된 위치를 유지 한 후 몇 개의 항목을 숨길 수 있습니다. 맨 위로 스크롤하면 검색하는 동안 사용자 경험이 향상됩니다.

이제 남은 것은 filter() 자체를 구현하는 것입니다.

private static List<ExampleModel> filter(List<ExampleModel> models, String query) {
    final String lowerCaseQuery = query.toLowerCase();

    final List<ExampleModel> filteredModelList = new ArrayList<>();
    for (ExampleModel model : models) {
        final String text = model.getText().toLowerCase();
        if (text.contains(lowerCaseQuery)) {
            filteredModelList.add(model);
        }
    }
    return filteredModelList;
}

우리가 여기서하는 첫 번째 일은 쿼리 문자열에서 toLowerCase() 를 호출하는 것입니다. 검색 함수가 대소 문자를 구분하는 것을 원하지 않으며 비교할 모든 문자열에서 toLowerCase() 를 호출하여 대소 문자와 상관없이 동일한 결과를 반환 할 수 있습니다. 그런 다음 전달한 List 모든 모델을 반복하고 쿼리 문자열이 모델의 텍스트에 포함되어 있는지 확인합니다. 이 경우 모델이 필터링 된 List 추가됩니다.

그리고 그게 다야! 위의 코드는 API 레벨 7 이상에서 실행되며 API 레벨 11부터 무료로 항목 애니메이션을 얻을 수 있습니다!

나는 이것이이 전체를 실제로보다 복잡하게 보이게하는 매우 상세한 설명이라는 것을 알고 있지만이 전체 문제를 일반화하고 SortedList 기반으로 Adapter 를 훨씬 간단하게 구현할 수있는 방법이 있습니다.

문제점 일반화 및 어댑터 단순화

이 섹션에서는 스택 오버플로에 대한 답변에 대한 문자 제한에 대해 실행 중이지만 대부분 위에서 이미 설명 했으므로 변경 사항을 요약하기 위해 크게 자세하게 설명하지는 않습니다. 기본 Adapter 구현할 수 있습니다. 클래스는 이미 SortedList 를 처리하고 ViewHolder 인스턴스에 모델을 바인딩하고 SortedList 기반의 Adapter 를 구현하는 편리한 방법을 제공합니다. 이를 위해 우리는 두 가지 일을해야합니다.

  • 모든 모델 클래스가 구현해야하는 ViewModel 인터페이스를 만들어야합니다
  • Adapter 가 모델을 자동으로 바인딩하는 데 사용할 수있는 bind() 메소드를 정의하는 ViewHolder 서브 클래스를 작성해야합니다.

이를 통해 모델을 구현하고 해당 ViewHolder 구현을 통해 RecyclerView 에 표시 될 내용에 집중할 수 있습니다. 이 기본 클래스를 사용하면 AdapterSortedList 의 복잡한 세부 사항에 대해 걱정할 필요가 없습니다.

정렬 된 목록 어댑터

에 대한 답변의 문자 제한으로 인해이 기본 클래스를 구현하는 각 단계를 거치거나 여기에 전체 소스 코드를 추가 할 수는 없지만이 기본 클래스의 전체 소스 코드를 찾을 수 있습니다-나는 그것을 SortedListAdapter 라고 불렀습니다. 이 GitHub Gist .

당신의 인생을 간단하게하기 위해 나는 SortedListAdapter 를 포함하는 jCenter에 라이브러리를 게시했습니다! 그것을 사용하려면 앱의 build.gradle 파일 에이 종속성을 추가하기 만하면됩니다.

compile 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.1'

이 라이브러리 대한 자세한 정보 는 라이브러리 홈페이지에서 찾을 수 있습니다.

SortedListAdapter 사용

SortedListAdapter 를 사용하려면 다음 두 가지를 변경해야합니다.

  • SortedListAdapter.ViewHolder 확장하도록 ViewHolder 변경하십시오. type 매개 변수는이 ViewHolder 바인딩해야하는 모델이어야합니다 (이 경우 ExampleModel . bind performBind() 대신 performBind() 에서 모델에 데이터를 바인딩해야합니다.

    public class ExampleViewHolder extends SortedListAdapter.ViewHolder<ExampleModel> {
    
        private final ItemExampleBinding mBinding;
    
        public ExampleViewHolder(ItemExampleBinding binding) {
            super(binding.getRoot());
            mBinding = binding;
        }
    
        @Override
        protected void performBind(ExampleModel item) {
            mBinding.setModel(item);
        }
    }
  • 모든 모델이 ViewModel 인터페이스를 구현해야 ViewModel .

    public class ExampleModel implements SortedListAdapter.ViewModel {
        ...
    }

그런 다음 ExampleAdapter 를 업데이트하여 SortedListAdapter 를 확장하고 더 이상 필요하지 않은 모든 것을 제거해야합니다. type 매개 변수는 작업중인 모델의 유형이어야합니다 (이 경우 ExampleModel . 그러나 다른 유형의 모델로 작업하는 경우 type 매개 변수를 ViewModel 로 설정하십시오.

public class ExampleAdapter extends SortedListAdapter<ExampleModel> {

    public ExampleAdapter(Context context, Comparator<ExampleModel> comparator) {
        super(context, ExampleModel.class, comparator);
    }

    @Override
    protected ViewHolder<? extends ExampleModel> onCreateViewHolder(LayoutInflater inflater, ViewGroup parent, int viewType) {
        final ItemExampleBinding binding = ItemExampleBinding.inflate(inflater, parent, false);
        return new ExampleViewHolder(binding);
    }

    @Override
    protected boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
        return item1.getId() == item2.getId();
    }

    @Override
    protected boolean areItemContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
        return oldItem.equals(newItem);
    }
}

그 후 우리는 끝났습니다! 그러나 마지막으로 언급 할 사항은 다음과 같습니다. SortedListAdapter 에는 원래 ExampleAdapter 와 동일한 add() , remove() 또는 replaceAll() 메소드가 없습니다. 별도의 Editor 객체를 사용하여 edit() 메서드를 통해 액세스 할 수있는 목록의 항목을 수정합니다. 따라서 항목을 제거하거나 추가 edit() 를 호출 한 다음이 Editor 인스턴스에서 항목을 추가 및 제거해야합니다. 일단 완료되면 commit() 을 호출하여 변경 사항을 SortedList 적용하십시오.

mAdapter.edit()
        .remove(modelToRemove)
        .add(listOfModelsToAdd)
        .commit();

이 방법으로 수행 한 모든 변경 사항은 함께 배치되어 성능을 향상시킵니다. 위의 장에서 구현 한 replaceAll() 메소드도이 Editor 객체에 있습니다.

mAdapter.edit()
        .replaceAll(mModels)
        .commit();

commit() 호출을 잊어 버린 경우 변경 사항이 적용되지 않습니다!


링크를 수정하여 동일한 문제를 해결했습니다. 카드가있는 RecyclerView의 검색 필터. 가능합니까? (이것이 도움이되기를 바랍니다).

여기 내 어댑터 클래스가 있습니다

public class ContactListRecyclerAdapter extends RecyclerView.Adapter<ContactListRecyclerAdapter.ContactViewHolder> implements Filterable {

Context mContext;
ArrayList<Contact> customerList;
ArrayList<Contact> parentCustomerList;


public ContactListRecyclerAdapter(Context context,ArrayList<Contact> customerList)
{
    this.mContext=context;
    this.customerList=customerList;
    if(customerList!=null)
    parentCustomerList=new ArrayList<>(customerList);
}

   // other overrided methods

@Override
public Filter getFilter() {
    return new FilterCustomerSearch(this,parentCustomerList);
}
}

// 필터 클래스

import android.widget.Filter;
import java.util.ArrayList;


public class FilterCustomerSearch extends Filter
{
private final ContactListRecyclerAdapter mAdapter;
ArrayList<Contact> contactList;
ArrayList<Contact> filteredList;

public FilterCustomerSearch(ContactListRecyclerAdapter mAdapter,ArrayList<Contact> contactList) {
    this.mAdapter = mAdapter;
    this.contactList=contactList;
    filteredList=new ArrayList<>();
}

@Override
protected FilterResults performFiltering(CharSequence constraint) {
    filteredList.clear();
    final FilterResults results = new FilterResults();

    if (constraint.length() == 0) {
        filteredList.addAll(contactList);
    } else {
        final String filterPattern = constraint.toString().toLowerCase().trim();

        for (final Contact contact : contactList) {
            if (contact.customerName.contains(constraint)) {
                filteredList.add(contact);
            }
            else if (contact.emailId.contains(constraint))
            {
                filteredList.add(contact);

            }
            else if(contact.phoneNumber.contains(constraint))
                filteredList.add(contact);
        }
    }
    results.values = filteredList;
    results.count = filteredList.size();
    return results;
}

@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
    mAdapter.customerList.clear();
    mAdapter.customerList.addAll((ArrayList<Contact>) results.values);
    mAdapter.notifyDataSetChanged();
}

}

// 활동 클래스

public class HomeCrossFadeActivity extends AppCompatActivity implements View.OnClickListener,OnFragmentInteractionListener,OnTaskCompletedListner
{
Fragment fragment;
 protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_homecrossfadeslidingpane2);CardView mCard;
   setContentView(R.layout.your_main_xml);}
   //other overrided methods
  @Override
   public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.

    MenuInflater inflater = getMenuInflater();
    // Inflate menu to add items to action bar if it is present.
    inflater.inflate(R.menu.menu_customer_view_and_search, menu);
    // Associate searchable configuration with the SearchView
    SearchManager searchManager =
            (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView =
            (SearchView) menu.findItem(R.id.menu_search).getActionView();
    searchView.setQueryHint("Search Customer");
    searchView.setSearchableInfo(
            searchManager.getSearchableInfo(getComponentName()));

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {
            if(fragment instanceof CustomerDetailsViewWithModifyAndSearch)
                ((CustomerDetailsViewWithModifyAndSearch)fragment).adapter.getFilter().filter(newText);
            return false;
        }
    });



    return true;
}
}

OnQueryTextChangeListener () 메소드에서 어댑터를 사용하십시오. 내 adpter가 조각난 상태에서 조각으로 캐스트했습니다. 활동 클래스에있는 경우 어댑터를 직접 사용할 수 있습니다.


어댑터에서 :

public void setFilter(List<Channel> newList){
        mChannels = new ArrayList<>();
        mChannels.addAll(newList);
        notifyDataSetChanged();
    }

활동 중 :

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                newText = newText.toLowerCase();
                ArrayList<Channel> newList = new ArrayList<>();
                for (Channel channel: channels){
                    String channelName = channel.getmChannelName().toLowerCase();
                    if (channelName.contains(newText)){
                        newList.add(channel);
                    }
                }
                mAdapter.setFilter(newList);
                return true;
            }
        });

이것은 필터링 애니메이션을 잃지 않기 위해 @klimat 답변을 확장하는 것입니다.

public void filter(String query){
    int completeListIndex = 0;
    int filteredListIndex = 0;
    while (completeListIndex < completeList.size()){
        Movie item = completeList.get(completeListIndex);
        if(item.getName().toLowerCase().contains(query)){
            if(filteredListIndex < filteredList.size()) {
                Movie filter = filteredList.get(filteredListIndex);
                if (!item.getName().equals(filter.getName())) {
                    filteredList.add(filteredListIndex, item);
                    notifyItemInserted(filteredListIndex);
                }
            }else{
                filteredList.add(filteredListIndex, item);
                notifyItemInserted(filteredListIndex);
            }
            filteredListIndex++;
        }
        else if(filteredListIndex < filteredList.size()){
            Movie filter = filteredList.get(filteredListIndex);
            if (item.getName().equals(filter.getName())) {
                filteredList.remove(filteredListIndex);
                notifyItemRemoved(filteredListIndex);
            }
        }
        completeListIndex++;
    }
}

기본적으로 전체 목록을 살펴보고 필터링 된 목록에 항목을 하나씩 추가 / 제거합니다.


@Shruthi Kamoji를 더 깔끔하게 따라 가면 필터링 가능한 필터를 사용할 수 있습니다.

public abstract class GenericRecycleAdapter<E> extends RecyclerView.Adapter implements Filterable
{
    protected List<E> list;
    protected List<E> originalList;
    protected Context context;

    public GenericRecycleAdapter(Context context,
    List<E> list)
    {
        this.originalList = list;
        this.list = list;
        this.context = context;
    }

    ...

    @Override
    public Filter getFilter() {
        return new Filter() {
            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                list = (List<E>) results.values;
                notifyDataSetChanged();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                List<E> filteredResults = null;
                if (constraint.length() == 0) {
                    filteredResults = originalList;
                } else {
                    filteredResults = getFilteredResults(constraint.toString().toLowerCase());
                }

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

                return results;
            }
        };
    }

    protected List<E> getFilteredResults(String constraint) {
        List<E> results = new ArrayList<>();

        for (E item : originalList) {
            if (item.getName().toLowerCase().contains(constraint)) {
                results.add(item);
            }
        }
        return results;
    }
} 

여기의 E는 제네릭 형식이므로 클래스를 사용하여 확장 할 수 있습니다.

public class customerAdapter extends GenericRecycleAdapter<CustomerModel>

또는 E를 원하는 유형으로 변경하십시오 (예 : <CustomerModel> ).

그런 다음 searchView (menu.xml에 넣을 수있는 위젯)에서 다음을 수행하십시오.

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String text) {
        return false;
    }

    @Override
    public boolean onQueryTextChange(String text) {
        yourAdapter.getFilter().filter(text);
        return true;
    }
});






android-filterable