简体   繁体   English

我的RecyclerView过滤后不断重置

[英]My RecyclerView keeps getting reset after filtering

I have a working RecyclerView and I need to implement a search bar. 我有一个可正常使用的RecyclerView,我需要实现一个搜索栏。 I managed to create a search bar out of EditText and TextWatcher, but I have the following problem: 我设法用EditText和TextWatcher创建了一个搜索栏,但是存在以下问题:

Normally, my list passes data based on the position of current item, and when I filter the list, the positions get of course screwed up. 通常,我的列表根据当前项目的位置传递数据,并且当我过滤列表时,位置当然会被弄乱。

I followed some guides and I found this workaround: 我遵循了一些指南,发现了解决方法:

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

The problem with this approach is that of course when mList.clear() happens, I lose all my data and only the filtered ones remain. 这种方法的问题是,当发生mList.clear()时,我会丢失所有数据,仅保留过滤后的数据。 Then when I remove the text from the search bar all I have is an empty RecyclerView. 然后,当我从搜索栏中删除文本时,我所拥有的只是一个空的RecyclerView。

I thought about trying to reset the list to it's original state when the EditText is null, but I don't know how to do that and it's not ideal. 我曾想过在EditText为null时尝试将列表重置为原始状态,但是我不知道该怎么做,而且也不理想。

Can anyone think of a way to fix this? 谁能想到解决此问题的方法? I'm getting really desperate :D 我真的很绝望:D

My RecyclerView class: 我的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);
}

} }

You have 2 potential problems here: 您在这里有2个潜在问题:

  1. After the filteredList is passed to the mListAdapter, you have to call adapter.notifyDataSetChanged notifyDataSetChanged to invalidate the list view. 将filteredList传递给mListAdapter之后,必须调用adapter.notifyDataSetChanged notifyDataSetChanged来使列表视图无效。
  2. Besides, you don't need to clear the mList after you add the filtered list to adapter. 此外,将过滤列表添加到适配器后,无需清除mList As loadData is only called once, if you clear this the first time, the list only have the filtered item left. 由于loadData仅被调用一次,因此如果您第一次清除它,则列表中只剩下已过滤的项目。 Even if you remove the filter string, the item will not added back to the mList . 即使删除了过滤器字符串,该项目也不会添加回mList

Please try add the following: 请尝试添加以下内容:

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

In your RecyclerView Adapter class add a ValueFilter and an arraylist that holds the previous data. 在您的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 will show cannot resolve symbol 'ValueFilter' , so create a class called ValueFilter . ValueFilter将显示cannot resolve symbol 'ValueFilter' ,因此创建一个名为ValueFilter的类。

Create the class 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();
    }
}

Now in your EditText TextChangedListener onTextChanged 现在在您的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) {

        }
    });

You made a silly mistake. 你犯了一个愚蠢的错误。 You are clearing and adding new values in your real list also. 您还要清除并在实际列表中添加新值。 You should not do that. 你不应该那样做。

  • You should only parse a new list [ filterlist ] which you got in the filter. 您只应解析在过滤器中获得的新列表[filterlist]
  • And remove to call filter(s.toString()) from afterTextChanged and add-in onTextChanged 并从afterTextChanged和加载项onTextChanged中删除以调用filter(s.toString())

Example :- 例子:-

Step 1 : change call filter in class 第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());
    }
});

Step 2 : remove below the line in filter function. 第2步: 删除过滤功能中该行下方的内容。

mList.clear();
mList.addAll(filteredList);

Step 3 : and in your Adapter class :- 步骤3: 在您的适配器类中:

public void filterlist(List<Item> name) {
    this.Listname = name;
    notifyDataSetChanged();
}

To get a full understanding of Search in Recycleview to refer below link:- 要全面了解Recycleview中的搜索,请参考以下链接:-

how can fillter the recyclerView 如何填充recyclerView

Hi Etoile, 嗨Etoile,

Our Sample Problem: 我们的样本问题:

Let say we have a picture frame, all we want to do is erase part of it for a period of time, after which we want the full frame to show up again. 假设我们有一个相框,我们要做的就是将其一部分擦除一段时间,然后再显示整个相框。

Posible Solution: 可能的解决方案:

We can save instance of the full Frame somewhere let say A = instance of Frame then we can say B = A. So, we can use B as our main Frame and A as the backup frame. 我们可以将整个Frame的实例保存在某个地方,假设A = Frame的实例,然后我们可以说B =A。因此,我们可以将B用作主框架,将A用作备用框架。 After we modify B frame by erasing some part for a period of time, then we can easily return back to our previous state by saying if we don't want to modify again, then let B = A. since our A still maintain its state. 在我们通过擦除一段时间来修改B帧之后,可以很容易地返回到先前的状态,方法是说我们不想再次修改,则让B =A。因为我们的A仍然保持其状态。

Sample Implementation From your code (Modified though) 代码示例实现 (尽管已修改)

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

Please file a bug if any and we can make corrections. 请提交错误(如有),我们可以进行更正。 Happy coding. 快乐的编码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM