在 RealmRecyclerViewAdapter 中的筛选不会隐藏被排除的元素。

huangapple go评论78阅读模式
英文:

Filtering in RealmRecyclerViewAdapter does not hide excluded elements

问题

我正在构建一个应用程序,该应用程序使用 RealmRecyclerViewAdapter 来显示 Realm 中的元素。

我正在研究实现 Filterable 接口,我成功地实现了这一点(多亏了这些答案:链接1 链接2),但现在我遇到了一个副作用:当我进行过滤时,Adapter 显示所有元素,即使它们与过滤条件不匹配。而且,被排除的元素显示了不正确的信息。当我关闭 SearchView 时,一切恢复正常。

这是我在调用 AdapterActivity

public class MainActivity extends AppCompatActivity {
    // ... (此处省略其他部分)
    RecyclerView rv = findViewById(R.id.habitCardRecyclerView);
    // ... (此处省略其他部分)
    adapter = new HabitCardAdapter(results, true, this, realm);
    rv.setAdapter(adapter);
}

这是扩展了 RealmRecyclerViewAdapterHabitCardAdapter

public class HabitCardAdapter extends RealmRecyclerViewAdapter<Habit, HabitCardAdapter.ViewHolder> implements Filterable {
    // ... (此处省略其他部分)
    @Override
    public void onBindViewHolder(@NonNull HabitCardAdapter.ViewHolder holder, int position) {
        // ... (此处省略其他部分)
    }

    public void filterResults(String text) {
        // ... (此处省略其他部分)
    }

    public Filter getFilter() {
        // ... (此处省略其他部分)
    }

    private class HabitFilter extends Filter {
        // ... (此处省略其他部分)
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        // ... (此处省略其他部分)
    }
}

如果你有其他需要翻译的部分,请继续提供。

英文:

I'm currently building an app which use a RealmRecyclerViewAdapter for displaying the elements inside Realm.

I was looking into implementing the Filterable interface, which I managed to do (thanks to those answers: Link 1 Link 2) but now I have a side effect: when I'm filtering, the Adapter shows all the elements, even if they doesn't match with the filter. Also, the excluded element does show incorrect information. When I close the SearchView, everything is back to normal.

Here is the Activity when I call the Adapter:

 public class MainActivity extends AppCompatActivity {
    private Realm realm;
    HabitCardAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setUIMode();
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Set the title inside the top bar for this activity.
        // I&#39;m not doing it inside the Manifest because it changes the app&#39;s name
        setTitle(R.string.MainActivityTitle);

        // Bottom App Bar setup
        BottomAppBar bottomAppBar = findViewById(R.id.bottomAppBar);
        cutBottomAppEdge(bottomAppBar);     // Diamond shape

        // Add listener to Stats button inside the bottom app bar
        MenuItem statsMenuItem = bottomAppBar.getMenu().findItem(R.id.statsMenuItem);
        statsMenuItem.setOnMenuItemClickListener(item -&gt; {
            if(item.getItemId() == R.id.statsMenuItem){
                Intent i = new Intent(getApplicationContext(), StatsActivity.class);
                startActivity(i);
                return true;
            }
            return false;
        });

        // FAB button setup
        FloatingActionButton fab = findViewById(R.id.fabAddButton);
        fab.setOnClickListener(view -&gt; {
            Intent intent = new Intent(getBaseContext(), CreateHabitActivity.class);
            startActivity(intent);
        });

        RecyclerView rv = findViewById(R.id.habitCardRecyclerView);
        TextView emptyMessage = findViewById(R.id.mainEmptyHabitListMessage);
        realm = Realm.getDefaultInstance();
        RealmResults&lt;Habit&gt; results = realm.where(Habit.class).sort(&quot;id&quot;).findAll();

        results.addChangeListener(habits -&gt; {
            if (habits.size() &gt; 0) {
                rv.setVisibility(View.VISIBLE);
                emptyMessage.setVisibility(View.GONE);
            } else {
                emptyMessage.setVisibility(View.VISIBLE);
                rv.setVisibility(View.GONE);
            }
        });

        //this is necessarily because it is not changed yet
        if (results.size() &gt; 0) {
            rv.setVisibility(View.VISIBLE);
            emptyMessage.setVisibility(View.GONE);

        } else {
            emptyMessage.setVisibility(View.VISIBLE);
            rv.setVisibility(View.GONE);
        }
        final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        rv.setLayoutManager(layoutManager);
        adapter = new HabitCardAdapter(results, true, this, realm);
        rv.setAdapter(adapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.top_app_bar_menu, menu);

        SearchView searchView = (SearchView) menu.findItem(R.id.searchMenuItem).getActionView();
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                adapter.getFilter().filter(query);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                if (adapter != null) {
                    adapter.getFilter().filter(newText);
                    return true;
                }
                return false;
            }
        });
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
            case R.id.searchMenuItem:
                return true;

            case R.id.settingMenuItem:
                Intent intent = new Intent(getApplicationContext(), SettingsActivity.class);
                startActivity(intent); //FIXME: animazione
                return true;

            case R.id.aboutMenuItem:
                MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
                builder.setTitle(getString(R.string.about_us_title));
                builder.setMessage(getString(R.string.about_us_message));
                builder.setIcon(R.drawable.ic_sprout_fg_small);
                builder.setPositiveButton(&quot;OK&quot;, (dialogInterface, i) -&gt; {
                    dialogInterface.dismiss();
                });
                builder.show();
                return true;

            default:
                return super.onOptionsItemSelected(item);
        }
    }


    /**
     * Set the Night/Light UI. On the first run of the app, the user get the Light UI.
     */
    private void setUIMode() {

        SharedPreferences preferences = getSharedPreferences(SettingsActivity.SHARED_PREFS_FILE, MODE_PRIVATE);

        int pref = preferences.getInt(SettingsActivity.SHARED_PREFS_DARK_MODE, AppCompatDelegate.MODE_NIGHT_NO);

        AppCompatDelegate.setDefaultNightMode(pref);
    }

    private void cutBottomAppEdge(BottomAppBar bar) {
        BottomAppBarTopEdgeTreatment topEdge = new SproutBottomAppBarCutCornersTopEdge(
                bar.getFabCradleMargin(),
                bar.getFabCradleRoundedCornerRadius(),
                bar.getCradleVerticalOffset());
        MaterialShapeDrawable babBackground = (MaterialShapeDrawable) bar.getBackground();
        //It requires 1.1.0-alpha10
        babBackground.setShapeAppearanceModel(
                babBackground.getShapeAppearanceModel()
                        .toBuilder()
                        .setTopEdge(topEdge)
                        .build());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        realm.removeAllChangeListeners();
        realm.close();
    }
} 

Here is the HabitCardAdapter which extends RealmRecyclerViewAdapter:

public class HabitCardAdapter extends RealmRecyclerViewAdapter&lt;Habit, HabitCardAdapter.ViewHolder&gt; implements Filterable {
    Context ct;
    OrderedRealmCollection&lt;Habit&gt; list;
    Realm mRealm;

    public HabitCardAdapter(@Nullable OrderedRealmCollection&lt;Habit&gt; data, boolean autoUpdate, Context context, Realm realm) {
        super(data, autoUpdate); //autoUpdate to true
        ct = context;
        list = data;
        mRealm = realm;
    }

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

    @NonNull
    @Override
    public HabitCardAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        //TODO: inflatare diversi tipi di carte a seconda del habitType
        View view = inflater.inflate(R.layout.fragment_habit_counter_card, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull HabitCardAdapter.ViewHolder holder, int position) {
        final Habit habit = getItem(position);
        if (habit != null) {
            holder.setHabit(habit);

            holder.editHabitButton.setOnClickListener(view -&gt; {
                Intent intent = new Intent(ct, EditHabitActivity.class);
                intent.putExtra(&quot;HABIT_ID&quot;, habit.getId());
                //TODO: Aggiungere l&#39;animazione
                ct.startActivity(intent);
            });

            holder.checkButton.setOnClickListener(view -&gt; {
                int habitId = habit.getId();
                int newRepValue = habit.getRepetitions() + 1;
                int maxReps = habit.getMaxRepetitions();
                Log.d(&quot;Testing&quot;, newRepValue + &quot; - &quot; + maxReps);
                if (newRepValue &lt;= habit.getMaxRepetitions()) {
                    habit.getRealm().executeTransaction(realm -&gt; {
                        Habit result = realm.where(Habit.class).equalTo(&quot;id&quot;, habitId).findFirst();
                        if (result != null) {
                            result.setRepetitions(newRepValue);
                            String newLabel = &quot;Completato &quot; + newRepValue + &quot; volte su &quot; + maxReps;
                            holder.progressLabel.setText(newLabel);
                        }
                    });
                }
            });
        }

    }

    public void filterResults(String text) {
        text = text == null ? null : text.toLowerCase().trim();

        if (text == null || &quot;&quot;.equals(text)) {
            updateData(mRealm.where(Habit.class).sort(&quot;id&quot;).findAllAsync());
        } else {
            updateData(mRealm.where(Habit.class).contains(&quot;title&quot;, text).sort(&quot;id&quot;).findAllAsync());
        }

    }

    public Filter getFilter() {
        HabitFilter filter = new HabitFilter(this);
        return filter;
    }

    private class HabitFilter extends Filter {
        private final HabitCardAdapter adapter;

        private HabitFilter(HabitCardAdapter adapter) {
            this.adapter = adapter;
        }

        @Override
        protected FilterResults performFiltering(CharSequence charSequence) {
            return new FilterResults();
        }

        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
            adapter.filterResults(charSequence.toString());
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        TextView habitTitle;
        ProgressBar progressBar;
        TextView progressLabel;
        ImageButton editHabitButton;
        Button checkButton;


        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            habitTitle = itemView.findViewById(R.id.habitCardTitle);
            editHabitButton = itemView.findViewById(R.id.counterHabitEditButton);
            progressBar = itemView.findViewById(R.id.counterHabitProgressBar);
            checkButton = itemView.findViewById(R.id.checkButton);
            progressLabel = itemView.findViewById(R.id.counterHabitProgressLabel);
        }

        void setHabit(Habit habit) {
            this.habitTitle.setText(habit.getTitle());
            this.progressBar.setProgress(habit.getRepetitions());
            this.progressBar.setMax(habit.getMaxRepetitions());
            this.progressLabel.setText(&quot;Completato &quot; + habit.getRepetitions() + &quot; volte su &quot; + habit.getMaxRepetitions()); //FIXME: sposta la stringa
        }
    }
}

答案1

得分: 0

不过我真的不确定这是否是解决这个问题的正确方法,但它现在按预期运行,所以我会在这里分享解决方案。

HabitCardAdapter 内部,我添加了另一个名为 filteredListOrderedRealmCollection<Habit> 成员,而 list 则包含了所有的数据。在构造函数中,filteredListlist 都与传递给构造函数的数据关联,但是 filteredList 将会被查询修改,而 list 则不会被修改(将其设为 final 可能是最佳实践)。然后,Adapter 中的所有内容现在都将引用 filteredList 而不是 list,当选择了 SearchView 并且查询生效时,filteredList 将获取数据,然后会调用 updateData(filteredList)

以下是我所做的更改部分:

public class HabitCardAdapter extends RealmRecyclerViewAdapter<Habit, HabitCardAdapter.ViewHolder> implements Filterable {
    Context ct;
    OrderedRealmCollection<Habit> list;
    OrderedRealmCollection<Habit> filteredList;
    Realm mRealm;

    ...
}
public HabitCardAdapter(@Nullable OrderedRealmCollection<Habit> data, Context context, Realm realm) {
    super(data, true, true);
    ct = context;
    list = data;
    filteredList = data;
    mRealm = realm;
    setHasStableIds(true);
}

可能错误出现在 getItemCount() 方法中,当 filteredList 的大小小于 list 的大小时,但是由于我没有对 filteredList 的引用,所以无法改变其大小,因此 Adapter 会继续显示 - 例如 - 6 个视图,而我在查询 3 个视图。将其作为一个适当的类成员,我可以这样做:

@Override
public int getItemCount() {
    return this.filteredList.size();
}
public void filterResults(String text) {
    text = text == null ? null : text.toLowerCase().trim();
    if (text == null || "".equals(text)) {
        filteredList = list;
    } else {
        filteredList = mRealm.where(Habit.class).beginsWith("title", text, Case.INSENSITIVE).sort("id").findAll();
    }
    updateData(filteredList);
}
英文:

I don't really know if this is the way to go for this problem, but it's now behaving as expected so I'll share the solution here.

Inside the HabitCardAdapter I added another OrderedRealmCollection&lt;Habit&gt; member, called filteredList, while list holds the whole data. In the costructor both of filteredList and list are tied to the data passed to the constructor, but while filteredList will be modified by the query, list will not (probably putting it to final is the best practice). Then everything in the Adapter will now reference to filteredList instead of list, and when the SearchView is selected and the query is up, filteredList will get the data, and then updateData(filteredList) will be called.

Here is what I changed:

public class HabitCardAdapter extends RealmRecyclerViewAdapter&lt;Habit, HabitCardAdapter.ViewHolder&gt; implements Filterable {
    Context ct;
    OrderedRealmCollection&lt;Habit&gt; list;
    OrderedRealmCollection&lt;Habit&gt; filteredList;
    Realm mRealm;

    ...
}
public HabitCardAdapter(@Nullable OrderedRealmCollection&lt;Habit&gt; data, Context context, Realm realm) {
        super(data, true, true);
        ct = context;
        list = data;
        filteredList = data;
        mRealm = realm;
        setHasStableIds(true);
    }

Probably the error was here in getItemCount(), when the filteredListsize was smaller than the list one, but since I didn't have any reference to filteredList, I didn't have any way to change that size, and so the Adapter would continue to show - for example - 6 views while I was querying for 3. Having it as a properly class member it let me make this:

@Override
    public int getItemCount() {
        return this.filteredList.size();
    }
public void filterResults(String text) {
        text = text == null ? null : text.toLowerCase().trim();
        if (text == null || &quot;&quot;.equals(text)) {
            filteredList = list;
        } else {
            filteredList = mRealm.where(Habit.class).beginsWith(&quot;title&quot;, text, Case.INSENSITIVE).sort(&quot;id&quot;).findAll();
        }
        updateData(filteredList);
    }

huangapple
  • 本文由 发表于 2020年9月10日 02:18:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/63817387.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定