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'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();
}
}
Here is the HabitCardAdapter
which extends RealmRecyclerViewAdapter
:
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
}
}
}