Fitler JTable of contents of an ArrayList - java

I have a simple library application, and one thing it needs to accomplish is checkout/return books. In that checkout/return window, I have two tables, a "Book List" table, which starts full of books I already have stored, and a "Checkout/Return" table, which starts out empty. The Checkout/Return table is populated by double clicking items in the Book List table, and then depopulated by double clicking items displayed within itself. Double clicking adds or removes the book items to/from a checkout/return ArrayList (which is then displayed in the table).
What I'm looking for is a way to filter out books in the Book List table by the contents of the Checkout/Return ArrayList. (Unable to post images of desired effect at this time).
Is this even possible, and if so, how similar is this to filtering by the contents of a JTextField?
Edit: I've since found this approach: http://pulkitsinghal.blogspot.com/2010/06/multiple-row-filters-for-jtable.html.
RowFilter<TableModel, Object> firstFiler = null;
RowFilter<TableModel, Object> secondFilter = null;
List<RowFilter<TableModel,Object>> filters = new ArrayList<RowFilter<TableModel,Object>>();
RowFilter<TableModel, Object> compoundRowFilter = null;
try {
firstFiler = RowFilter.regexFilter(yourRegexString, columnIndex);
secondFilter = RowFilter.regexFilter(yourRegexString, columnIndex);
filters.add(firstFiler);
filters.add(secondFilter);
compoundRowFilter = RowFilter.andFilter(filters); // you may also choose the OR filter
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(compoundRowFilter);
However, this achieves the opposite affect I want. Rather than filtering out everything that doesn't match the filter input, I want to filter out everything that does match the filter input.

Related

JList setSelectedValue not working

I have a JList in Swing working bad. I list all items from Database into the list with no problem with this code.
My code:
Integer index = null;
DefaultListModel<String> model = new DefaultListModel<String>();
index = DataBase.getIndex1(cbActivity.getSelectedItem().toString());
activities = DataBase.getIndex2(index);
for(MapActivity mapActitivy : activities)
{
model.addElement(mapActivity.getActivity().toString());
}
jList.setModel(model);
But now, I would like to select individual or multiple selection, but nothing I tried works. I tried:
jList.setSelectedValue("Ball", true);
//jList.setSelectedIndex(2);
jList.setSelectionBackground(Color.red);
But nothing happen. Just the list on screen with nothing selected. Single or multiple.
Any help?
Try this:
setSelectedIndex(1); // here use index of items
or if it does not work use below one:
setSelectedItem("ball") // here use name of item.

JavaFX TableView rows not refreshing when list item added

I have two controllers, one main screen that shows a list of parts and a button to open a second controller window to add a new part.
When I add a part, it adds to the arraylist just fine, but the tableview does not update. I have a search field at the top, and if I hit the button it shows me the new item, but nothing I have tried will get the table to refresh and display the new item when it is added.
Here is the MainController
#Override
public void initialize(URL url, ResourceBundle rb) {
if (firstLoad) {
inventory.addPart(new Part(1,"Widget",1.13,5,1,8));
inventory.addPart(new Part(2,"Sprocket",2.88,5,1,8));
inventory.addPart(new Part(3,"Gear",3.46,5,1,8));
firstLoad = false;
}
colPartID.setCellValueFactory(new PropertyValueFactory<>("partID"));
colPartName.setCellValueFactory(new PropertyValueFactory<>("name"));
colPartInvLevel.setCellValueFactory(new PropertyValueFactory<>("inStock"));
colPartPrice.setCellValueFactory(new PropertyValueFactory<>("price"));
tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));
}
inventory class has a static arraylist of parts
private static ArrayList<Part> allParts = new ArrayList<>();
and the addpartcontroller just adds to the arraylist, which works just fine
inv.addPart(new Part(1,"test",2,3.46));
stage.close();
After the stage is closed, the main screen doesn't seem to update at all, the parts table view still has the 3 parts in it
If I leave the search textfield blank and hit the search button, the 4th part shows up
FilteredList<Part> filteredData = new FilteredList<>(FXCollections.observableArrayList(inventory.getAllParts()), p -> true);
String newValue = txtPartSearch.getText();
filteredData.setPredicate(part -> {
if (newValue == null || newValue.isEmpty()) {
return true;
}
String lowerCaseFilter = newValue.toLowerCase();
if (part.getName().toLowerCase().contains(lowerCaseFilter)) {
return true;
}
return Integer.toString(part.getPartID()).equals(lowerCaseFilter);
});
SortedList<Part> sortedData = new SortedList<>(filteredData);
sortedData.comparatorProperty().bind(tblParts.comparatorProperty());
tblParts.setItems(sortedData);
I have tried tblParts.refresh(), I have also tried having the addpartcontroller call a method in maincontroller to set the table before closing, but the table never refreshes unless I call the search method.
EDIT:
Everything works fine if all done within the maincontroller. For instance if I remove the entire search code above and replace it with the following two lines (which execute on the main controller when pressed), then the table on the main controller updates to show the new item right away.
inventory.addPart(new Part(6,"Test",5.23,4,2,8));
tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));
There are a couple things wrong with your code. The most relevant being that you use FXCollections.observableArrayList(Collection). This Javadoc of this method states:
Creates a new observable array list and adds a content of collection col to it.
In other words, it copies the Collection into a new ObservableList that is backed by an ArrayList. Any updates to the original Collection will never even be added to the ObservableList. You should be using FXCollections.observableList(List) if you want the passed List to be the backing List.
The Javadoc for FXCollections.observableList(List) (emphasis mine):
Constructs an ObservableList that is backed by the specified list. Mutation operations on the ObservableList instance will be reported to observers that have registered on that instance.
Note that mutation operations made directly to the underlying list are not reported to observers of any ObservableList that wraps it.
This Javadoc hints at the second issue. Unless you are doing differently in code you haven't posted it appears you add the elements to the ArrayList field (named allParts). Because of this the ObservableList is never aware anything changes and thus no change events are fired. The firing of change events is coded in the ObservableList. If you want to be notified of changes you must only access the list via the ObservableList that wraps the ArrayList.
In this case, your code would still work (when you call tableView.refresh()) if it wasn't for the fact you also wrap your ObservableList in a FilteredList. A FilteredList creates a "view" of the backing ObservableList. If the backing ObservableList never fires any changes the FilteredList is never notified of any changes which means it never knows to update this "view". This means when you add elements to the ArrayList these new elements are "outside the view" (if you were to replace an element that is "inside the view" the change would be visible).
You can see this behavior with the following code:
import java.util.*;
import javafx.collections.*;
import javafx.collections.transformation.*;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
ObservableList<String> obsList = FXCollections.observableList(list);
FilteredList<String> filteredList = new FilteredList<>(obsList);
System.out.println("INITIAL STATE");
System.out.printf("\tList: %s%n", list);
System.out.printf("\tFilteredList: %s%n", filteredList);
list.add("Goodbye");
list.add("World");
System.out.println("AFTER ADDING ELEMENTS");
System.out.printf("\tList: %s%n", list);
System.out.printf("\tFilteredList: %s%n", filteredList);
list.set(0, "Foo");
list.set(1, "Bar");
System.out.println("AFTER REPLACING ELEMENTS");
System.out.printf("\tList: %s%n", list);
System.out.printf("\tFilteredList: %s%n", filteredList);
}
}
Which prints out:
INITIAL STATE
List: [Hello, World]
FilteredList: [Hello, World]
AFTER ADDING ELEMENTS
List: [Hello, World, Goodbye, World]
FilteredList: [Hello, World]
AFTER REPLACING ELEMENTS
List: [Foo, Bar, Goodbye, World]
FilteredList: [Foo, Bar]
Taking all this into account the easiest way to fix your code would be to make allParts an ObservableList. Otherwise you must take care to only use the ObservableList that you created around the ArrayList.
Edit
You also mention that, "If I leave the search textfield blank and hit the search button, the 4th part shows up". I want to address this. Here is your code with explanations why the new Part shows up in the TableView when you hit search:
/*
* I extracted the FXCollections.observableArrayList(Collection) out of the FilteredList
* constructor to more easily see what is going on.
*/
/*
* You create a **new** ObservableList using FXCollections.observableArrayList(Collection). This basically
* creates a *snapshot* of the List returnd by getAllParts() as it currently is. At this point the 4th
* Part is in that returned List. This means the newly created ObservableList will also contian the new
* Part (since observableArrayList(Collection) copies the data). However, the *old* ObservableList that
* was already set on the TableView *does not* contain that 4th Part.
*/
ObservableList<Part> parts = FXCollections.observableArrayList(inventory.getAllParts());
// You create a FilteredList that performs no filtering around "parts". Note
// here that a Predicate that always returns true is equivalent to passing null
// as the Predicate.
FilteredList<Part> filteredData = new FilteredList<>(parts, p -> true);
// Get the search criteria
String newValue = txtPartSearch.getText();
filteredData.setPredicate(part -> {
if (newValue == null || newValue.isEmpty()) {
return true; // don't filter if there is no search criteria
// since the newValue will be null or blank in this
// case no Parts are filtered
}
// filter based on lower-case names and search critiera
String lowerCaseFilter = newValue.toLowerCase();
if (part.getName().toLowerCase().contains(lowerCaseFilter)) {
return true;
}
// else filter by ID
return Integer.toString(part.getPartID()).equals(lowerCaseFilter);
});
// Wrap the FilteredList in a SortedList and bind the comparatorProperty to
// the comparatorProperty of the TableView (allows sorting by column).
SortedList<Part> sortedData = new SortedList<>(filteredData);
sortedData.comparatorProperty().bind(tblParts.comparatorProperty());
// Set the sortedData to the TableView
tblParts.setItems(sortedData);
So, the fundamental reason why when you search you see the new Part show up is because you are creating a new ObservableList every time you search. This new ObservableList has the most recent state of the getAllParts() List. Also, as I already mentioned in the comments, your edit is basically doing the same thing as your sorting code. Since you do:
inventory.addPart(new Part(6,"Test",5.23,4,2,8));
tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));
which adds the Part before creating the ObservableList. Again, FXCollections.observableArrayList(Collection) takes a snapshot of the Collection which, when the method is called here, contains that new Part. If you were to flip the code to:
tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));
inventory.addPart(new Part(6, "Test", 5.23, 4, 2, 8));
then the TableView's items property will not contain the new Part. However, the allParts ArrayList in inventory will.

JavaFX TableView: collect data from visible columns only

I want to put the data from a TableView into a Collection of Maps. Each Map represents a row in the table and contains the columnName-entry-pairs of all visible columns.
Now the tricky part for me is to get the data from the visible columns only.
On the TableView, I could call getItems(), but how could I then check which column is visible?
With getColumns(), I can check which column is visible, but how could I then get the corresponding data?
The easiest thing would be a way to iterate through all visible columns - but I can't find a way to do this.
Can anyone give me a hint or a pointer in the right direction?
You can try this, though I did not test this:
final ObservableList<Map<String, Object>> collection = FXCollections.observableArrayList();
tableView.getItems().forEach(item -> {
final Map<String, Object> itemMap = new HashMap<>();
tableView.getVisibleLeafColumns().forEach(column -> {
itemMap.put(column.getText(), column.getCellObservableValue(item).getValue());
}
collection.add(itemMap);
});

Stuck adding list selection listener to program [duplicate]

This question already has an answer here:
Showing a counter dynamically for JList items highlighted
(1 answer)
Closed 6 years ago.
I asked a similar question yesterday and while I got some direction I am now stuck. I have a program that contains a GUI with two lists which are side-by-side. The first list (which I will refer to as left) contains a number of items which are parsed from an uploaded file.
The other list (I'll call the right list) is empty by default. The user can use some buttons to transfer items between the two lists. At runtime the items in the right list will be kept and the left list will be discarded. Now I have successfully added a counter that shows dynamically the total count of items for each list.
I had an idea yesterday that I want to add another dynamic counter that will show the user how many items are selected in the left list (but haven't been moved yet). The reason for this is that users may have very large lists (300+ items) and need to move a certain amount (like 50).. Having a counter to show how many items they have currently selected will save them a little time.
I understand to do this I need to use a list selection listener that will have a method to set the text of my JLabel. The problem is I can't figure out the method to get the size of the list.
Here is where my list is created.
input = new DefaultListModel();
Here it is populated with an array of items that was filled during a buffered writer based on the upload file.
String[] inputItems = new String[MainWriter.entryDetails.size()];
inputItems = MainWriter.entryDetails.toArray(inputItems);
for(int i = 0; i < inputItems.length; i++){
input.addElement(inputItems[i]);
}
Creating the list:
inputDetails = new JList(output);
inputDetails.setVisibleRowCount(10);
inputDetails.setFixedCellHeight(20);
inputDetails.setFixedCellWidth(400);
inputDetails.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
//Creates a new scroll pane for the inputDetails.
JScrollPane list2 = new JScrollPane(inputDetails);
Here is my attempt at creating the list selection listener..
inputDetails.addListSelectionListener(new ListSelectionListener(){
#Override
public void valueChanged(ListSelectionEvent arg0) {
selectedCount.setText(inputDetails.);
}
});
As you see I'm not sure what the best method would be to set the text to the currently selected items.
I believe that is enough information to illustrate my problem. I want to have the line Selected Items and then dynamically say how many items in the left list the user has highlighted.
Any help would be greatly appreciated.
EDIT:
As suggested I added the following:
inputDetails.addListSelectionListener(new ListSelectionListener(){
#Override
public void valueChanged(ListSelectionEvent arg0) {
String counter = Integer.toString(inputDetails.getSelectedIndices().length);
selectedCount.setText("Currently Selected Transactions: "+counter);
}
});
however the JLabel does not appear to be updating, can someone advise me on the problem?
In the listener, selectedCount.getSelectedIndices().length will tell you how many items are currently selected.

ParseQuery: convert from list of prices in database to strings android

​Hi All,
I am trying to build a scrolling custom listview that displays a list of products ordered by Price ascending. However I just realized I was storing the prices as strings which means $1000.00 comes before $2.01 because it is a character and not a number. I have converted my data to a "Number" on Parse and believe the best type to retrieve it is a double (can anyone comment on that for dollar amounts). The problem is I need to keep it as a number convert it to a string and then pass it to the listview for display on a text field. Initially i had
PPI.setProductprice((String) product.get("Price"));
like this:
// Locate the class table named "Products" in Parse.com
ParseQuery<ParseObject> query = new ParseQuery<ParseObject>(
"Products");
// Locate the column named "Price" in Parse.com and order list
// by ascending
query.orderByAscending("Price");
ob = query.find();
for (ParseObject product : ob) {
// Locate images in PrimaryPhoto column
ParseFile productimage = (ParseFile) product.get("PrimaryPhoto");
ProductPopulation PPI = new ProductPopulation();
PPI.setProductname((String) product.get("Name"));
PPI.setProductbrand((String) product.get("Brand"));
PPI.setProductprice((String) product.get("Price"));
PPI.setProductimage(productimage.getUrl());
productpopulationlist.add(PPI);
I then tried putting it into an array of doubles an iterating through it to convert to strings.
My last attempt which probably doesn't make sense was to change it like this:
PPI.setProductprice((Double) product.getDouble("Price"));
I am fairly knew to Android and any help you can give me would be appreciated.
Thanks in advance.
OK so i do not get the context here but what you can do is save the price as a string and when extracting it you could call Integer.parseInt(String intvalue); on the string to convert the value back to int then you can do all operations you ought to do. you can get a disordered array from the server and arrange it at device level that will save you some time and logic.
I don't know what is the ProductPopulation class you use to populate the list, so I can not say what exactly is the best way in your case, but in general a list can be ordered by means of the Collections.sort() method (see the method documentation).
You could sort the list before you add it to your list view. To sort a list the way you need, you must provide a comparator, than obtains the required values (fields or method results) from the objects that comprise your list, compares the obtained values and returns -1, 0, or 1, depending on the comparison result.
It could look somewhat like this:
for (...) {
ItemClass newObject = new ItemClass(); // new list item
// ...here add the values to the list item...
theList.add(newObject); // add the new item to the list
}
// now sort the list before adding it to the list viewer
Collections.sort(theList, new Comparator<ItemClass>() {
#Override
public int compare(ItemClass o1, ItemClass o2) {
// obtain and compare the values you need
return Double.compare(o1.getDouble(), o1.getDouble());
// you could also do something like
// Double.compare(
// Double.parseDouble(o1.getString()),
// Double.parseDouble(o2.getString()));
// but it would be much slower
}
});
// now add the sorted list to the viewer
listViewer.setList(theList);

Categories