First of all, I'm using Kotlin in my JavaFX Project.
I'm trying to implement my own JavaFX Node that extends Canvas.
class BrikkCanvas(width: Double, height: Double, private val rows: Int, private val cols: Int) : Canvas(width, height)
I also want to be able to add BrikkCanvas directly in the FXML File like so
<BrikkCanvas fx:id="myCanvas" width="100.0" height="100.0" rows="1" cols="1" />
My class has no default constructor, that's why including it in FXML is not trivial.
I found out, however, that you can implement custom BuilderFactory, so I did:
class BrikkCanvasBuilderFactory : BuilderFactory {
private val defaultBuilderFactory = JavaFXBuilderFactory()
override fun getBuilder(clazz: Class<*>): Builder<*> =
if (clazz == BrikkCanvas::class.java) BrikkCanvasBuilder()
else defaultBuilderFactory.getBuilder(clazz)
private class BrikkCanvasBuilder : Builder<BrikkCanvas> {
var width: Double = 0.0
var height: Double = 0.0
var rows: Int = 0
var cols: Int = 0
override fun build(): BrikkCanvas = BrikkCanvas(width, height, rows, cols)
}
}
In the App class that extends Application I use my BrikkCanvasBuilderFactory like this:
fun startTheGame(playerName: String) {
val loader = FXMLLoader(App::class.java.getResource("game.fxml"), null, BrikkCanvasBuilderFactory())
val scene = Scene(loader.load())
val controller = loader.getController<GameController>()
primaryStage.scene = scene
}
However, when I start the application and click the button that invokes startTheGame, I get the following error:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
...
Caused by: javafx.fxml.LoadException:
/path to project/target/classes/game.fxml:21
at App$Companion.startTheGame(App.kt:18)
...
Caused by: java.lang.IllegalStateException: defaultBuilderFactory.getBuilder(clazz) must not be null
at controller.BrikkCanvasBuilderFactory.getBuilder(BrikkCanvasBuilderFactory.kt:12)
game.fxml:21 = fx:controller="controller.GameController"
App$Companion.startTheGame(App.kt:18) = val scene = Scene(loader.load())
BrikkCanvasBuilderFactory.kt:12 = else defaultBuilderFactory.getBuilder(clazz)
Please note that I haven't even included any <BrikkCanvas ... /> tags in the FXML File yet
I'm pretty sure that I'm missing something really basic that probably has to do with the fact that I'm using Kotlin instead of Java
As I understand it, the BuilderFactory and Builder interfaces are relics of the past (essentially deprecated) and should be avoided in new code—pretend they don't exist. You should instead annotate your constructor parameters with the #NamedArg annotation.
class BrikkCanvas(
#NamedArg("width") width: Double,
#NamedArg("height") height: Double,
#param:NamedArg("rows") private val rows: Int,
#param:NamedArg("cols") private val cols: Int
) : Canvas(width, height)
The FXMLLoader will then be able to tell which FXML attribute corresponds with which parameter, and thus can set the properties via the constructor.
Related
I have a class defined to store configuration data for my application. I want to save the instances of this out to xml and use XStream for this. But I keep getting outofmemory errors when I try to write an instance.
Here is my class definition:
public class Eol_Target_Variable {
String name;
String alias;
long value;
long default_val;
int size;
int scaling;
int div;
Boolean read_access;
Boolean write_access;
public Eol_Target_Variable(String arg_name, String arg_alias, int arg_value, int arg_size, int arg_scaling,int arg_div)
{
name = arg_name;
alias = arg_alias;
value = arg_value;
default_val = 0;
scaling = arg_scaling;
div = arg_div;
size = arg_size;
read_access = true;
write_access = true;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
...etc for all standard getters and setters
Here is my handler for exporting a single object to xml
public void importConfiguration() {
XStream xstream = new XStream(new DomDriver());
Eol_Target_Variable myvar = new Eol_Target_Variable("jamie", "xtracold", 1977, 16, 1, 1);
String myxml = xstream.toXML(myvar);
System.out.print(myxml);
}
Every time I get "Exception in thread "JavaFX Application Thread" java.lang.OutOfMemoryError: Java heap space" thrown. I cannot see why such a simple class would throw the out of memory error. I have managed to output simple String objects using XStream so the library is working, it is just this custom class that seems to cause problems.
I have also tried to increase the heap allocated at startup with the VM arguments -Xms512m -Xmx1024m but that makes no difference.
Thanks
Jamie
Here is the new class declaration
#XStreamAlias("targetVar")
public class Eol_Target_Variable {
String name;
String alias;
long value;
#XStreamAlias("default")
long default_val;
int size;
int scaling;
int div;
#XStreamOmitField
Node node;
#XStreamOmitField
Boolean read_access;
#XStreamOmitField
Boolean write_access;
public Eol_Target_Variable(String arg_name, String arg_alias, int arg_value, int arg_size, int arg_scaling,int arg_div, Node arg_node)
{
name = arg_name;
alias = arg_alias;
value = arg_value;
default_val = 0;
scaling = arg_scaling;
div = arg_div;
size = arg_size;
node = arg_node;
read_access = true;
write_access = true;
}
I also used a different parser as the basic DOMParser never handled the massive amount of data. When I changed to StaxDriver was at least able to see the streams of text in the debug output as XStream traversed the whole scene graph.
XStream xstream = new XStream(new StaxDriver());
xstream.processAnnotations(Eol_Target_Variable.class);
I don't pretend to fully understand why declaring the class inlined causes problems, but I can reason as to why asking XStream to parse a complete Node might cause issues.
If anyone has any experience with XStream and complex data structures, that are declared inside a JavaFX app I would welcome their input.
I'm learning Kotlin, current using Fedora 25 OpenJDK 8 and Kotlin 1.1.
I copied the example from the Kotlin website: https://kotlinlang.org/docs/reference/delegated-properties.html and changed the get operator.
class Example {
var p: String by Delegate()
}
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
// My implementation
return property.getter.call(thisRef) as String
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name} in $thisRef.'")
}
}
Reading the Reflection documentation the getter expects the object instance and no other parameter, but I only achieved the following error.
(Error is abbreviate because it's too big, it's in recursion.)
.
.
.
at info.malk.Example.getP(Delegation.kt)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at kotlin.reflect.jvm.internal.FunctionCaller$Method.callMethod(FunctionCaller.kt:98)
at kotlin.reflect.jvm.internal.FunctionCaller$InstanceMethod.call(FunctionCaller.kt:115)
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:107)
at info.malk.Delegate.getValue(Delegation.kt:32)
at info.malk.Example.getP(Delegation.kt)
.
.
.
Caused by: java.lang.reflect.InvocationTargetException
... 1024 more
Caused by: java.lang.StackOverflowError
... 1024 more
Process finished with exit code 1
Help.
Translation Rule says:
For instance, for the property prop the hidden property prop$delegate is generated, and the code of the accessors(getter/setter) simply delegates to this additional property.
so the kotlin property will dispatch the getter/setter to the delegator. when you get/set the value on a property around in the delegate handlers (getValue/setValue) will result in recursive call.
your Delegate should more like this:
class Delegate<T> {
private var value: T? = null;
// ^--- store the proeprty value internal
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw UninitializedPropertyAccessException();
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value;
}
}
I am using Kotlin TornadoFX to create a browser. When I implement WebEngine setCreatePopupHandler, I get an error:
e: surfing\src\surfing.kt: (76, 13): Modifier 'override' is not
applicable to 'local function'
e: surfing\src\surfing.kt: (76, 13): Expected a value of type WebEngine!
I referenced this Java code using JavaFX:
webEngine.setCreatePopupHandler(
new Callback<PopupFeatures, WebEngine>() {
#Override
public WebEngine call(PopupFeatures config) {
smallView.setFontScale(0.8);
if (!toolBar.getChildren().contains(smallView)) {
toolBar.getChildren().add(smallView);
}
return smallView.getEngine();
}
});
Translated into Kotlin to use TornadoFX:
var wv = webview()
val br = wv.getEngine()
br.setCreatePopupHandler(Callback<PopupFeatures, WebEngine>() {
override fun call(pf: PopupFeatures): WebEngine {
var smallView = webview()
val stage = Stage(StageStyle.UTILITY)
stage.setScene(Scene(smallView))
stage.show()
val engine = smallView.getEngine()
return engine
}
})
I have been searching for a long time on the internet, but I didn't find anything. Please can somebody help me fix this error.
You're almost there :) To create an anonymous class much the same way you do in Java, you need to use the object keyword in front of the class statement:
br.createPopupHandler = object : Callback<PopupFeatures, WebEngine> {
However, Kotlin allows you to turn SAM types into lambdas. You can also utilize the property access pattern and immutable values to clean up the code a little. Here is a the code rewritten and Kotlinified:
val wv = webview()
val br = wv.engine
br.setCreatePopupHandler {
val smallView = webview()
val stage = Stage(StageStyle.UTILITY)
stage.scene = Scene(smallView)
stage.show()
smallView.engine
}
I haven't really evaluated what you're doing here or if that's a good idea, so only take my advice on the syntax :)
Trying to understand how to get classes to play nicely with prototype declarations. I prefer them because of memory saving. I get an error here that types is not defined on MyClass. How do I predefine prototype on classes?
//#flow
class MyClass {
x: number;
constructor(){
this.x = 1
}
}
(MyClass.prototype.types: Object);
MyClass.prototype.types = {};
const x : number = MyClass.prototype.types;
I am using Flex 4 and for whatever reason I cannot get the following code to work which happens inside of a ListEvent handler for a dataGrid:
_tempRule = DataGrid(event.currentTarget).selectedItem as Rule;
Rule is a custom class, and the above code always returns null. The dataprovider for the datagrid is an ArrayCollection. If I try to wrap the above code to make it like the following:
DataGrid(event.currentTarget).selectedItem as Rule
I get this error:
TypeError: Error #1034: Type Coercion failed: cannot convert Object#e15a971 to com.mycompany.arcc.business.Rule
Now I know I have done this before with native Flex classes like Button, etc, but it my case it will not work.
Here is the Rule class:
package com.mycompaany.arcc.business
{
import flash.utils.describeType;
import mx.collections.ArrayCollection;
[Bindable]
public class Rule extends Object
{
public static const RANGE:String = "Range";
public static const SINGLE:String = "Single";
public static const LIST:String = "List";
/*name of the rule*/
private var _name:String;
/*rule group, like a radio group, only 1 rule from a group can be selected*/
private var _group:String;
/*Description of the group for the rule*/
private var _groupDescription:String;
/*Description of the rule*/
private var _description:String;
/*arry of values for this rule, based on the control type*/
private var _values:ArrayCollection;
/*min num of values*/
private var _numValues:int;
/*type of control to build, if range, 2 inputs, single, 1 , list 1 or more*/
private var _type:String;
public function Rule(name:String=null, group:String=null, description:String=null, values:ArrayCollection=null, numValues:int=0, type:String=null)
{
super();
_values = new ArrayCollection();
this._name = name
this._group = group;
this._description = description;
if (values)
{
this._values = values;
}
this._numValues = numValues;
this._type = type;
}
}
}
So what am I missing?
The quick and easy solution was to add the [RemoteClass] metatag at the top my custom class.
I found this solution on Adobe's website, livedocs.adobe.com/flex/3/html/…. It seems that using the native drag and drop capabilities between list-based components causes custom classes to lose their type during copying. Thanks to everyone for the assistance.
the errormessage is the result of an failed casting.
The better way is to use the cast like this,
Rule((event.currentTarget).selectedItem);
In this case, you will receive an exception, when the cast fails and not a null reference
BR
Frank