SettingsPane, SliderEditor (OptionEditor) changing values after creation - javafx

Is it possible to adjust values of an OptionEditor child class after creation?
I am using the SliderEditor code from the example http://docs.gluonhq.com/samples/notes/ to create a SliderEditor.
public class SliderOption extends OptionBase<Number> {
private final int min;
private final int max;
public SliderOption(Node graphic, String caption, String description, String category, IntegerProperty value,
boolean isEditable, int min, int max) {
super(graphic, caption, description, category, (Property<Number>) value, isEditable);
this.min = min;
this.max = max;
}
#Override
public Property<Number> valueProperty() {
return value;
}
#Override
public Optional<Function<Option<Number>, OptionEditor<Number>>> editorFactoryProperty() {
return Optional.of(option -> new SliderEditor(option, min, max));
}
public class SliderEditor implements OptionEditor<Number> {
private final Slider slider;
public SliderEditor(Option<Number> option, int min, int max) {
slider = new Slider(min, max, option.valueProperty().getValue().doubleValue());
slider.setSnapToTicks(true);
slider.setMajorTickUnit(1);
slider.setMinorTickCount(0);
slider.setShowTickLabels(true);
valueProperty().bindBidirectional(option.valueProperty());
}
#Override
public Node getEditor() {
return slider;
}
#Override
public Number getValue() {
return slider.getValue();
}
#Override
public void setValue(Number value) {
slider.setValue(value.doubleValue());
}
#Override
public final Property<Number> valueProperty() {
return (Property<Number>) slider.valueProperty();
}
}
}
Creating the settingsPane and slider:
SettingsPane settingsPane = new SettingsPane();
settingsPane.setSearchBoxVisible(false);
final Option<Number> sliderOption = new SliderOption(MaterialDesignIcon.PERSON.graphic(),
"Text", "Set the text size", null,settings.getProperty() , true, 0, 10);
settingsPane.getOptions().addAll(sliderOption, new DefaultOption(Option.SEPARATOR));
Works great but in a buttonEvent i would like to disable the slider:
Slider slider = (Slider)settingsPane.getOptionEditorFactory().call(sliderOption).getEditor();
slider.setDisable(true);
slider.setValue(10);
Disable does nothing, also tried setmax/setshowticklabels none work except setvalues it works like a charm. It seems like i cannot modify the slider except its value. What am i doing wrong?

This is not working because setDisable is not called on the original Slider but on a new instance which is created by the factory method:
#Override
public Optional<Function<Option<Number>, OptionEditor<Number>>> editorFactoryProperty() {
return Optional.of(option -> new SliderEditor(option, min, max, slider));
}
So instead of creating a new instance each time, you have to return the original instance:
public class SliderOption extends OptionBase<Number> {
private final SliderEditor sliderEditor;
public SliderOption(Node graphic, String caption, String description, String category, IntegerProperty value,
boolean isEditable, int min, int max) {
super(graphic, caption, description, category, value, isEditable);
sliderEditor = new SliderEditor(this, min, max);
}
#Override
public Property<Number> valueProperty() {
return value;
}
#Override
public Optional<Function<Option<Number>, OptionEditor<Number>>> editorFactoryProperty() {
// return Optional.of(option -> new SliderEditor(option, min, max, slider));
return Optional.of(option -> sliderEditor);
}
}
Disable does nothing, also tried setmax/setshowticklabels none work except setvalues it works like a charm.
setValue works because the valueProperty of the new Slider instance is bound bidirectional to the valueProperty of the existing Option instance, which is bound to the original Slider as well.

Related

Android changing order of items run-time in Realm Recyclerview

I need to order the list of items based on a field say starredAt
I am loading the data in the recyclerview from Realm DB using RealmRecyclerView by thorbenprimke
The field changes it value on user's action i.e when user presses star button the item should be moved to top.
For this I am just updating the starredAt field of the object.
The items are already sorted by starredAt so realm loads the updated list but it randomly adds one more item to the recyclerview.
CheatSheet.java
public class CheatSheet extends RealmObject {
#PrimaryKey
private String id;
private RealmList<Item> items;
private String title;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public RealmList<Item> getItems() {
return items;
}
public void setItems(RealmList<Item> items) {
this.items = items;
}
}
Item.java
public class Item extends RealmObject {
#PrimaryKey
private String id;
private String description;
private Date starredAt;
public Item() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getStarredAt() {
return starredAt;
}
public void setStarredAt(Date starredAt) {
this.starredAt = starredAt;
}
}
CheatSheetActivity.java
public class MainActivity extends AppCompatActivity {
RealmRecyclerView revItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setData();
}
private void setData() {
rvItems = (RealmRecyclerView) findViewById(R.id.rev_items);
RealmResults<Item> items = Realm.getDefaultInstance().where(CheatSheet.class)
.equalTo("id", "some-id").findFirst().getItems()
.where()
.findAllSorted("starredAt", Sort.DESCENDING);
ItemRealmListAdapter itemRealmListAdapter =
new ItemRealmListAdapter(this, items,
true, true);
rvItems.setAdapter(itemRealmListAdapter);
}
ItemRealmListAdapter.java
public class ItemRealmListAdapter extends RealmBasedRecyclerViewAdapter<Item,
ItemRealmListAdapter.ItemViewHolder> {
RealmResults<Item> mItems;
public ItemRealmListAdapter(Context context, RealmResults<Item> realmResults,
boolean automaticUpdate, boolean animateResults) {
super(context, realmResults, automaticUpdate, animateResults);
this.mItems = realmResults;
}
#Override
public ItemViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int i) {
return new ItemViewHolder(LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_layout_cs_text, viewGroup, false));
}
public Item getItem(int position) {
return mItems.get(position);
}
#Override
public void onBindRealmViewHolder(ItemViewHolder itemViewHolder, int position) {
itemViewHolder.txtBody.setText(getItem(position).getDescription());
if (getItem(position).getStarredAt() != null) {
itemViewHolder.imvStar.setImageResource(R.drawable.ic_star_yellow);
}
itemViewHolder.imvStar.setOnClickListener(v -> handleStarClick(v,position));
}
private void handleStarClick(View v, int position) {
if (getItem(position).getStarredAt() != null) {
((ImageView) v).setImageResource(R.drawable.ic_star);
CheatSheetStorage.unStarItem("some-id", getItem(position));
} else {
((ImageView) v).setImageResource(R.drawable.ic_star_yellow);
CheatSheetStorage.starItem("some-id", getItem(position));
}
}
public static class ItemViewHolder extends RealmViewHolder {
#Bind(R.id.txt_cheat_sheet)
TextView txtBody;
#Bind(R.id.img_star)
ImageView imvStar;
public ItemViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
CheatSheetStorage.java
public class CheatSheetStorage {
public static void unStarItem(String cheatSheetId, Item item) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
CheatSheet cheatSheet = getCheatSheetById(cheatSheetId);
Item itemDB = cheatSheet.getItems().where().equalTo("id", item.getId()).findFirst();
itemDB.setStarredAt(null);
realm.commitTransaction();
}
public static void starItem(String cheatSheetId, Item item) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
CheatSheet cheatSheet = getCheatSheetById(cheatSheetId);
Item itemDB = cheatSheet.getItems().where().equalTo("id", item.getId()).findFirst();
itemDB.setStarredAt(new Date());
realm.commitTransaction();
}
}
Please refer following screenshots for clearer idea :
Screenshot before starring
Screenshot after starring the sixth item
#Rohan-Peshkar - You will have to provide a animateExtraColumnName value to the adapter. For the animations, the adapter keeps track of the items and since that item's id doesn't change, the list isn't updated. With an additional column (in your case that should be the starredAt column - as long as it is stored as an Integer), the diffing algorithm will detect a change and the order is updated.
For reference: https://github.com/thorbenprimke/realm-recyclerview/blob/2835a543dce20993d8f98a4f773fa0e67132ce52/library/src/main/java/io/realm/RealmBasedRecyclerViewAdapter.java#L177
You can also check out the MainActivity in the example folder. The example changes a row's text from "ABC" to "Updated ABC" and the list recognizes the change because both the primary key and the quote field are used to basically create a composite key for diffing purposes.

JavaFX - Incompatible parameter type with using TreeView.EditEvent in lambda

In a JavaFX TreeView I'm using 'custom' classes which extend TreeItem. This makes me able to edit the items in the TreeView (I can double click them and edit the contents when running the application) but I can't seem to be able to set the .setOnEditCommit() method properly. I was hoping it'd work similar as the function in a tableview but I didn't have any luck yet.
This is my code in my controller in which I try to set the setOnEditCommit() method. In my TreeView called 'trvDivisies' I display football team divisions / competitions and one level lower I display all the teams that are in a certain division.
private void setUpTreeView() {
trvDivisies.setEditable(true);
trvDivisies.setShowRoot(false);
TreeItem<String> root = new TreeItem<>();
for (Divisie d : divisies) {
TreeItem<String> divisieTreeItem = d;
divisieTreeItem.valueProperty().set(d.getNaam());
for (VoetbalTeam vt : d.getVoetbalTeams()) {
TreeItem<String> voetbalTeamTreeItem = vt;
voetbalTeamTreeItem.valueProperty().setValue(vt.getTeamNaam());
divisieTreeItem.getChildren().add(voetbalTeamTreeItem);
}
root.getChildren().add(divisieTreeItem);
}
trvDivisies.setRoot(root);
trvDivisies.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
System.out.println(newValue);
}
});
trvDivisies.setCellFactory(TextFieldTreeCell.forTreeView());
// I get an error at the following line when compiling
trvDivisies.setOnEditCommit((TreeView.EditEvent p) -> {
TreeItem<String> selectedItem = p.getTreeItem();
if (selectedItem instanceof Divisie) {
updateDivisie((Divisie)selectedItem);
} else if (selectedItem instanceof VoetbalTeam) {
updateTeam((VoetbalTeam)selectedItem);
}
});
}
This is what my 'custom' classes look like.
public class Divisie extends TreeItem<String> {
private static int idCount = 0;
private int id;
private String naam;
private List<VoetbalTeam> voetbalTeams;
public int getId() {
return id;
}
public String getNaam() {
return naam;
}
public List<VoetbalTeam> getVoetbalTeams() {
return voetbalTeams;
}
public Divisie(int id, String naam) {
super(naam);
this.id = id;
this.naam = naam;
}
public Divisie(String naam) {
this.id = ++idCount;
this.naam = naam;
}
public void addTeam(VoetbalTeam toBeAdded) {
if (voetbalTeams == null) {
voetbalTeams = new LinkedList<>();
}
voetbalTeams.add(toBeAdded);
}
#Override
public String toString() {
return this.naam;
}
}
Second 'lower level' class
public class VoetbalTeam extends TreeItem<String> {
private static int idCount = 0;
private int id;
private String teamNaam;
private List<Speler> spelers;
public int getId() {
return id;
}
public String getTeamNaam() {
return teamNaam;
}
public List<Speler> getSpelers() {
return this.spelers;
}
public VoetbalTeam(int id, String teamNaam) {
super(teamNaam);
this.id = id;
this.teamNaam = teamNaam;
}
public VoetbalTeam(String teamNaam) {
super(teamNaam);
this.id = ++idCount;
this.teamNaam = teamNaam;
}
public void addSpeler(Speler nieuweSpeler) {
if (spelers == null) {
spelers = new LinkedList<>();
}
this.spelers.add(nieuweSpeler);
}
#Override
public String toString() {
return this.teamNaam;
}
}
When trying to run the application WITH the .setOnEditCommit() method I get an error saying:
Error:(97, 37) java: incompatible types: incompatible parameter types in lambda expression
I was hoping you guys can tell me what I need to change my TreeView.EditEvent lambda to or help me find an easier solution.
For a TreeView<T>, the signature of setOnEditCommit is
void setOnEditCommit(EventHandler<TreeView.EditEvent<T>> value)
Since you have (apparently) a TreeView<String>, you need
trvDivisies.setOnEditCommit((TreeView.EditEvent<String> p) -> {
// ...
});
Or, of course, you can just let the compiler do the work for you:
trvDivisies.setOnEditCommit(p -> {
// ...
});

How to make TableCell editable , so it automatically updates the data class?

I am making a system for a school project , and one part of it is a TableView that is populated with rows using my own data class InventoryData that has properties correspondent to the table columns. I would like to make cells in some columns editable using a TextField, so that when an edit is committed, it will update the InventoryData object's relevant property.
I tried setting TextFieldTableCell.forTableColumn() as the cell factory of the columns. Although, now after committing the edit, the text in the cell will change, I don't think it is changing the property in the InventoryData object. The reason why I think that, is because when I try to edit that cell again ( after already being edited once), the TextField shows the former value ( before the first edit).
Did I do something wrong , or is that normal behavior and I have to implement the commits myself?
Here's the code for InventoryData :
package UILayer.TableData;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import ModelLayer.Product;
public class InventoryData {
// From Product
private Product productObj;
private SimpleIntegerProperty id;
private SimpleStringProperty name;
// Constructor - converts Product obj into InventoryData
public InventoryData(Product product)
{
this.productObj = product;
this.id = new SimpleIntegerProperty(product.getId());
this.name = new SimpleStringProperty(product.getName())
}
// GET & SET
public Product getProduct()
{
return productObj;
}
public int getId() {
return id.get();
}
public void setId(int id) {
this.id.set(id);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
productObj.setName(name);
System.out.println(productObj.getName());
}
}
You need your InventoryData class to use the JavaFX Properties pattern. Specifically it needs property-type accessor methods in order to retrieve the property in the table cells. Without this, the cell value factory just calls the standard getName() or getId() method, and wraps the result in a ReadOnlyStringWrapper (or ReadOnlyIntegerWrapper): the table cell cannot change the values of those wrappers (since they are read only).
public class InventoryData {
// From Product
private Product productObj;
private IntegerProperty id;
private StringProperty name;
// Constructor - converts Product obj into InventoryData
public InventoryData(Product product)
{
this.productObj = product;
this.id = new SimpleIntegerProperty(product.getId());
this.name = new SimpleStringProperty(product.getName())
this.name.addListener((obs, oldName, newName) ->
productObj.setName(newName));
}
// GET & SET
public Product getProduct()
{
return productObj;
}
public IntegerProperty idProperty() {
return id ;
}
public final int getId() {
return idProperty().get();
}
public final void setId(int id) {
idProperty().set(id);
}
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
this.nameProperty().set(name);
// productObj.setName(name);
// System.out.println(productObj.getName());
}
}

How to get at Apache Wicket PropertyColumn data to dynamically change CSS class based on content?

I have an Apache Wicket page that has a DataTable with a column that needs to show the statuses, Red, Yellow, Green. If the content of the column is Red, I want to change the CSS class to red-status, if yellow yellow-status, else green-status. I can't seem to get at the data in the way you can from a clickable property column. How do you get at the data in a PropertyColumn, or is there another way to do this in a DataTable? Thank you!
UPDATE
Thank you, Martin. Here's what I came up with:
#Override
public void populateItem(Item<ICellPopulator<T>> cellItem, String componentId, final IModel<T> rowModel) {
Label label = new Label(componentId, getDataModel(rowModel));
cellItem.add(label);
LOGGER.debug("populateItem: label DefaultModelObject: {}", (String) label.getDefaultModelObject());
label.add(new AttributeModifier("class", new AbstractReadOnlyModel<String>() {
private static final long serialVersionUID = 1L;
ProcessingTime processingTime = (ProcessingTime) rowModel.getObject();
#Override
public String getObject() {
String cssClass = null;
if (StringUtils.equals("Red", processingTime.getStatus())) {
cssClass = "red-status";
} else if (StringUtils.equals("Yellow", processingTime.getStatus())) {
cssClass = "yellow-status";
} else if (StringUtils.equals("Green", processingTime.getStatus())) {
cssClass = "green-status";
} else {
cssClass = "process-status";
}
return cssClass;
}
}));
}
First thing first, look at the populateItem of PropertyColumn, how does the implementation looks like, in Wicket 6 (similar like other versions) it is:
public class PropertyColumn<T, S> extends AbstractColumn<T, S> implements IExportableColumn<T, S, Object>
...
#Override
public void populateItem(final Item<ICellPopulator<T>> item, final String componentId,
final IModel<T> rowModel)
{
item.add(new Label(componentId, createLabelModel(rowModel)));
}
...
}
You have to modify the inner component that is create as the label of the column.
First method: create your own component (also your component is able to contain its own mechanism of creation css class or style instead of adding an AttributeModifier here):
#Override
public void populateItem(final Item<ICellPopulator<T>> item, final String componentId,
final IModel<T> rowModel)
{
super.populateItem(item, componentId, rowModel);
MarkupContainer c = item.get(componentId);
c.add(new AttributeModifier("class", new AbstractReadonlyModel<String>() {
private static final long serialVersionUID = 1L;
#Override
public String getObject() {
// some logic how to which css you want to apply
return "MY-CSS-CLASS";
}
}));
}
or you can let Wicket to create the Label component itself and you just add an AttributeModifier:
#Override
public void populateItem(final Item<ICellPopulator<T>> item, final String componentId, final IModel<T> rowModel)
{
super.populateItem(item, componentId, rowModel);
Label l = new Label(componentId, createLabelModel(rowModel));
item.add(l);
l.add(new AttributeModifier("class", new AbstractReadonlyModel<String>() {
private static final long serialVersionUID = 1L;
#Override
public String getObject() {
// some logic how to which css you want to apply
return "MY-CSS-CLASS";
}
}));
}
NOTE: the method 'createLabelModel' is deprecated in Wicket 6, rather to use 'getDataModel' instead.

JavaFX Row Not Updating

There is a very similar question to this already, but mine's a bit different. I am using properties and on observable list to change it, it won't update.
Original question is here.
So when I am transferring rows between tables, like this:
The first row would appear, but when adding more than one would cause the ones after the first row to not update, like this:
They only reappear when I move around the columns though.
//Loot identification
TableColumn lootIdentCol = new TableColumn<>("Identification");
TableColumn<ItemDef, Integer> lootIDCol = new TableColumn<>("ID");
lootIDCol.setCellValueFactory(
new PropertyValueFactory<ItemDef, Integer>("id"));
TableColumn<ItemDef, String> lootNameCol = new TableColumn<>("Name");
lootNameCol.setCellValueFactory(
new PropertyValueFactory<ItemDef, String>("name"));
lootIdentCol.getColumns().addAll(lootNameCol, lootIDCol);
//Loot price
TableColumn<ItemDef, Integer> lootPriceCol = new TableColumn<>("Price");
lootPriceCol.setCellValueFactory(
new PropertyValueFactory<ItemDef, Integer>("price"));
//To loot items table
toLootItemsTableView.getColumns().addAll(lootIdentCol, lootPriceCol);
grid.add(toLootItemsTableView, 0, 1);
//Lootable items table
lootableItemsTableView.getColumns().addAll(lootIdentCol, lootPriceCol);
grid.add(lootableItemsTableView, 2, 1);
toLootItemsTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
lootableItemsTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
lootableItemsTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
toLootItemsTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
lootableTableList.add(new ItemDef("Ab", 141, false, false));
lootableTableList.add(new ItemDef("Ac", 25, false, false));
lootableTableList.add(new ItemDef("AD", 262, false, false));
AddRemoveButtons<ItemDef> addRemoveLootButtons = new AddRemoveButtons<>(
lootableTableList, lootableItemsTableView.getSelectionModel(),
toLootTableList, toLootItemsTableView.getSelectionModel()
);
Code for AddRemoveButtons:
private final ObservableList<E> fromList;
private final ObservableList<E> toList;
public AddRemoveButtons(final ObservableList<E> fromList, final SelectionModel<E> from,
final ObservableList<E> toList, final SelectionModel<E> to) {
this.fromList = fromList;
this.toList = toList;
setAlignment(Pos.CENTER);
setPadding(new Insets(5, 5, 5, 5));
setSpacing(15);
ObservableList<Node> children = getChildren();
Button moveInto = new Button("Add");
moveInto.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (from instanceof MultipleSelectionModel) {
MultipleSelectionModel<E> multipleFrom = (MultipleSelectionModel<E>) from;
ObservableList<Integer> selectedIndices = multipleFrom.getSelectedIndices();
for (int i : selectedIndices)
transfer(i, true);
} else
transfer(from.getSelectedIndex(), true);
}
});
Button delete = new Button("Del");
delete.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (to instanceof MultipleSelectionModel) {
MultipleSelectionModel<E> multipleFrom = (MultipleSelectionModel<E>) to;
ObservableList<Integer> selectedIndices = multipleFrom.getSelectedIndices();
for (int i : selectedIndices)
transfer(i, false);
} else
transfer(to.getSelectedIndex(), false);
}
});
children.addAll(moveInto, delete);
}
private void transfer(int index, boolean forward) {
if (forward)
toList.add(fromList.remove(index));
else
fromList.add(toList.remove(index));
}
ItemDef which implements Identifiable, Serializable, Comparable:
private final String name;
private final int id;
private final boolean members;
private final boolean stackable;
private int price;
public ItemDef(JSONObject jsonObject) {
this(
(String) jsonObject.get("name"),
Integer.parseInt((String) jsonObject.get("id")),
Boolean.parseBoolean((String) jsonObject.get("members")),
Boolean.parseBoolean((String) jsonObject.get("stackable"))
);
}
public ItemDef(String name, int id, boolean members, boolean stackable) {
this.name = name;
this.id = id;
this.members = members;
this.stackable = stackable;
price = -1;
}
public String getName() {
return name;
}
#Override
public int getId() {
return id;
}
public boolean isMembers() {
return members;
}
public boolean isStackable() {
return stackable;
}
public int getPrice() {
return price != -1 ? price : updatePrice();
}
//Other methods not relevant
Figured out why it kept doing that.
You just can't have the same TableColumn being referenced on multiple tables.
You should not share columns in multiple tables if you want data to update in multiple tables share the data set between them not the columns.

Resources