Yes, the problem is very similar to an old question asked a few years ago. In fact, my sample code is based on that one, too. I also tried the answer given there but it didn't work.
Here is the sample code:
package com.example.bindingssize;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.IntegerBinding;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class HelloApplication extends Application {
private IntegerBinding sizeBinding;
private BooleanBinding isEven;
#Override
public void start(Stage primaryStage) {
final ObservableList<String> list = FXCollections.observableArrayList();
primaryStage.setTitle("Demonstrator");
// Button
Button addButton = new Button();
addButton.setText("Add Element");
addButton.setOnAction(event -> list.add("TEST"));
// ListView
ListView<String> lv = new ListView<>();
lv.setItems(list);
// Add elements to root
VBox root = new VBox();
root.getChildren().add(addButton);
root.getChildren().add(lv);
// Show scene
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
sizeBinding = Bindings.size(list);
isEven = Bindings.createBooleanBinding(() -> {
System.out.println(sizeBinding.get()); // key line 1 of 2
System.out.println("inside isEven");
return list.size() % 2 == 0;
}, sizeBinding);
// key line 2 of 2
isEven.addListener((obs, o, n) -> System.out.println(isEven.get() ? "Even" : "Odd"));
}
public static void main(String[] args) {
launch();
}
}
The above code works. Each time the add button is clicked, the print statements inside 'isEven' print as expected. However, this behavior completely depends on the 2 key lines. The print statement won't print anything if one or both of the key lines is missing. Here I made everything a field of the class, so they shouldn't be garbage collected. So what's the reason of this behavior?
This is JavaFX 11.0.2, AdoptOpenJDK-11.0.11+9, on macOS Catalina.
Bindings Are Lazy
In JavaFX, a binding has a "valid" state. When the value is potentially changed while the binding is in a valid state then the binding fires an invalidation event. But if the binding is not currently valid then no invalidation event will be fired. The way to "validate" a binding is to query its value. This setup allows a binding to compute its value only when that value is needed.
Depending On Bindings
Your isEven binding is dependent on the sizeBinding binding. This is implemented by having the isEven binding observe the sizeBinding binding via an InvalidationListener. As noted above, invalidation events are only fired when a binding goes from a valid state to an invalid state.
The problem is, except for your println(sizeBinding.get()) call, you never query the value of sizeBinding. That means you never validate the binding, and that means the isEven binding never knows to recompute its value. If you had:
isEven = Bindings.createBooleanBinding(() -> sizeBinding.get() % 2 == 0, sizeBinding);
Then it would work. Notice it queries the sizeBinding value.
Change Listeners
If a ChangeListener is added to a binding then the value is computed eagerly. The reason for this is simple: The listener is passed both the old and new values. Without the ChangeListener you add to the isEven binding, the binding remains lazy. And since you never validate the isEven binding it never needs to (re)compute its value.
With the ChangeListener added, however, the value is (re)computed eagerly. But note the isEven binding will only recompute its value if it knows that value has changed. In other words, you still have to make sure the sizeBinding binding is validated when appropriate.
Related
I have TableColumn with CheckBox. I have to do this listener and I wonder why the listener doubles after every click.
selectedColumn.setCellFactory(column -> new CheckBoxTableCell<>());
selectedColumn.setCellValueFactory(cellData -> {
Dir dir = cellData.getValue();
BooleanProperty property = dir.isSelectedProperty();
property.addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) ->{
System.out.println(newValue);
});
return property;
});
First click in checkbox i row return:
True
True
Second unselect return:
False
False
False
False
Thrid select return:
True
True
True
True
True
True
why ? :)
This is happening because controls like TableView (and ListView etc) virtualizes its contents. From TableView Javadoc:
The TableView control is designed to visualize an unlimited number of
rows of data, broken out into columns.
The TableView may hold a large number of items. However, on the screen, you could probably see 10-30 rows (each corresponding to a single item) at any point in time.
Without virtualization, if you have 1 million items, it would create 1 million TableRow objects (we haven't talked about TableCell), with each TableRow maintaining all the states/values. This takes a lot of memory and processing power.
On the other hand, with virtualization, if your View can only display 10 rows, TableView will only create, for example, 12 TableRow instances. As you scroll through the list, some of the items disappear from your sight. These TableRow instances are immediately reused for items that enters your sight.
This is why setCellFactory() and setCellValueFactory() methods are of Callback type. Each time a row was reused, it will call this callback object to update the TableCell. This is why your listener is being added repeatedly, causing this.
Depending on what you need, it may be possible to add a ListChangedListener to the list of items.
ObservableList<Dir> list = FXCollections.observableArrayList(item -> new javafx.beans.Observable[] {item.isSelectedProperty()});
list.addAll(DirList.getDirList());
dirList.setItems(list);
list.addListener(new ListChangeListener<Dir>() {
#Override public void onChanged(javafx.collections.ListChangeListener.Change<? extends Dir> c) {
while (c.next()) {
if (c.wasUpdated()) {
// Do something
}
}
}
});
I see you have a model named Dir with a BooleanProperty named selected
You can simply let the cellValueFactory just:
selectedColumn.setCellValueFactory(cellData -> cellData.getValue().isSelectedProperty());
This would update the model's property if you check/un-check the CheckBox in the table cell.
If you use this selected/deselected state of the checkbox, you can use the model, and add the listener there, you will get the same result. Then the listener will be added just once.
You should have instances of Dir created then you can do the following for ex. in initialize:
dir1.selectedProperty().addListener((observable, oldValue, newValue) -> {
dir2.selectedProperty().set(oldValue);
});
Or anything you want, then you are sure that the listener is added just once.
As #Jai mentioned, the cell data is used many time and every time the CallBack is called, the listener is added to the property, so don't use it inside that method if you want to avoid adding the listener many times to the property.
I'm trying to make an uranium ingot that gives players that hold it in their inventory a wither effect. I got some tips from the minecraft forums, they told me to do to make my item give me the wither effect.
Re: 1.10.2 Item has wither « Reply #2 on: Today at 02:29:58 am » QuoteThank You Create a class that extends Item and overrides
Item#onUpdate.
In your override, check if the entityIn argument is an instance of EntityLivingBase. If it is, cast it to EntityLivingBase and call EntityLivingBase#isPotionActive to check if it has the MobEffects.WITHER effect active. If it doesn't, create a PotionEffect and call EntityLivingBase#addPotionEffect to add it.
My Question
Create and register an instance of this class instead of Item.
The last line is what im confused on.
Here is the class i made that he told me to do. Also please inform me if i didnt do something else right in this class
package item;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.MobEffects;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.potion.PotionEffect;
import net.minecraft.world.World;
public class UraniumIngotEffect extends Item{
#Override
public void onUpdate(ItemStack stack, World worldIn, Entity entityIn, int itemSlot, boolean isSelected) {
if(entityIn instanceof EntityLivingBase){
Object EntityLivingBase = ((EntityLivingBase) entityIn).isPotionActive(MobEffects.WITHER);
}else{
Object PotionEffect = new PotionEffect(MobEffects.WITHER);
}
super.onUpdate(stack, worldIn, entityIn, itemSlot, isSelected);
}
}
You need to make the item object in your mod hold the onUpdate method.
This means:
have a class that extends Item(your uranium ingot)
Register the item in the item loader
Item myurnanium = new UraniumIngot();
GameRegistry.register(myuranium);
and of course make the proper json files so the item will render properly.
I suggest you read:
http://bedrockminer.jimdo.com/modding-tutorials/basic-modding-1-8/first-item/
I am implementing drag and drag between two tree views. When a treeItem is dropped onto another treeView of treeItem a line connection is established between the two treeItems. This is working fine , but to have a connection initially without a drag and drop events is problem to me.
I am using treeCell for the drag and drop events.
final var treeCells = treeView.lookupAll( ".tree-cell" );
final var cells = new ArrayList<>( treeCells );
final var row = treeView.getRow( n );
final var node = cells.get( row );
if( node instanceof TreeCell ) {
#SuppressWarnings("rawtypes")
final var cell = (TreeCell) node;
System.out.println( "TREE CELL: " + cell );
}
As I thought, this turns out to be pretty difficult.
There is, by design, no way to get the TreeCell belonging to a given TreeItem. This is because of the "virtualization" design of the TreeView: a minimal number of TreeCells are created and these are updated with new TreeItems as required. Thus there typically won't be a TreeCell for every TreeItem, and the TreeItem represented by a given TreeCell may change at any point.
To make this work, first create an observable collection storing all the connections between the trees (e.g. an ObservableSet should work well). Represent the connections by some class that exposes start and end points which can be used for the lines.
Create custom cell factories for the trees. The cells they return should:
observe the item they are representing. If the item changes to one that is at an end of one or more connections, then the appropriate point on those connections should be bound to the appropriate transform of the coordinates of the cell.
If the item changes from one that is at the end of one or more connections, then unbind the appropriate end from the cell coordinates
observe the observable collection of connections. If one is added for which this cell's item is one end, then bind the coordinates as above
Note that when you bind the coordinates, you need to take into account the fact that the cells may move (e.g. via scrolling or via other changes in GUI layout). You also need to transform the coordinates from the cell's own coordinate system into the coordinate system of whichever pane is holding the connections (obviously, if these are connecting one tree to another, it must be some common scene graph ancestor of both trees).
And finally, you need some housekeeping. The connections need to make sure they either become invisible, or are removed from the scene if they are no longer bound at one or more ends.
I created an example. I just created some simple controls for generating the connections, but you could easily do this with drag and drop instead. The class encapsulating the view of the connection is AssignmentView; it uses Assignment to represent the actual data that is connected. The ConnectedTrees class is the main application and most of the interesting controller-type work is in there. The remaining classes are just data representation. The example is all Java 8; I think it would be much uglier in JavaFX 2.2.
This solution uses recursion calls to traverse the nodes tree of the tree view.
Normally this recursion shouldn't be dangerous as there is only limited nodes number (TreeCell's instances are reused by TreeView):
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import java.util.List;
public class TreeViewExplore {
/**
* Returns a cell for the tree item given. Or null.
*
* #param treeItem tree item
* #param treeView tree view
* #return tree cell
*/
public static TreeCell findCellByItem(TreeItem treeItem, TreeView treeView) {
return recursiveFindCellByItem(treeItem, treeView);
}
private static TreeCell recursiveFindCellByItem(TreeItem treeItem, Node node) {
if (node.getStyleClass().contains("tree-cell")
&& TreeCell.class.isAssignableFrom(node.getClass())
&& ((TreeCell) node).getTreeItem() == treeItem)
return (TreeCell) node;
if (!Parent.class.isAssignableFrom(node.getClass()))
return null;
List<Node> nodes = ((Parent) node).getChildrenUnmodifiable();
if (nodes == null) return null;
for (Node n : nodes) {
TreeCell cell = recursiveFindCellByItem(treeItem, n);
if (cell != null) return cell;
}
return null;
}
}
Usage:
// TreeItem treeItem = ...
TreeCell cell = TreeViewExplore.findCellByItem(treeItem, treeView);
// Check result:
System.out.println(
cell == null ? "cell is null" :
"(cell.getTreeItem() == treeItem) = "
+ (cell.getTreeItem() == treeItem));
Yet another solution using lookupAll() method. It is only for example here as it looks non very efficient for me because this method collects all nodes with CSS-selector given (and traverses all over the tree in any case):
public static TreeCell findCellByItem(TreeItem treeItem, TreeView treeView) {
return (TreeCell) treeView.lookupAll(".tree-cell").stream()
.filter(n -> ((TreeCell) n).getTreeItem() == treeItem)
.findFirst()
.orElse(null);
}
I've got 2 files, my Application and a custom component.
In my component I have a httpservice and a string named _requestUrl that is bindable. The httpservice uses this.
<mx:HTTPService id="srv"
url="{_requestUrl}"
result="parseHttpResult(event)"
resultFormat="xml"
method="GET"
useProxy="false">
In my application file I make an instance of my component in the onCreationComplete function.
In this function if I say
mycomponent._urlRequest ="http://example.com" the httpservice throws a null url error but if I say mycomponent.srv.url="http://example.com" it works fine.
Why is this?
EDIT:
<mx:Script>
import mx.events.FlexEvent;
import components.custom
private var comp:custom= new custom()
private var comp:custom= new custom()
public function setVars(event:FlexEvent):void
{
comp._requestUrl = "http://example.com"
comp.setVars(event)
pform.addChild(comp)
}
//creationComplete="setVars(event)"
</mx:Script>
Because when components are initialized, your _requestUrl is null in the beginning anyway, thats why you get this error. And your url of srv is bound to null value on initialization.
Flex creates components in phases, so if you set variable in creationComplete etc, creationcomplete is called after it has completely created the components, it is called after few millseconds of the initialization of class.
So at time of initialization, by default everything is null except you initialize it inline init expression as below
// this will not be null...
var myUrl:String = "my url";
// this will be null
var myUrl2:String;
// binding above value may give null exception
// because it is null at time of initialization
Even to me first time it was confusing but in Flex's context, Initialized event is called before "CreationComplete" and in normal programming context, we think that we create and initialize object later.
In your example, binding starts working even before "creationComplete" is called that causes it to report null pointer exception, so before this event, your object's property is any way null right.
I'm very new to flash and actionscript 3. I've been reading a lot about it and this is also my first aprouch on object oriented programming.
So far, I created an application with a login button, that's all. However, I would like to know what kind of things I am doing wrong or should be doing different (or better). I am using Adobe Flex Builder 3.
The main actionscript file is Client2.as:
package
{
//import required libraries
import flash.display.Sprite;
//set project properties
[SWF(width="800", height="600", frameRate="31", backgroundColor="#C0C0C0")]
//launch main class
public class Client2 extends Sprite
{
public function Client2() { //the constructor
trace("Client launched.");
var loginGui:LoginInterface = new LoginInterface(); //load the login interface object
loginGui.init(); //initialize the login interface (load it)
addChild(loginGui); //add login gui to the display tree
}
}
}
It is loading the login interface object. Is that a good thing, and am I doing it the right way?
Then there's the LoginInterface.as class file:
package
{
//import required libraries
import flash.display.Sprite;
//the LoginInterface class
public class LoginInterface extends Sprite
{
public function LoginInterface() //the constructor
{
trace("LoginInterface object loaded.");
}
public function init():void //initialize the login interface (load it)
{
trace("LoginInterface init method was called.");
var loginButton:CustomButton = new CustomButton(300, 300, 100, 30, 3, 18, "Login!"); //create a new custom button
addChild(loginButton); //add the custom button to the display tree
}
}
}
What about that? Any comments? To make the creation of simple buttons a bit easier, I then created another class file called CustomButton.as -->
package
{
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
public class CustomButton extends Sprite
{
public function CustomButton(xLoc:int, yLoc:int, width:int, height:int, iLabelOffset:int, fontsize:uint, label:String)
{
//create new simple button instance
var myButton:SimpleButton = new SimpleButton();
//create the look of the states
var normal:Sprite = new Sprite();
normal.graphics.lineStyle(1, 0x000000);
normal.graphics.beginFill(0x6D7B8D);
normal.graphics.drawRect(xLoc, yLoc, width, height);
//the mouseover sprite
var over:Sprite = new Sprite();
over.graphics.lineStyle(1, 0x000000);
over.graphics.beginFill(0x616D7E);
over.graphics.drawRect(xLoc, yLoc, width, height);
// assign the sprites
myButton.upState = normal;
myButton.downState = normal;
myButton.hitTestState = normal;
myButton.overState = over;
//add the button to the display tree
addChild(myButton);
//create button label
var tText:TextField = new TextField();
tText.mouseEnabled = false,
tText.x = xLoc;
tText.y = yLoc + iLabelOffset;
tText.width = width;
tText.selectable = false
var Format:TextFormat = new TextFormat();
Format.font = "Arial";
Format.color = 0x000000;
Format.size = fontsize;
Format.bold = false;
Format.align = TextFormatAlign.CENTER;
tText.defaultTextFormat = Format;
tText.text = label;
addChild(tText)
}
}
}
Is there anything to comment on this? I am sure that I'm doing a lot of things wrong, maybe I didn't really get the whole object oriented thing? Also, I have a bad feeling about the way I am using the "extends ..." after a class declaration, mainly because I'm just using Sprite all the time and don't really understand why or what it does (having trouble finding out on the internet aswell). Another thing I am unsure about is the naming of variables in AS3. Should I really be using names such as xLoc, or iLabelOffset? I think I am not being very consistent in my variable naming atleast?
I hope someone can give me a push to a better track than the one I am on now, as I am sure that I should improve my AS3 coding before I continue working on this beast.
Thanks a lot.
My opinion:
A class called Client2 is probably a bad naming choice. Client2 isn't telling me much. How much will it tell you in a year's time?
In CustomButton, initialization is taken care of in the constructor. In LoginInterface, using an instance of the class requires an explicit call to init(). Easy to forget and unnecessary. Unless there's a good reason not to, call init from the constructor.
What does iLabelOffset mean? better to use a less confusing name in a parameter list.
The parameter list of the CustomButton constructor is pretty long. It's not necessary to pass in the x and y. Sprite has an x and y property already, so put everything back to a zero offset and manipulate the x and y properties of the CustomButton once it's constructed.
Of the remaining parameters to the CustomButton constructor, consider reordering them so that you can provide default parameters (which can only go at the end of the parameter list). labelOffset and fontSize seem like good candidates.
Keep function size small by removing repeated code. Create a function to create the button state Sprites that takes a color in its parameters (or better yet, move this functionality into a new type of Sprite derived class), and also add a createLabel function so that you can move that code out of the constructor. Your code will become easier to read and maintain if you try to keep function size small. It also means you have to write less comments ;-)
Spender hit the nail on the head. Those are definitely the issues that are raised when I looked over your code. The things he mentioned are not nessesarly Actionscript issues, (issue's not quite the right word, perhaps "areas to note"), these are issues general to all programing languages. Descriptive naming for example is extremely important.
There are few books that focus on this side of programming, and even fewer that do it well. I would highly recommend picking up the following two books if you want to grow more in this area, (I'll recommend it even if you don't want too :)
Code Complete
The pragmatic programmer
There both books that every programmer should read, so check them out. You're code from an Actionscript point of view is fine, but that's just syntax. It's important to note that these skill will never develop unless you actually write code, so by all means "continue working on this beast" and the rest will follow suit.
Just as a matter of style, I like to declare my variables outside of the constructor. It helps me to feel that I won't have any surprises with public vs private or scope. Also notice the added white space, which can improve readability.
public class CustomButton extends Sprite
{
private var myButton:SimpleButton;
private var normal:Sprite;
private var over:Sprite;
// etc ...
public function CustomButton(xLoc:int, yLoc:int, width:int, height:int, iLabelOffset:int, fontsize:uint, label:String)
{
//create new simple button instance
myButton = new SimpleButton();
//create the look of the states
normal = new Sprite();
normal.graphics.lineStyle(1, 0x000000);
normal.graphics.beginFill(0x6D7B8D);
normal.graphics.drawRect(xLoc, yLoc, width, height);
//the mouseover sprite
over = new Sprite();
over.graphics.lineStyle(1, 0x000000);
over.graphics.beginFill(0x616D7E);
over.graphics.drawRect(xLoc, yLoc, width, height);
// etc ...