[英]Getting wrong position after Recyclerview filtering using SearchView
[英]My RecyclerView keeps getting reset after filtering
我有一个可正常使用的RecyclerView,我需要实现一个搜索栏。 我设法用EditText和TextWatcher创建了一个搜索栏,但是存在以下问题:
通常,我的列表根据当前项目的位置传递数据,并且当我过滤列表时,位置当然会被弄乱。
我遵循了一些指南,发现了解决方法:
private void filter(String text){
ArrayList<Item> filteredList = new ArrayList<>();
for (Item item : mList ) {
if(item.getName().toLowerCase().contains(text.toLowerCase())) {
filteredList.add(item);
}
}
mListAdapter.filterList(filteredList);
//when the app is done filtering, it clears the existing ArrayList and it adds just the filtered Items
mList.clear();
mList.addAll(filteredList);
}
这种方法的问题是,当发生mList.clear()时,我会丢失所有数据,仅保留过滤后的数据。 然后,当我从搜索栏中删除文本时,我所拥有的只是一个空的RecyclerView。
我曾想过在EditText为null时尝试将列表重置为原始状态,但是我不知道该怎么做,而且也不理想。
谁能想到解决此问题的方法? 我真的很绝望:D
我的RecyclerView类:
public class ListFragment extends Fragment {
static final String EXTRA_NAME = "monumentname";
static final String EXTRA_NUMBER = "monumentnumber";
static final String EXTRA_REGION = "monumentregion";
static final String EXTRA_REGION2 = "monumentregion2";
static final String EXTRA_TOWN = "monumenttown";
static final String EXTRA_DESCRIPTION = "monumentdescription";
static final String EXTRA_WEB = "web";
private ListAdapter mListAdapter;
private ArrayList<Item> mList;
private RequestQueue mRequestQueue;
private Context mContext;
private InternetCheck internetCheck = new InternetCheck();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable final Bundle savedInstanceState) {
View rootview = inflater.inflate(R.layout.fragment_list, container, false);
RecyclerView mRecyclerView = rootview.findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
Button syncData = rootview.findViewById(R.id.sync_button);
final ProgressBar progressBar = rootview.findViewById(R.id.progressbar);
EditText editText = rootview.findViewById(R.id.edittext);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable s) {
filter(s.toString());
}
});
..........some other irrelevant stuff here
mListAdapter.setOnItemClickListener(new ListAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
Intent detailIntent = new Intent(getActivity(), SingleItem.class);
Item clickedItem = mList.get(position);
detailIntent.putExtra(EXTRA_NAME, clickedItem.getName());
detailIntent.putExtra(EXTRA_NUMBER, clickedItem.getNumber());
detailIntent.putExtra(EXTRA_REGION, clickedItem.getRegion());
detailIntent.putExtra(EXTRA_REGION2, clickedItem.getRegion2());
detailIntent.putExtra(EXTRA_TOWN, clickedItem.getTown());
detailIntent.putExtra(EXTRA_DESCRIPTION, clickedItem.getDescription());
detailIntent.putExtra(EXTRA_WEB, clickedItem.getWeb());
startActivity(detailIntent);
}
});
return rootview;
}
.......some onCreateView and stuff like that here
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadData();
mListAdapter = new ListAdapter(mContext, mList);
mRequestQueue = Volley.newRequestQueue(mContext);
}
private void parseJSON() {
JsonObjectRequest request = new JsonObjectRequest("http://192.168.0.105/sestavsisvujsvetweb/api/seznammagnetek", null, new Response.Listener<JSONObject>() {
.....parsing my JSON here
mRequestQueue.add(request);
}
private CharSequence removeHtmlFrom(String html) {
return new HtmlCleaner().clean(html).getText();
}
@Override
public void onStop() {
super.onStop();
saveData();
}
private void saveData() {
SharedPreferences sharedPreferences = getContext().getSharedPreferences("shared preferences", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
Gson gson = new Gson();
String json = gson.toJson(mList);
editor.putString("seznam magnetek", json);
editor.apply();
}
private void loadData() {
SharedPreferences sharedPreferences = getContext().getSharedPreferences("shared preferences", MODE_PRIVATE);
Gson gson = new Gson();
String json = sharedPreferences.getString("seznam magnetek", null);
Type type = new TypeToken<ArrayList<Item>>() {
}.getType();
mList = gson.fromJson(json, type);
if (mList == null || mList.isEmpty()) {
Toast.makeText(getContext(), "Seznam magnetek je prázdný. Aktualizujte prosím data z databáze.", Toast.LENGTH_SHORT).show();
mList = new ArrayList<>();
}
}
private void filter(String text) {
ArrayList<Item> filteredList = new ArrayList<>();
for (Item item : mList) {
if (item.getName().toLowerCase().contains(text.toLowerCase())) {
filteredList.add(item);
}
}
mListAdapter.filterList(filteredList);
mList.clear();
mList.addAll(filteredList);
}
}
您在这里有2个潜在问题:
adapter.notifyDataSetChanged
notifyDataSetChanged来使列表视图无效。 mList
。 由于loadData
仅被调用一次,因此如果您第一次清除它,则列表中只剩下已过滤的项目。 即使删除了过滤器字符串,该项目也不会添加回mList
。 请尝试添加以下内容:
private void filter(String text){
ArrayList<Item> filteredList = new ArrayList<>();
for (Item item : mList ) {
if(item.getName().toLowerCase().contains(text.toLowerCase())) {
filteredList.add(item);
}
}
mListAdapter.filterList(filteredList);
mListAdapter.notifyDataSetChanged();
}
在您的RecyclerView Adapter类中,添加一个ValueFilter
和一个保存先前数据的ValueFilter
。
private ValueFilter valueFilter;
private ArrayList<Item> mListFull; // this arraylist contains previous data.
private ArrayList<Item> mList;
public Filter getFilter() {
if (valueFilter == null) {
valueFilter = new ValueFilter();
}
return valueFilter;
}
ValueFilter将显示cannot resolve symbol 'ValueFilter'
,因此创建一个名为ValueFilter的类。
创建类ValueFilter
private class ValueFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
String str = constraint.toString().toUpperCase();
Log.e("constraint", str);
FilterResults results = new FilterResults();
if (constraint.length() > 0) {
ArrayList<Item> filterList = new ArrayList<>();
for (int i = 0; i < mListFull.size(); i++) {
if ((mListFull.get(i).getName().toUpperCase())
.contains(constraint.toString().toUpperCase())) {
Item item = mListFull.get(i);
filterList.add(item);
}
}
results.count = filterList.size();
results.values = filterList;
} else {
results.count = mListFull.size();
results.values = mListFull;
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mList = (ArrayList<Item>) results.values;
notifyDataSetChanged();
}
}
现在在您的EditText TextChangedListener onTextChanged中
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
youradapter.getFilter().filter(charSequence.toString()); //Add this (change youradapter with the name of your recyclerview adapter.)
}
@Override
public void afterTextChanged(Editable s) {
}
});
你犯了一个愚蠢的错误。 您还要清除并在实际列表中添加新值。 你不应该那样做。
例子:-
第1步: 在课堂上更改通话过滤器
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
filter(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
// filter(s.toString());
}
});
第2步: 删除过滤功能中该行下方的内容。
mList.clear();
mList.addAll(filteredList);
步骤3: 在您的适配器类中:
public void filterlist(List<Item> name) {
this.Listname = name;
notifyDataSetChanged();
}
要全面了解Recycleview中的搜索,请参考以下链接:-
嗨Etoile,
我们的样本问题:
假设我们有一个相框,我们要做的就是将其一部分擦除一段时间,然后再显示整个相框。
可能的解决方案:
我们可以将整个Frame的实例保存在某个地方,假设A = Frame的实例,然后我们可以说B =A。因此,我们可以将B用作主框架,将A用作备用框架。 在我们通过擦除一段时间来修改B帧之后,可以很容易地返回到先前的状态,方法是说我们不想再次修改,则让B =A。因为我们的A仍然保持其状态。
代码示例实现 (尽管已修改)
private ArrayList<Item> mList; //Aka A from our solution above
private ArrayList<Item> filteredList = new ArrayList<>();// Aka B from our solution above
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListAdapter = new ListAdapter(mContext, filteredList);
loadData();
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2{
filter(s.toString()); // I feel like it will be faster filtering from here
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
/* Here, we load the lists of items and notify our adapter of the changes, the last time in this function does the nofity */
private void loadData() {
SharedPreferences sharedPreferences = getContext().getSharedPreferences("shared preferences", MODE_PRIVATE);
Gson gson = new Gson();
String json = sharedPreferences.getString("seznam magnetek", null);
mList = gson.fromJson(json, new TypeToken<ArrayList<Item>>(){}.getType(););
if (mList == null || mList.isEmpty()) {
Toast.makeText(getContext(), "Seznam magnetek je prázdný. Aktualizujte prosím data z databáze.", Toast.LENGTH_SHORT).show();
mList = new ArrayList<>();
}
filteredList = mList;
mListAdapter.notifyDataSetChanged();
}
//So, if nothing is typed in the EditText, we simply want to reset back to the //previous state else do as appropriate i.e modify filteredList and notify adapter
private void filter(String text){
if(text.isEmpty()){
filteredList = mList;
mListAdapter.notifyDataSetChanged();
}else{
filteredList.clear();
for (Item item : mList ) {
if(item.getName().toLowerCase().startsWith(text.toLowerCase())) {
filteredList.add(item); //Aka B from our solution above
}
}
//we have cleared and modified filteredList, all we need to do if notify adapter.
mListAdapter.notifyDataSetChanged();
}
}
请提交错误(如有),我们可以进行更正。 快乐的编码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.