RealmRecyclerViewAdapter 中的过滤不会隐藏被排除的元素
Filtering in RealmRecyclerViewAdapter does not hide excluded elements
我目前正在构建一个应用程序,它使用 RealmRecyclerViewAdapter
来显示 Realm
中的元素。
我正在考虑实现 Filterable
接口,我设法做到了(感谢这些答案: )但现在我有一个副作用:当我过滤时, Adapter
显示所有元素,即使它们与过滤器不匹配。此外,被排除的元素确实显示了不正确的信息。当我关闭 SearchView
时,一切恢复正常。
这是我调用 Adapter
时的 Activity
:
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'm not doing it inside the Manifest because it changes the app'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 -> {
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 -> {
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<Habit> results = realm.where(Habit.class).sort("id").findAll();
results.addChangeListener(habits -> {
if (habits.size() > 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() > 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("OK", (dialogInterface, i) -> {
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();
}
}
这是扩展 RealmRecyclerViewAdapter
的 HabitCardAdapter
:
public class HabitCardAdapter extends RealmRecyclerViewAdapter<Habit, HabitCardAdapter.ViewHolder> implements Filterable {
Context ct;
OrderedRealmCollection<Habit> list;
Realm mRealm;
public HabitCardAdapter(@Nullable OrderedRealmCollection<Habit> 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 -> {
Intent intent = new Intent(ct, EditHabitActivity.class);
intent.putExtra("HABIT_ID", habit.getId());
//TODO: Aggiungere l'animazione
ct.startActivity(intent);
});
holder.checkButton.setOnClickListener(view -> {
int habitId = habit.getId();
int newRepValue = habit.getRepetitions() + 1;
int maxReps = habit.getMaxRepetitions();
Log.d("Testing", newRepValue + " - " + maxReps);
if (newRepValue <= habit.getMaxRepetitions()) {
habit.getRealm().executeTransaction(realm -> {
Habit result = realm.where(Habit.class).equalTo("id", habitId).findFirst();
if (result != null) {
result.setRepetitions(newRepValue);
String newLabel = "Completato " + newRepValue + " volte su " + maxReps;
holder.progressLabel.setText(newLabel);
}
});
}
});
}
}
public void filterResults(String text) {
text = text == null ? null : text.toLowerCase().trim();
if (text == null || "".equals(text)) {
updateData(mRealm.where(Habit.class).sort("id").findAllAsync());
} else {
updateData(mRealm.where(Habit.class).contains("title", text).sort("id").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("Completato " + habit.getRepetitions() + " volte su " + habit.getMaxRepetitions()); //FIXME: sposta la stringa
}
}
}
我真的不知道这是否是解决这个问题的方法,但它现在的表现符合预期,所以我将在这里分享解决方案。
在 HabitCardAdapter
中我添加了另一个 OrderedRealmCollection<Habit>
成员,称为 filteredList
,而 list
保存了整个数据。在构造函数中,filteredList
和 list
都绑定到传递给构造函数的数据,但是 filteredList
将被查询修改,而 list
不会(可能将final
是最佳做法)。 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数据,然后 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
将继续显示 - 例如 - 在我查询 3 个视图时显示 6 个视图。将其作为正确的 class 成员它让我做这个:
@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);
}
我目前正在构建一个应用程序,它使用 RealmRecyclerViewAdapter
来显示 Realm
中的元素。
我正在考虑实现 Filterable
接口,我设法做到了(感谢这些答案:Adapter
显示所有元素,即使它们与过滤器不匹配。此外,被排除的元素确实显示了不正确的信息。当我关闭 SearchView
时,一切恢复正常。
这是我调用 Adapter
时的 Activity
:
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'm not doing it inside the Manifest because it changes the app'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 -> {
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 -> {
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<Habit> results = realm.where(Habit.class).sort("id").findAll();
results.addChangeListener(habits -> {
if (habits.size() > 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() > 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("OK", (dialogInterface, i) -> {
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();
}
}
这是扩展 RealmRecyclerViewAdapter
的 HabitCardAdapter
:
public class HabitCardAdapter extends RealmRecyclerViewAdapter<Habit, HabitCardAdapter.ViewHolder> implements Filterable {
Context ct;
OrderedRealmCollection<Habit> list;
Realm mRealm;
public HabitCardAdapter(@Nullable OrderedRealmCollection<Habit> 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 -> {
Intent intent = new Intent(ct, EditHabitActivity.class);
intent.putExtra("HABIT_ID", habit.getId());
//TODO: Aggiungere l'animazione
ct.startActivity(intent);
});
holder.checkButton.setOnClickListener(view -> {
int habitId = habit.getId();
int newRepValue = habit.getRepetitions() + 1;
int maxReps = habit.getMaxRepetitions();
Log.d("Testing", newRepValue + " - " + maxReps);
if (newRepValue <= habit.getMaxRepetitions()) {
habit.getRealm().executeTransaction(realm -> {
Habit result = realm.where(Habit.class).equalTo("id", habitId).findFirst();
if (result != null) {
result.setRepetitions(newRepValue);
String newLabel = "Completato " + newRepValue + " volte su " + maxReps;
holder.progressLabel.setText(newLabel);
}
});
}
});
}
}
public void filterResults(String text) {
text = text == null ? null : text.toLowerCase().trim();
if (text == null || "".equals(text)) {
updateData(mRealm.where(Habit.class).sort("id").findAllAsync());
} else {
updateData(mRealm.where(Habit.class).contains("title", text).sort("id").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("Completato " + habit.getRepetitions() + " volte su " + habit.getMaxRepetitions()); //FIXME: sposta la stringa
}
}
}
我真的不知道这是否是解决这个问题的方法,但它现在的表现符合预期,所以我将在这里分享解决方案。
在 HabitCardAdapter
中我添加了另一个 OrderedRealmCollection<Habit>
成员,称为 filteredList
,而 list
保存了整个数据。在构造函数中,filteredList
和 list
都绑定到传递给构造函数的数据,但是 filteredList
将被查询修改,而 list
不会(可能将final
是最佳做法)。 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数据,然后 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
将继续显示 - 例如 - 在我查询 3 个视图时显示 6 个视图。将其作为正确的 class 成员它让我做这个:
@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);
}