I'm building an application that will allow users to pick from a RecyclerView list, highlighting their choice. The problem is that in order to highlight an item for the first time, a long press is needed. (Afterwards, a short click is enough to do the selection.)
I haven't found anything in the documentation to indicate why this happens.
I'm using SelectionTracker
Specifically following this guide
Here's the code :
https://github.com/marcosholgado/multiselection
Expectations:
I expect the item on the RecyclerView to be selected every time someone short clicks on it.
Reality:
In order to select an item for the first time, the user needs to long press it.
Any Ideas?
Just override SelectionHotspot to return true. That's all you need
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int = adapterPosition
override fun getSelectionKey(): Long? = itemId
override fun inSelectionHotspot(e: MotionEvent): Boolean { return true }
}
While I couldn't think of a solution that doesn't involve reimplementing both a MotionInputHandler and a SelectionTracker.Builder (as mentioned in the guide), there is a neat trick to achieve the behaviour you want.
We know that the TouchInputHandler selects items on single click as long as the SelectionTracker is not empty. That means if we have some special key saved in the SelectoinTracker that's not associated with a real list item we practically 'activate' the on single click selection mode this way. However we also have to make sure that our KeyProvider doesn't provide that same special key to keep our data consistent.
So assuming you have picked a special key, say ghostKey, activating and deactivating the selection mode is now a matter of calling mSelectionTracker.select(ghostkey) or mSelectionTracker.clearSelection(). You can then execute these calls however you like, be that having a button that activates and deactivates the selection mode or simply calling that during the hosting view creation process i.e onCreate, onCreateView etc..
If you are using Kotlin you can also define some Extensions that wrap these calls for you, so you'd be able to do things like mSelectionTracker.enable() or mSelectionTracker.disable()
Use this line
selectionTracker.select(item.getSelectionKey());
On this override method
.withOnItemActivatedListener(new OnItemActivatedListener() {
#Override
public boolean onItemActivated(#NonNull ItemDetailsLookup.ItemDetails item, #NonNull MotionEvent e) {
selectionTracker.select(item.getSelectionKey());
return true;
}
})
I solved like this. Keep coding..
In your ViewHolder, set onClickListener and in that check whether you have any selected items using SelectionTracker.hasSelection() and if it returns false, just select that item using SelectionTracker.select(getItemDetails().getSelectionKey())
like this:
itemView.setOnClickListener(v -> {
if (!tracker.hasSelection())
tracker.select(getItemDetails().getSelectionKey());
});
This is what I did:
binding.root.setOnClickListener {
tracker?.let {
if(!it.hasSelection()) it.select(binding.currItem?.uuid!!)
binding.setVariable(BR.selected, it.isSelected(binding.currItem?.uuid))
}
binding.executePendingBindings()
}
I implemented a click listener on the view with the check if the selection tracker has any selected entity or not. If it does not have any selection then I explicitly added the selection and the selection tracker gets enabled automatically.
I think this must the solution you were looking for.
The RecyclerView-selection framework is not designed to handle either of the following scenarios!!
A.) An 'enable multiselect' button that toggles the multi-selection on-and-off.
B.) RecyclerView Adapters that utilize more than one row ViewType that must not be selectable.
*If your scope requires A.) or B.) (above), do NOT use the RecyclerView-selection framework.
Instead, remove it and do the following:
1.) Add a Post-Api data field to your list item model:
var isMultiSelected: Boolean = false // User has highlighted this inbox's row for multi-selection
2.) Create a Listener to communicate from the Adapter to the Fragment/Activity:
class MyFragment : MyFragmentListener {
interface MyFragmentListener {
fun myDataModelOnClick(myDataModel: MyDataModel)
fun engageMultiSelect(selectedDataModelCount: Int)
fun disengageMultiSelect()
}
override fun engageMultiSelect(selectedMyModelCount: Int) {
// Update the fragment view for multi-select engaged conditions
selectedCount_textView.text = selectedMyModelCount.toString()
}
override fun disengageMultiSelect() {
// Update the fragment view for multi-select engaged conditions
}
3.) Update your Adapter.onBindViewHolder() to use the new field
holder.enableMultiSelectionButton.setOnClickListener {
myModel.isMultiSelected = true
myModel.engageMultiSelect(getMultiSelectionCount()) // Enable multi-selection
notifyItemChanged(position)
}
holder.rowConstraintLayout.setOnClickListener {
if (getMultiSelectionCount() > 0) { // Is multi-selection engaged?
myModel.isMultiSelected = !myModel.isMultiSelected
if (getMultiSelectionCount() > 0) { // New Item Count may still be larger than 0
myModelListener.engageMultiSelect(getMultiSelectionCount())
} else {
myModelListener.disengageMultiSelect()
}
notifyItemChanged(position)
} else {
// Normal 'tap' when multi-selection is not engaged
}
}
holder.rowConstraintLayout.setOnLongClickListener {
myModel.isMultiSelected = !myModel.isMultiSelected
if (getMultiSelectionCount() > 0) { // Is multi-selection engaged?
myModelListener.engageMultiSelect(getMultiSelectionCount())
} else {
myModelListener.disengageMultiSelect()
}
notifyItemChanged(position)
true
}
if (myModel.isMultiSelected) {
holder.rowConstraintLayout.setBackgroundColor(R.id.coolcolor)
} else {
holder.rowConstraintLayout.setBackgroundColor(R.id.white)
}
Related
I'm writing a small program for a project for Uni and it's basically a library program to manage books anr read/write to JSON file instead of using a database cause it'd be simpler since it's my first proper Java application.
I'm utilizing a TextField to filter a ListView with all the books' titles, and it works, it shows the correct book in the list and updates the corresponding informations on screen when that book is selected, the issue is that even if the program works as intended, it throws an error everytime I update the search field I get an ArrayIndexOutOfBoundsException. The full stack is as follows:
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 7
at javafx.base#19-ea/javafx.collections.transformation.FilteredList.get(FilteredList.java:172)
at com.libraryproject.javalibrary/com.libraryproject.javalibrary.MainViewController.populateDetails(MainViewController.java:200)
at com.libraryproject.javalibrary/com.libraryproject.javalibrary.MainViewController.lambda$initialize$3(MainViewController.java:127)
at javafx.graphics#19-ea/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics#19-ea/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
at javafx.graphics#19-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run$$$capture(InvokeLaterDispatcher.java:96)
at javafx.graphics#19-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java)
at javafx.graphics#19-ea/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics#19-ea/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
After some googling, some people suggested that when updating the GUI from user input, one should do it in the Application Thread, which to be honest I'm not absolutely sure what that means, but anyway I followed the advice and wrapped the functions that would then update the UI variables in a Platform.runLater(() -> {} , but the issue still remains, and it's the stack above, at this point I have absolutely no idea what the problem could be, so, following the stack posted, let's see the code of the parts that are shown:
I'm using a FilteredList to, well, filter the listrView using the search, here's the code managing that and most of the initialize method:
private FilteredList<Book> filteredBooks;
...
...
// inside the initialize method
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// Populate the variable we use throughout the program with the data from the JSON file
filteredBooks = new FilteredList<Book>(handleJSON.getBooks());
// Then update the list view for the first time
populateView(filteredBooks);
...
...
// section of code responsible to check for search changes, when found, fires populateView once more, this time with the variable updated.
searchField.textProperty().addListener((obs, oldText, newText) -> {
filteredBooks.setPredicate(book -> {
if(newText == null || newText.isEmpty() || newText.isBlank()) {
return true;
}
String lowerCaseCompare = newText.toLowerCase();
if(book.getTitle().toLowerCase().contains(lowerCaseCompare)) {
return true;
}
return false;
});
Platform.runLater(() -> populateView(filteredBooks));
}); // Listener
...
...
...
// This one handles the selection of an item in the list, when selected, the fields on the other side of the windows will get populated with the respective data from the book based on the id from the list, since they essentialy share the same FilteredList
listView.getSelectionModel().selectedItemProperty().addListener((obs, oldSel, newSel) -> {
Platform.runLater(() -> {
populateDetails(listView.getSelectionModel().selectedIndexProperty().getValue(), filteredBooks);
editButton.setDisable(false);
});
As you can see I wrapped all of the function that will update the ListView and fields in the window with Platform.runLater, but it doesn't seem to help.
Now for the populateView function that fires the first time the program is opened and everytime there's a change in the searchfiield:
public void populateView(FilteredList<Book> booksList) {
// clears the listview to avoid old elements stacking in the list.
listView.getSelectionModel().clearSelection();
listView.getItems().clear();
ObservableList<String> rawTitles = FXCollections.observableArrayList();
for(Book book: booksList) {
rawTitles.add(book.getTitle());
}
listView.setItems(rawTitles);
} // populateView()
And last but not least the populateDetails function that fills the fields about a book based on the list selection:
public void populateDetails(Integer selectedBookID, FilteredList<Book> books) {
Book currentBook = books.get(selectedBookID);
titleValue.setText(currentBook.getTitle());
authorValue.setText(currentBook.getAuthor());
languageValue.setText(currentBook.getLanguage());
genreValue.setText(currentBook.getGenre());
pagesValue.setText(currentBook.getPages().toString());
yearValue.setText(currentBook.getRelease().toString());
if (currentBook.getAvailable()) {
availableRadio.setSelected(true);
} else {
unavailableRadio.setSelected(true);
}
} // populateDetails
Thats basically I tried to use the runLater in different places just to be sure, I still get the same stack, any idea what could cause this?
The stack trace tells you exactly what the problem is. The ArrayIndexOutOfBoundsException occurs when you call get(..) on a FilteredList with the value -1, which you do on line 200 of MainViewController.java, in the populateDetails(...) method. Looking at your code, this line must be the line
Book currentBook = books.get(selectedBookID);
so selectedBookID must be the culprit, having the value -1.
selectedBookID is a parameter passed to the method, and you call the method from line 127 of MainController.java, in a lambda expression in the initialize() method. (Again, this information is in the stack trace.) The value you pass is
listView.getSelectionModel().selectedIndexProperty().getValue()
The documentation tells you explicitly when this happens:
The selected index is either -1, to represent that there is no selection, or an integer value that is within the range of the underlying data model size.
So your populate details needs to handle the case where nothing is selected (probably by clearing the text fields). I think it's cleaner to listen to the selectedItemProperty() instead of the selectedIndexProperty(), as it directly gives you the selected Book (or null if nothing is selected), and you don't have to retrieve the Book from the list:
public void populateDetails(Book currentBook) {
if (currentBook == null) {
titleValue.setText("");
authorValue.setText("");
languageValue.setText("");
genreValue.setText("");
pagesValue.setText("");
yearValue.setText("");
availableRadio.setSelected(false);
unavailableRadio.setSelected(false);
} else {
titleValue.setText(currentBook.getTitle());
authorValue.setText(currentBook.getAuthor());
languageValue.setText(currentBook.getLanguage());
genreValue.setText(currentBook.getGenre());
pagesValue.setText(currentBook.getPages().toString());
yearValue.setText(currentBook.getRelease().toString());
if (currentBook.getAvailable()) {
availableRadio.setSelected(true);
} else {
unavailableRadio.setSelected(true);
}
}
}
Your code is overkill; there is basically no need for the populateView() method. The filtered list will update its contents when you change the predicate, and notify observers that its content has changed. So you should just set the list view's items list to the filtered list directly. Then your listener for the search field only has to update the predicate, and the list view will automatically update.
Delete the populateView() method and update the initialize() method as:
public void initialize(URL arg0, ResourceBundle arg1) {
// Populate the variable we use throughout the program with the data from the JSON file
filteredBooks = new FilteredList<Book>(handleJSON.getBooks());
listView.setItems(filteredBooks);
// ...
// ...
// section of code responsible to check for search changes, when found, fires populateView once more, this time with the variable updated.
searchField.textProperty().addListener((obs, oldText, newText) -> {
filteredBooks.setPredicate(book -> {
if(newText == null || newText.isEmpty() || newText.isBlank()) {
return true;
}
String lowerCaseCompare = newText.toLowerCase();
return book.getTitle().toLowerCase().contains(lowerCaseCompare)
});
}); // Listener
// ...
// This one handles the selection of an item in the list, when selected, the fields on the other side of the windows will get populated with the respective data from the book based on the id from the list, since they essentialy share the same FilteredList
listView.getSelectionModel().selectedItemProperty().addListener(
(obs, oldSel, newSel) -> populateDetails(newSel)
);
}
Sorry if the code is a bit convoluted, I've been spamming things out trying to fix this for ages.
I create children within a layout and track the number in which they were added, then compare them so we know which sets belong to which exercises. On click of the arrow, the set information should collapse but only the last view of each exercise does.
fully loaded
clicking to hide
all buttons clicked to hide
fun listeners() {
for (i in childCount3) {
val imageButton: TextView = binding.parentLinearLayout.getChildAt(i).weightTxt
val fragment = EditSetFragment()
imageButton.setOnClickListener {
supportFragmentManager.beginTransaction().replace(R.id.parent_linear_layout, fragment).commit()
}
}
// childList = 10 | childList2 = [0,3,6,8] | childList3 = [1,2,4,5,7,9]
var testCount = 0
var testCount2 = 0
for (i in 0 until childCount2.size) {
var matches = ArrayList<Int>()
val imageButton: ImageButton = binding.parentLinearLayout.getChildAt(childCount2[i]).expandCard
Log.d("I ", i.toString())
for (k in 0 until childCount3.size) {
Log.d("k ", k.toString())
Log.d("CHECKER ", binding.parentLinearLayout.getChildAt(childCount2[i]).instanceTxtEx.text.toString() + binding.parentLinearLayout.getChildAt(childCount3[k]).instanceTxt.text).toString()
if(binding.parentLinearLayout.getChildAt(childCount2[i]).instanceTxtEx.text == binding.parentLinearLayout.getChildAt(childCount3[k]).instanceTxt.text) {
matches.add(childCount3[k])
Log.d("MATCH ", i.toString())
imageButton.setOnClickListener{
binding.parentLinearLayout.getChildAt(childCount3[k]).visibility = GONE
}
}
}
}
}
It's a little hard to follow what you're doing, but I think you're going over each item in childCount2, grabbing its expand button, then trying to add a click listener for each child that item has in childCount3?
The problem is you can only set one click listener on a View, so in your loop you're not adding multiple listeners which each hide a button, you're replacing the current listener each time. So it ends up only hiding the last item you set a listener for.
If you're adding these items to the layout programmatically, can't you just store a list of the children of each top-level item? That way you can just set the listener once for each button, and in it just reference your list of its children. Something like this:
val parentsAndChildren = mutableMapOf<View, MutableList<View>>()
// Add your top-level (childCount2) views here, mapped to an empty list
// As you add children (childCount3), grab their parent's list and add the child to it
fun setUpCollapseButtons() {
// iterate over all the parents and set a click listener on their buttons
parentsAndChildren.keys.forEach { parent ->
parent.expandCard.setOnClickListener {
// grab the current list of children for this parent, and do the thing
parentsAndChildren[parent].forEach { it.visibility = GONE }
// could also do { it.visibility = if (it.visibility == VISIBLE) GONE else VISIBLE } for a toggle
}
}
}
I'm not 100% sure how you're referencing things like expandCard when getChildAt just returns a View, but you can use findViewById on each parent when you're setting up the click listeners. Hopefully that gives you the idea - it's much easier to just keep track of the views themselves, instead of having to go digging for them (and doing text comparisons to match them)
If you still want to do it this way, here's how you can set one click listener on each:
// collect all the parent Views
val parents = childCount2.map { i -> binding.parentLinearLayout.getChildAt(i) }
parents.forEach { parent ->
// collect all its children
val children = childCount3.map { j -> binding.parentLinearLayout.getChildAt(j) }
.filter { child -> parent.instanceTxtEx.text == child.instanceTxtEx.text }
// add a single click listener to the parent, make it affect all the children
parent.expandCard.setOnClickListener {
children.forEach { it.visibility = GONE }
}
}
There are fancier ways to collect the parents and children, but hopefully that makes sense - you need to collect them all before you can set the click listener that affects them all
I am using cell factory for listview with checkboxes like:
listView.setCellFactory(CheckBoxListCell.forListView(new Callback < Bean, ObservableValue < Boolean >> () {
#Override
public ObservableValue < Boolean > call(Bean item) {
BooleanProperty observable = new SimpleBooleanProperty();
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
if (!beanChoices.contains(item.toString())) {
beanChoices.add(item.toString());
observable.setValue(true);
//listView.scrollTo(listView.getItems().size() - 1);
}
} else if (wasSelected) {
if (beanChoices.contains(item.toString())) {
beanChoices.remove(item.toString());
observable.setValue(false);
}
}
});
/* [Code] which compares values with bean item string value and select observable to true for that for edit mode
but here the observer not called for beanItem that are under scrollpane of listview. But on scroll it gets called. */
return observable;
}
}));
It works fine but not for all cases.
Case: When I have say more than 10 entries, the scrollpane comes. Say I have beanChoices to be checked that are at 8 or 9 index(you have to scroll to view them). The listener is not called for the items not visible(that are under scrollpane). On Debug, I found that listener is called when I scroll down.
Problem: when I get checked values from beanChoices for above case, it return empty.
Detail: I have beanChoices which I need to make checked for listview items (edit mode). When I update without changing anything. (Assume that the value which is under the scrollpane of listview will be selected and added to beanChoices)
The Callback is used to retrieve the property for the checked state when the item is associated with a cell. The item may be removed from a cell and put in a new one at any time. This is how ListView (and similar controls like TableView) works. CheckBoxListCell simply gets the checked state property every time a new item is associated with the cell.
The return value is also used to set the initial state of the CheckBox. Since you do not properly initialize the property with the correct value the initial state is not preserved.
Also note that it makes little sense to update the value of the property to the new value in the change listener. It happens anyway.
Since BooleanProperty is a wrapper for primitive boolean the possible values are true and false; the ChangeListener only gets called when !Objects.equals(oldValue, newValue) you can be sure that isNowSelected = !wasSelected.
Of course you also need to return the value:
#Override
public ObservableValue < Boolean > call(Bean item) {
final String value = item.toString();
BooleanProperty observable = new SimpleBooleanProperty(beanChoices.contains(value));
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
beanChoices.add(value);
} else {
beanChoices.remove(value);
}
});
return observable;
}
I also recommend using a Collection of Beans instead of relying on the string representation of the objects. toString many not produce unique results and Beans.equals would be the better choice to compare the objects.
I have a RecyclerView item on Java that contains several images that will be visible based on some conditions. However, I noticed if most of the conditions are met, many images will be visible and will be out of bound from its parent layout.
Below illustrates how my intention is for this purpose:
The working code is simple for each image in the RecyclerView, e.g.:
if (iJobType == 1) {
imageView1.setVisibility(View.VISIBLE)
} else {
imageView1.setVisibility(View.GONE)
}
if (iJobType == 2) {
imageView2.setVisibility(View.VISIBLE)
} else {
imageView2.setVisibility(View.GONE)
}
However, I'm not sure what to do to only show 3 images once 3 conditions are met. Let's say iJobType given is 1, 2, 3, 4, 5 ,6; that means 6 images will be shown based on such condition, but right now I only need 3 images to be shown.
I hope it's clear enough, feel free to leave comments below to understand this better. Cheers!
What i would do in this case is:
Firstly I will create a getter setter boolean isConditionmeet in Object class which is used in adapter class.
e.g. list is used in Adapter class as:
private ArrayList<MyObject> list;
and the Object class:
public class MyObject
{
private boolean conditionMeet;
public boolean isconditionMeet()
{
return conditionMeet;
}
public void setconditionMeet(boolean conditionMeet)
{
this.conditionMeet = conditionMeet;
}
}
Now whenever condition will meet to show Item, i will set setconditionMeet(true) for that item and conditionMeet is false for other Items. Lastly update adapter to get changes in UI.
I have several items in a RecyclerView and each item has a long value saved with it. I'm using FastAdapter as the adapter for my RecyclerView.
Suppose there are 7 items in the RecyclerView with the long values: 11122, 12321, -98811, 8870, -88009, 3398, and -22113.
So, what I want to do is, I want to filter the items based on the above given long values using this logic:
if (l <= 1000) {
// show items with long value <=1000
} else if (l > 1000) {
// show items with long value >1000
}
I tried various things, but nothing worked out.
UPDATE 1: Items here are a sort of different data stored in CardView and then shown in RecyclerView. Each card contains different data, one of which are the above given long values. I want to filter the data based on these long values stored in each card based on the logic given above.
Please help me with this issue and suggest some algorithm or code with which I can achieve this.
With the amount of information given I can only suppose l is a foreign selector value which controls the items to be displayed inside the RecyclerView. Comment below if this is not the case, I will try to correct my answer.
I recommend implementing a custom ViewAdapter, sending in the list of items and the selector variable l using respective methods:
public class ItemsAdapter extends
RecyclerView.Adapter<ItemsAdapter.ItemViewHolder> {
private List<Long> mItemList;
private List<Long> mDisplayItems;
private boolean mAboveThousand = true;
public void setItemList(List<Long> list) {
mItemList = list;
updateDisplayItems();
}
public void setSelectionType(boolean aboveThousand) {
mAboveThousand = aboveThousand;
updateDisplayItems();
}
private updateDisplayItems() {
mDisplayItems.clear();
for(Long item: mItemList) {
if(/*check your contition*/) {
mDisplayItems.add(item);
}
}
notifyDataSetChanged(); //important
}
...
// Rest of implementation
}
Also, I have never used FastAdapter, but I suppose there must be some methods to override if you extend its class.
Update
Since, you are facing problems understanding the basics of using a ViewAdapter, I would recommend learning and implementing a custom ViewAdapter before using any library. Here's a extensive tutorial for how to implement ViewAdapter for RecyclerView.
Now, after you have implemented the ViewAdapter you can use my piece of code to filter out cards. Basically, what the code is doing is saving a list of all the required data inside mItemList, while mDisplayList is a list storing the items to be displayed, which is updated every-time mAboveThousand, which stores the user preference of above or below 1000, is set. Now this mDisplayList must be used to inflate data inside the RecyclerView.
Even your very basic code there would work. You can count the number of items in that range and return the number in that range. I suggest you try to do this without FastAdapter because the core concept of parsing the data based on a filter value is rightly perfectly solid. You can iterate the loop and count them, and you can iterate the loop and return the nth item.
If you do want to keep using FastAdapter, it has a built-in filter functionality (see point number 5 in the README of the project. Note that the filter method should be called after withFilterPredicate and not before as shown there).
EDIT - after you pointed out that I misunderstood you before - here is my updated proposed instructions:
You need to resolve the logics of which set you want to display (using the checkboxes in the dialog you mentioned in the comment) and pass that information onto the filter, for example:
boolean displayUnderThreshold = //put the logic here - true if you want <1000
fastAdapter.filter(Boolean.toString(displayUnderThreshold));
And where you set the adapter (before the above line is called) have:
final long threshold = 1000;
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<GRModeClass>() {
#Override
public boolean filter(GRModeClass item, CharSequence constraint) {
boolean displayUnderThreshold = new Boolean(constraint.toString());
return (displayUnderThreshold ^ (item.l<threshold)); //false to remove from list
}
});
Old answer From when I thought you wanted to filter the items according to their ms long values, using an external l long indicator:
In your code, assuming your app does get to the if you mentioned in the question when it should - remove the fastItemAdapter.clear(); and instead of the for loop with the if inside it write
fastItemAdapter.filter(Long.toString(l));
and somewhere before that, preferably where you set the adapter (most likely in the onCreate of MainActivity) add the following:
final long threshold = 1000;
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<GRModeClass>() {
#Override
public boolean filter(GRModeClass item, CharSequence constraint) {
long indicator = new Long(constraint.toString());
return (item.ms<threshold && indicator>=threshold) || (item.ms>=threshold && indicator<threshold) ;
}
});
(Assuming here that GRModeClass is your items' class and that the long ms is the long you referred to that should determine whether the )
I guess your class is like
public Class ListItem {
// .. Some other attributes
public long l;
}
Now I hope you've some function which is called when you're putting a filter in your RecyclerView. Let the function name is toggleFilter.
public void toggleFilter(long l) {
if(l <= 1000) {
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<Item>() {
#Override
public boolean filter(ListItem item, CharSequence constraint) {
if(item.l <= 1000) return true;
else return false;
}
});
} else if (l > 1000) {
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<Item>() {
#Override
public boolean filter(ListItem item, CharSequence constraint) {
if(item.l > 1000) return true;
else return false;
}
});
}
// Finally call notifyDataSetChanged
fastAdapter.notifyDataSetChanged();
}
You can filter while fetching from firebase.
l <= 1000
firebaseDatabase.child(key).orderByChild("long_value_key_in_firebase").endAt(1000);
l > 1000
firebaseDatabase.child(key).orderByChild("long_value_key_in_firebase").startAt(1000);