Make the RecyclerView searchable

This commit is contained in:
Jakob Nixdorf 2017-07-04 14:31:52 +02:00
parent 6275207362
commit 0a5124207d
No known key found for this signature in database
GPG key ID: BE99BF86574A7DBC
6 changed files with 148 additions and 20 deletions

View file

@ -35,6 +35,8 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
@ -48,19 +50,27 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.EntryViewHolder> public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.EntryViewHolder>
implements ItemTouchHelperAdapter { implements ItemTouchHelperAdapter, Filterable {
private Context context; private Context context;
private EntryFilter filter;
private ArrayList<Entry> entries; private ArrayList<Entry> entries;
private ArrayList<Integer> displayedEntries;
public ViewHolderEventCallback viewHolderEventCallback; public ViewHolderEventCallback viewHolderEventCallback;
public EntriesCardAdapter(Context context, ArrayList<Entry> entries) { public EntriesCardAdapter(Context context, ArrayList<Entry> entries) {
this.context = context; this.context = context;
this.entries = entries; this.entries = entries;
displayedEntries = defaultIndices();
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return displayedEntries.size();
}
public int getFullItemCount() {
return entries.size(); return entries.size();
} }
@ -70,11 +80,39 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.
public void setEntries(ArrayList<Entry> e) { public void setEntries(ArrayList<Entry> e) {
entries = e; entries = e;
displayedEntries.clear();
displayedEntries = defaultIndices();
notifyDataSetChanged();
}
public ArrayList<Integer> defaultIndices() {
ArrayList<Integer> newIdx = new ArrayList<>();
for (int i = 0; i < entries.size(); i++)
newIdx.add(i);
return newIdx;
}
public int removeIndex(int pos) {
int removed = displayedEntries.remove(pos);
ArrayList<Integer> newIdx = new ArrayList<>();
for (int i = 0; i < displayedEntries.size(); i++) {
int idx = displayedEntries.get(i);
if (idx > removed)
idx -= 1;
newIdx.add(idx);
}
displayedEntries = newIdx;
return removed;
} }
@Override @Override
public void onBindViewHolder(EntryViewHolder entryViewHolder, int i) { public void onBindViewHolder(EntryViewHolder entryViewHolder, int i) {
Entry entry = entries.get(i); Entry entry = entries.get(displayedEntries.get(i));
entryViewHolder.OTPValue.setText(entry.getCurrentOTP()); entryViewHolder.OTPValue.setText(entry.getCurrentOTP());
entryViewHolder.OTPLabel.setText(entry.getLabel()); entryViewHolder.OTPLabel.setText(entry.getLabel());
@ -104,7 +142,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i) {
entries.remove(position); entries.remove(removeIndex(position));
notifyItemRemoved(position); notifyItemRemoved(position);
SettingsHelper.store(context, entries); SettingsHelper.store(context, entries);
@ -121,15 +159,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.
@Override @Override
public boolean onItemMove(int fromPosition, int toPosition) { public boolean onItemMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) { Collections.swap(entries, fromPosition, toPosition);
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(entries, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(entries, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition);
SettingsHelper.store(context, entries); SettingsHelper.store(context, entries);
@ -141,7 +171,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.
AlertDialog.Builder builder = new AlertDialog.Builder(context); AlertDialog.Builder builder = new AlertDialog.Builder(context);
final EditText input = new EditText(context); final EditText input = new EditText(context);
input.setText(getItem(pos).getLabel()); input.setText(entries.get(displayedEntries.get(pos)).getLabel());
input.setSingleLine(); input.setSingleLine();
FrameLayout container = new FrameLayout(context); FrameLayout container = new FrameLayout(context);
@ -156,7 +186,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.
.setPositiveButton(R.string.button_save, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.button_save, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i) {
getItem(pos).setLabel(input.getEditableText().toString()); entries.get(displayedEntries.get(pos)).setLabel(input.getEditableText().toString());
notifyItemChanged(pos); notifyItemChanged(pos);
SettingsHelper.store(context, entries); SettingsHelper.store(context, entries);
@ -207,6 +237,43 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.
this.viewHolderEventCallback = cb; this.viewHolderEventCallback = cb;
} }
public EntryFilter getFilter() {
if (filter == null)
filter = new EntryFilter();
return filter;
}
public class EntryFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
final FilterResults filterResults = new FilterResults();
ArrayList<Integer> newIdx = new ArrayList<>();
if (constraint != null && constraint.length() != 0){
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getLabel().toLowerCase().contains(constraint.toString().toLowerCase())) {
newIdx.add(i);
}
}
} else {
newIdx = defaultIndices();
}
filterResults.count = newIdx.size();
filterResults.values = newIdx;
return filterResults;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
displayedEntries = (ArrayList<Integer>) results.values;
notifyDataSetChanged();
}
};
public class EntryViewHolder extends RecyclerView.ViewHolder public class EntryViewHolder extends RecyclerView.ViewHolder
implements ItemTouchHelperViewHolder { implements ItemTouchHelperViewHolder {

View file

@ -35,20 +35,30 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public static final float ALPHA_FULL = 1.0f; public static final float ALPHA_FULL = 1.0f;
private boolean dragEnabled = true;
private boolean swipeEnabled = true;
private final ItemTouchHelperAdapter mAdapter; private final ItemTouchHelperAdapter mAdapter;
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) { public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
mAdapter = adapter; mAdapter = adapter;
} }
public void setDragEnabled(boolean newDragState) {
this.dragEnabled = newDragState;
}
public void setSwipeEnabled(boolean newSwipeState) {
this.swipeEnabled = newSwipeState;
}
@Override @Override
public boolean isLongPressDragEnabled() { public boolean isLongPressDragEnabled() {
return true; return dragEnabled;
} }
@Override @Override
public boolean isItemViewSwipeEnabled() { public boolean isItemViewSwipeEnabled() {
return true; return swipeEnabled;
} }
@Override @Override

View file

@ -41,9 +41,11 @@ import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.support.v7.widget.helper.ItemTouchHelper; import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.Menu; import android.view.Menu;
@ -68,6 +70,7 @@ public class MainActivity extends AppCompatActivity {
private ArrayList<Entry> entries; private ArrayList<Entry> entries;
private EntriesCardAdapter adapter; private EntriesCardAdapter adapter;
private FloatingActionButton fab; private FloatingActionButton fab;
private SimpleItemTouchHelperCallback touchHelperCallback;
private Handler handler; private Handler handler;
private Runnable handlerTask; private Runnable handlerTask;
@ -184,7 +187,6 @@ public class MainActivity extends AppCompatActivity {
if (success) { if (success) {
entries = SettingsHelper.load(this); entries = SettingsHelper.load(this);
adapter.setEntries(entries); adapter.setEntries(entries);
adapter.notifyDataSetChanged();
Toast.makeText(this, R.string.msg_import_success, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.msg_import_success, Toast.LENGTH_LONG).show();
} else { } else {
@ -301,8 +303,8 @@ public class MainActivity extends AppCompatActivity {
adapter = new EntriesCardAdapter(this, entries); adapter = new EntriesCardAdapter(this, entries);
recList.setAdapter(adapter); recList.setAdapter(adapter);
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter); touchHelperCallback = new SimpleItemTouchHelperCallback(adapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback); ItemTouchHelper touchHelper = new ItemTouchHelper(touchHelperCallback);
touchHelper.attachToRecyclerView(recList); touchHelper.attachToRecyclerView(recList);
final float durationScale = fixAnimationScale(); final float durationScale = fixAnimationScale();
@ -334,7 +336,7 @@ public class MainActivity extends AppCompatActivity {
animation.start(); animation.start();
boolean change = false; boolean change = false;
for(int i =0;i < adapter.getItemCount(); i++){ for(int i =0;i < adapter.getFullItemCount(); i++){
boolean item_changed = adapter.getItem(i).updateOTP(); boolean item_changed = adapter.getItem(i).updateOTP();
change = change || item_changed; change = change || item_changed;
} }
@ -403,6 +405,38 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu); getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem searchItem = menu.findItem(R.id.menu_search);
SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
adapter.getFilter().filter(newText);
return false;
}
});
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
fab.setVisibility(View.GONE);
touchHelperCallback.setDragEnabled(false);
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
fab.setVisibility(View.VISIBLE);
touchHelperCallback.setDragEnabled(true);
return true;
}
});
return true; return true;
} }

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>

View file

@ -1,6 +1,13 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_search"
android:title="@string/title_search"
android:icon="@drawable/ic_search_white"
app:showAsAction="always|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />
<item <item
android:id="@+id/submenu_backup" android:id="@+id/submenu_backup"
android:title="@string/menu_submenu_backup"> android:title="@string/menu_submenu_backup">

View file

@ -42,6 +42,7 @@
<string name="msg_storage_permissions">Storage permissions not granted</string> <string name="msg_storage_permissions">Storage permissions not granted</string>
<string name="title_animator_duration">Animator duration scale</string> <string name="title_animator_duration">Animator duration scale</string>
<string name="title_search">Search</string>
<string name="pref_animator_warning_displayed">animator_warning_displayed</string> <string name="pref_animator_warning_displayed">animator_warning_displayed</string>