Add action to a cancel button in an editable grid in vaadin - grid

I'm working with an editable Grid with Vaadin 7. When a row is in edit mode, it shows two buttons: save and cancel.
(Just in case, the image was taken from here Book of Vaadin)
With the:
grid.getEditorFieldGroup().addCommitHandler(new CommitHandler() {
private static final long serialVersionUID = 1L;
#SuppressWarnings("unchecked")
#Override
public void preCommit(CommitEvent commitEvent) throws CommitException{}
#Override
public void postCommit(CommitEvent commitEvent) throws CommitException{}
});
I can do something in the save action.
But, can I do something like that with the cancel action?
Thank you.

This is a serious deficiency of the component. According to the forum, they're working on it, but for the time being it seems that the most straightforward way is to extend the Grid component and override the doCancelEditor method. Here's a snippet:
public class MyGrid extends Grid {
protected Object newRow;
#Override
protected void doCancelEditor() {
super.doCancelEditor();
getContainerDataSource().removeItem(newRow);
setEditorEnabled(false);
}
public void setNewRow(Object newRow) {
this.newRow = newRow;
}
Note that you have to tell the MyGrid object when you create the row. Also, note that you're extending the server side, so you don't have to alter the client (widget code), but you do need to refer to the new component in your UI design.

Actually, saveEditor() should be also overridden, as doCancelEditor() seems to be invoked on save action, too. My code:
public class MyGrid extends Grid {
private boolean addingMode = false;
private JPAContainer<SomeEntity> container;
private Object recentlyAddedItemID;
public MyGrid(Indexed indexed) {
container = indexed;
}
#Override
protected void doCancelEditor() {
Object id = getEditedItemId();
super.doCancelEditor();
if (addingMode) {
getContainerDataSource().removeItem(id);
recentlyAddedItemID = null;
}
addingMode = false;
}
#Override
public void saveEditor() throws FieldGroup.CommitException {
if (addingMode) recentlyAddedItemID = getEditedItemId();
addingMode = false;
super.saveEditor();
}
public Object getRecentlyAddedItemID() {
return recentlyAddedItemID;
}
public void addNewElement(SomeEntity entity) {
addingMode = true;
editItem(container.addEntity(entity));
}
}
MyGrid grid = new MyGrid(JPAContainerFactory.make(SomeEntity.class, entityManager));
grid.addNewElement(new SomeEntity());
/*
if we want to know the new items's ID (actually the new primary key
in case of JPAContainer), we can check it by:
*/
Object id = grid.getRecentlyAddedItemID();
/*
returns null if editor was cancelled and finally nothing new was persisted
*/

Related

How does one set the TableColumn's sort arrow alignment?

The Gist
In a JavaFX TableColumn, there is a sort arrow off to the right side.
How does one set this arrow's alignment?
My Use Case
I ask because I'm trying to apply Material Design to JavaFX and the arrow needs to be on the left—otherwise the arrow appears to belong to the adjacent column.
What I do know
I know that you can get at the TableColumnHeader like so:
for (final Node headerNode : tableView.lookupAll(".column-header")) {
TableColumnHeader tableColumnHeader = (TableColumnHeader) headerNode;
I know that TableColumnHeader has a Label label and a GridPane sortArrowGrid as its children.
How do I move the sortArrowGrid to the front of the children? .toFront() is just for z-order right?
Node arrow = tableColumnHeader.lookup(".arrow");
if (arrow != null) {
GridPane sortArrowGrid = (GridPane) arrow.getParent();
// TODO: Move the sortArrowGrid to the front of the tableColumnHeader's children
}
I feel I may be going about this wrong—I'd love to do it with CSS.
Expanding a bit (with some code) on my comment: as already noted, alignment (or in fx-speak: content display) of the sort indicator is not configurable, not in style nor by any property of column/header - instead, it's hard-coded in the header's layout code.
Meaning that we need to implement a custom columnHeader that supports configurable display. The meat is in a custom TableColumnHeader which has:
a property sortIconDisplayProperty() to configure the relative location of the sort indicator
an overridden layoutChildren() that positions the label and sort indicator as configured
for fun: make that property styleable (needs some boilerplate around StyleableProperty and its registration with the CSS handlers)
To use, we need the whole stack of a custom TableViewSkin, TableHeaderRow, NestedTableColumnHeader: all just boiler-plate to create and return the custom xx instances in their relevant factory methods.
Below is an example which crudely (read: the layout is not perfect, should have some padding and guarantee not to overlap with the text ... but then, core isn't that good at it, neither) supports setting the icon left of the text. For complete support, you might want to implement setting it on top/bottom .. me being too lazy right now ;)
/**
* https://stackoverflow.com/q/49121560/203657
* position sort indicator at leading edge of column header
*
* #author Jeanette Winzenburg, Berlin
*/
public class TableHeaderLeadingSortArrow extends Application {
/**
* Custom TableColumnHeader that lays out the sort icon at its leading edge.
*/
public static class MyTableColumnHeader extends TableColumnHeader {
public MyTableColumnHeader(TableColumnBase column) {
super(column);
}
#Override
protected void layoutChildren() {
// call super to ensure that all children are created and installed
super.layoutChildren();
Node sortArrow = getSortArrow();
// no sort indicator, nothing to do
if (sortArrow == null || !sortArrow.isVisible()) return;
if (getSortIconDisplay() == ContentDisplay.RIGHT) return;
// re-arrange label and sort indicator
double sortWidth = sortArrow.prefWidth(-1);
double headerWidth = snapSizeX(getWidth()) - (snappedLeftInset() + snappedRightInset());
double headerHeight = getHeight() - (snappedTopInset() + snappedBottomInset());
// position sort indicator at leading edge
sortArrow.resize(sortWidth, sortArrow.prefHeight(-1));
positionInArea(sortArrow, snappedLeftInset(), snappedTopInset(),
sortWidth, headerHeight, 0, HPos.CENTER, VPos.CENTER);
// resize label to fill remaining space
getLabel().resizeRelocate(sortWidth, 0, headerWidth - sortWidth, getHeight());
}
// --------------- make sort icon location styleable
// use StyleablePropertyFactory to simplify styling-related code
private static final StyleablePropertyFactory<MyTableColumnHeader> FACTORY =
new StyleablePropertyFactory<>(TableColumnHeader.getClassCssMetaData());
// default value (strictly speaking: an implementation detail)
// PENDING: what about RtoL orientation? Is it handled correctly in
// core?
private static final ContentDisplay DEFAULT_SORT_ICON_DISPLAY = ContentDisplay.RIGHT;
private static CssMetaData<MyTableColumnHeader, ContentDisplay> CSS_SORT_ICON_DISPLAY =
FACTORY.createEnumCssMetaData(ContentDisplay.class,
"-fx-sort-icon-display",
header -> header.sortIconDisplayProperty(),
DEFAULT_SORT_ICON_DISPLAY);
// property with lazy instantiation
private StyleableObjectProperty<ContentDisplay> sortIconDisplay;
protected StyleableObjectProperty<ContentDisplay> sortIconDisplayProperty() {
if (sortIconDisplay == null) {
sortIconDisplay = new SimpleStyleableObjectProperty<>(
CSS_SORT_ICON_DISPLAY, this, "sortIconDisplay",
DEFAULT_SORT_ICON_DISPLAY);
}
return sortIconDisplay;
}
protected ContentDisplay getSortIconDisplay() {
return sortIconDisplay != null ? sortIconDisplay.get()
: DEFAULT_SORT_ICON_DISPLAY;
}
protected void setSortIconDisplay(ContentDisplay display) {
sortIconDisplayProperty().set(display);
}
/**
* Returnst the CssMetaData associated with this class, which may
* include the CssMetaData of its superclasses.
*
* #return the CssMetaData associated with this class, which may include
* the CssMetaData of its superclasses
*/
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return FACTORY.getCssMetaData();
}
/** {#inheritDoc} */
#Override
public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
return getClassCssMetaData();
}
//-------- reflection acrobatics .. might use lookup and/or keeping aliases around
private Node getSortArrow() {
return (Node) FXUtils.invokeGetFieldValue(TableColumnHeader.class, this, "sortArrow");
}
private Label getLabel() {
return (Label) FXUtils.invokeGetFieldValue(TableColumnHeader.class, this, "label");
}
}
private Parent createContent() {
// instantiate the tableView with the custom default skin
TableView<Locale> table = new TableView<>(FXCollections.observableArrayList(
Locale.getAvailableLocales())) {
#Override
protected Skin<?> createDefaultSkin() {
return new MyTableViewSkin<>(this);
}
};
TableColumn<Locale, String> countryCode = new TableColumn<>("CountryCode");
countryCode.setCellValueFactory(new PropertyValueFactory<>("country"));
TableColumn<Locale, String> language = new TableColumn<>("Language");
language.setCellValueFactory(new PropertyValueFactory<>("language"));
TableColumn<Locale, String> variant = new TableColumn<>("Variant");
variant.setCellValueFactory(new PropertyValueFactory<>("variant"));
table.getColumns().addAll(countryCode, language, variant);
BorderPane pane = new BorderPane(table);
return pane;
}
/**
* Custom nested columnHeader, headerRow und skin only needed to
* inject the custom columnHeader in their factory methods.
*/
public static class MyNestedTableColumnHeader extends NestedTableColumnHeader {
public MyNestedTableColumnHeader(TableColumnBase column) {
super(column);
}
#Override
protected TableColumnHeader createTableColumnHeader(
TableColumnBase col) {
return col == null || col.getColumns().isEmpty() || col == getTableColumn() ?
new MyTableColumnHeader(col) :
new MyNestedTableColumnHeader(col);
}
}
public static class MyTableHeaderRow extends TableHeaderRow {
public MyTableHeaderRow(TableViewSkinBase tableSkin) {
super(tableSkin);
}
#Override
protected NestedTableColumnHeader createRootHeader() {
return new MyNestedTableColumnHeader(null);
}
}
public static class MyTableViewSkin<T> extends TableViewSkin<T> {
public MyTableViewSkin(TableView<T> table) {
super(table);
}
#Override
protected TableHeaderRow createTableHeaderRow() {
return new MyTableHeaderRow(this);
}
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
URL uri = getClass().getResource("columnheader.css");
stage.getScene().getStylesheets().add(uri.toExternalForm());
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TableHeaderLeadingSortArrow.class.getName());
}
The columnheader.css to configure:
.column-header {
-fx-sort-icon-display: LEFT;
}
Version note:
the example is coded against fx9 - which moved Skins into public scope along with a bunch of other changes. To make it work with fx8
adjust import statement to old locations in com.sun.** (not shown anyway, your IDE is your friend ;)
for all SomethingHeader, change the constructors to contain the tableSkin as parameter and pass the skin in all factory methods (possible in fx8, as getTableViewSkin() - or similar - has protected scope and thus is accessible for subclasses)

Make TvInputService display an overlay

I'm trying to write a most simple TvInputService, that will display an overlay using TIF Companion Library.
The Live Channels application already recognizes my channel Simple Channel declared in my EpgSyncJobService subclass.
My Program is displayed in the Live Channels application's EPG as currently presented one.
However, all I can see is the blue spinner, as if the channel didn't "tune".
What am I doing wrong?
public class MyTvInputService extends BaseTvInputService {
#Override
public final BaseTvInputService.Session onCreateSession(String inputId) {
BaseTvInputService.Session session = new MyTvInputSession(this, inputId);
session.setOverlayViewEnabled(true);
return super.sessionCreated(session);
}
class MyTvInputSession extends BaseTvInputService.Session {
public MyTvInputSession(Context context, String inputId) {
super(context, inputId);
}
#Override
public View onCreateOverlayView() {
mTextView = new TextView(MyTvInputService.this);
mTextView.setText("This is an example overlay");
mTextView.setTextColor(Color.RED);
mTextView.setVisibility(View.VISIBLE);
mTextView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
return mTextView;
}
#Override
public boolean onPlayProgram(Program program, long startPosMs) {
return true;
}
#Override
public boolean onPlayRecordedProgram(RecordedProgram recordedProgram) {
return true;
}
#Override
public void onSetCaptionEnabled(boolean enable) {}
#Override
public TvPlayer getTvPlayer() { return null; }
}
private TextView mTextView;
}
According to TvInputService.Session.notifyVideoAvailable() documentation:
The TV input service must call this method as soon as the content
rendered onto its surface is ready for viewing. This method must be
called each time onTune(Uri) is called.
So it is enough to call notifyVideoAvailable() in BaseTvInputService.Session.onTune(Uri) method override, like so:
#Override
public boolean onTune(Uri channelUri) {
notifyVideoAvailable();
return true;
}
I must say, that using the TV Companion Library's BaseTvInputService makes finding such issues harder than when using bare TvInputService, as an onTune() method is non-abstract in BaseTvInputService.Session.

customize the style of javafx datepicker

I am trying to avoid rolling my own JavaFX control if I could do this with either CSS or some code in main.java/controller.java.
There is a button within the datepicker, which shows a calander icon, I want to change it to display an image.
I could do this to button using -fx-graphic. Is there an equivalent in datepicker?
I also wants to know if the weekday could be modified. my Locale is Chinese, it shows "星期一","星期二" and so on. It is like "Weekday Monday", which is long and unnecessary. I want to change it to 一, 二, etc. It is equivalent to Mon, Tue, etc.
Is there anyway to do this? if CSS could not do it.
Could this be done using reflection and set string value?
After playing a bit with the date picker component I have found out how to change the icon, using only css, in the button next to the date picker... it's more of a hack, but I could not google anywhere the "official way".
.date-picker:editable > .arrow-button > * {
-fx-opacity: 0; /* Set the node in the button to be visible */
}
.date-picker > .arrow-button {
-fx-background-image: url("/icons/calendar.png"); /* Url of the icon */
-fx-background-size: 60% 60%; /* Can be `cover` or `100% 100%` ect. */
-fx-background-repeat: no-repeat;
-fx-background-position: center center; /* Put the icon in the center */
}
With more styles I have managed to make my date picker look like this, notice the custom icon:
Formatting/configuring the internals of picker's dropDown is done in DatePickerContent. Which unfortunately is an internal class (no idea why it didn't make it into public api, fx9 would have been a perfect time to move it out into the open, along with the skins).
DatePickerContent configures the text of the weekDayName cells in a method updateDayNameCells() which internally is called whenever they need to be re-configured. If you are allowed to access/tweak internal code internals:
extend DatePickerContent to override updateDayNameCells, fully re-implement (c&p ;) and use your own formatter (the example below uses the "narrow standalone" style). Access to the dayNameCells and other fields must be done reflectively (beware: dirty!)
extend DatePickerSkin and inject your custom content, again requires reflective access (beware: dirty!)
extend DatePicker and let it return the custom skin in its skin factory method
Sample code:
public class DatePickerConfig extends Application {
/**
* Custom picker content which allows to tweak the formatter for weekDay cells.
*/
public static class XDatePickerContent extends DatePickerContent {
private DateTimeFormatter weekDayNameFormatter;
public XDatePickerContent(DatePicker datePicker) {
super(datePicker);
}
/**
* c&p from super to format with narrow formatter
*/
#Override
public void updateDayNameCells() {
// first day of week, 1 = monday, 7 = sunday
Locale locale = getLocale();
int firstDayOfWeek = WeekFields.of(locale).getFirstDayOfWeek().getValue();
List<DateCell> dayNameCells = getDayNameCells();
int daysPerWeek = getDaysPerWeek();
// july 13th 2009 is a Monday, so a firstDayOfWeek=1 must come out of the 13th
LocalDate date = LocalDate.of(2009, 7, 12 + firstDayOfWeek);
DateTimeFormatter weekDayNameFormatter = getWeekDayNameFormatter();
for (int i = 0; i < daysPerWeek; i++) {
String name = weekDayNameFormatter.withLocale(locale).format(date.plus(i, DAYS));
dayNameCells.get(i).setText(getTitleCaseWord(name));
}
}
/**
* Lazily creates and returns the formatter for week days.
* Note: this is called from the constructor which implies that
* the field is not available.
*/
private DateTimeFormatter getWeekDayNameFormatter() {
if (weekDayNameFormatter == null) {
weekDayNameFormatter =
createWeekDayNameFormatter(); //
}
return weekDayNameFormatter;
}
/**
* Factory method for weekDayNameFormatter, here: narrow standalone day name
*/
protected DateTimeFormatter createWeekDayNameFormatter() {
return DateTimeFormatter.ofPattern("ccccc");
}
// ------------- going dirty: reflective access to super
protected String getTitleCaseWord(String str) {
return (String) FXUtils.invokeGetMethodValue(DatePickerContent.class, this, "titleCaseWord", String.class, str);
}
protected int getDaysPerWeek() {
return (int) FXUtils.invokeGetFieldValue(DatePickerContent.class, this, "daysPerWeek");
}
protected List<DateCell> getDayNameCells() {
return (List<DateCell>) FXUtils.invokeGetFieldValue(DatePickerContent.class, this, "dayNameCells");
}
}
/**
* Custom picker skin that reflectively injects the custom content.
*/
public static class XDatePickerSkin extends DatePickerSkin {
public XDatePickerSkin(DatePicker control) {
super(control);
}
/**
* Overridden to reflectively inject the custom picker content.
*/
#Override
public Node getPopupContent() {
DatePickerContent content = (XDatePickerContent) getDatePickerContent();
if (!(content instanceof XDatePickerContent)) {
content = new XDatePickerContent((DatePicker) getSkinnable());
replaceDatePickerContent(content);
}
return content;
}
//------------- going dirty: reflective access to super
protected DatePickerContent getDatePickerContent() {
return (DatePickerContent) FXUtils.invokeGetFieldValue(DatePickerSkin.class, this, "datePickerContent");
}
protected void replaceDatePickerContent(DatePickerContent content) {
FXUtils.invokeSetFieldValue(DatePickerSkin.class, this, "datePickerContent", content);
}
}
private Parent createContent() {
Locale.setDefault(Locale.CHINA);
LocalDate now = LocalDate.now();
DatePicker picker = new DatePicker(now) {
#Override
protected Skin<?> createDefaultSkin() {
return new XDatePickerSkin(this);
}
};
// just to see some options
List<String> patterns = List.of("c", "ccc", "cccc", "ccccc", "e", "ee", "eee", "eeee", "eeeee");
HBox box = new HBox(10);
patterns.forEach(p -> {
DateTimeFormatter ccc = DateTimeFormatter.ofPattern(p);
String name = ccc.withLocale(getLocale()).format(now);
box.getChildren().add(new Label(name));
});
BorderPane content = new BorderPane(picker);
content.setBottom(box);
return content;
}
protected Locale getLocale() {
return Locale.getDefault(Locale.Category.FORMAT);
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent(), 400, 200));
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(DatePickerConfig.class.getName());
}

JavaFX Property's invalidate() method not called with binding

I'm trying to make a custom control in JavaFX using some binding feature. This is my problem: I have a class with a DoubleProperty that I use to calculate the position of an element inside my custom control. Here is the code:
public class CustomControl extends Region {
private DoubleProperty positionProperty;
public CustomControl() {
positionProperty= new DoublePropertyBase(0.0) {
#Override public Object getBean() { return CustomControl.this; }
#Override public String getName() { return "position"; }
#Override protected void invalidated() { updatePostion(); }
};
}
public DoubleProperty positionProperty() { return positionProperty; }
public double getPosition() { return positionProperty.get(); }
public void setPosition(double value) { positionProperty.set(value); }
private void updatePosition() {
double value = doubleProperty.get();
//compute the new position using value
}
}
In my application i have two CustomControls and i want that when i call the method setPosition() on the first of them, the second one updates the position of its component as well. To do so I binded the positionProperty of the two CustomControls like this:
CustomControl control1 = new CustomControl();
CustomControl control2 = new CustomControl();
control2.positionProperty.bind(control1.positionProperty);
Then when I call for example
control1.setPosition(50.0);
only the position of the component of control1 is updated, indeed when i call the method setPosition(), the method invalidated() of the positionProperty of control1 is actually called, but not the one of the positionProperty of contol2 as I espected. How shoul i achieve what i want? Thank you!
PS: I also noticed that using the method bindBidirectional() instead of bind() works, but shouldn't it work using only bind() too?
EDIT: example code is available here: https://luca_bertolini#bitbucket.org/luca_bertolini/customcontrolexample.git
JavaFX uses lazy evaluation for all bindings, which means that when a change occurs on your out.positionProperty object, the new value is not immediately taken into account. This happens later, if and only when the value is subsequently requested.
Try this:
out.positionProperty().addListener(new InvalidationListener() {
#Override
public void invalidated(final Observable observable) {
// System.out.println("It must go now.");
}
});
and you will see that this time your code works as you want.

Toolbar with SearchView temporary filtering RecyclerView

I need to implement search functionality inside my android app which uses toolbar, SlidingTabLayout and ViewPager that holds fragments. Inside each fragment there is a RecyclerView with list of items.
RecyclerView data is static defined in separate class (DataAccess.java) and those lists are updated and RecyclerView gets refreshed just by calling (without passing new data)
mRecyclerView.getAdapter().notifyDataSetChanged();
Is there any simple way to temporary filter RecyclerView without changing the data and after the user presses return button inside Toolbar to remove the filter and show inital list.
Before pressing Search icon inside toolbar menu:
So when the user is typing "Josip..." the filter will be active
and after he presses the X button in SearchView the user will get the same data as before without filter.
#Override
public boolean onQueryTextChange(String newText) {
// filter data (temporary remove all items from DataAccess.list that don't .startsWith(newText)
}
#Override
public boolean onQueryTextSubmit(String query)
// Doesn't help if I revert deleted items here
}
#Override
public boolean onQueryTextSubmit(String query){
((ItemAdapter) myRecList.getAdapter()).setFilter(query)
}
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> {
private List<String> visibleObjects;
private List<String> allObjects;
.....
public void flushFilter(){
visibleObjects=new ArrayList<>();
visibleObjects.addAll(allObjects);
notifyDataSetChanged();
}
public void setFilter(String queryText) {
visibleObjects = new ArrayList<>();
constraint = constraint.toString().toLowerCase();
for (String item: allObjects) {
if (item.toLowerCase().contains(queryText))
visibleObjects.add(item);
}
notifyDataSetChanged();
}
}
I wanted to add as comment but due to less reputation...I am answering post.
This method works fine if (item.toLowerCase().contains(queryText)) but what to do if match is not found in first iteration.then it will go in else part without looping throughout allObjects list...
for (RouteByATMList.Route_ATM item: Main_ATMItemList)
{
if (item.ATMNumber.contains(queryText)) {
visibleObjects.add(item);
}else {
Toast.makeText(mContext,"No search result found!",Toast.LENGTH_SHORT).show();
break;
}
}
I got the answer from my superior ,hope it helps.
public void setFilter(String queryText) {
visibleObjects = new ArrayList<>();
for (RouteByATMList.Route_ATM item: Main_ATMItemList)
{
if (item.ATMNumber.contains(queryText))
{
visibleObjects.add(item);
}
}
if(visibleObjects.size()==0){
Toast.makeText(mContext,"No search result found!",Toast.LENGTH_SHORT).show();
}
notifyDataSetChanged();
Log.e("dataset changed","dataset changed");
}
I don't oppose the given and accepted answer. There is a room for possible performance pitfalls. One should make use of Filterabe interface. Having this implemented will behave as ol'good ListView that did the filtering asynchronously. Then all you need is to write your own Filter and instantiate it in the overridden getFilter() method;
In my case I used Filter to sort an adapter of many (yeah, many many) items. It was junky sorting it on UI-thread.
So I had this abstract class
public abstract class BaseFilterableRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> implements Filterable {
private Context mContext;
public BaseFilterableRecyclerViewAdapter(Context context) {
this.mContext = context;
}
public abstract void sort(SortingFilter.Sort sortingStrategy);
//...Other methods
}
And the class that inherit:
public class ItemAdapter extends BaseFilterableRecyclerViewAdapter<RecyclerView.ViewHolder>{
//... RecyclerView methods
#Override
public Filter getFilter() {
return new SortingFilter(mData) {
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.values != null) {
int last = mData.size();
mData = (List<Product>) results.values;
notifyDataSetChanged();
}
}
};
}
#Override
public void sort(SortingFilter.Sort sortingStrategy) {
getFilter().filter(sortingStrategy.toString());
}
}

Resources