Adding view with constructor arguments to a border

2020-08-01 12:10发布

问题:

I want to add a the same class to different BorderPane´s with different constructor arguments (one true and the other false) but it seems UIComponents cannot have arguments yet giving none crashes the page

Adding the parameter, intelliJ shows me it is attempting to make a comparison?? I have tried adding the Views as VBox´s instead, but then nothing appears, I have also tried an AnchorPane instead of BorderPane but that displays nothing as well.

UPDATE:

class ZoomedOutView : View("ZoomedOutView") { 
    val audioView = find<AudioView>(mapOf(AudioView::playFromFile to false)) 
    //TODO change views to fragments 
    override val root = borderpane {
        bottom = audioLiveView.root
    }
}

View class needing parameter:

class AudioView(var playFromFile: Boolean) : View("AudioView"){ 
    //constructor() : this(playFromFile) //error: cannot access because superclass constructor has been called        
    var audioSensor = AudioSensor()    
    override val root = vbox(10) { 
        vbox { 
            prefWidth = 1600.0 hbox { 
                if (!playFromFile) {
                    dataCollectionToggle = togglebutton {
                    ...
                    audioSensor.captureaudio()
... ...
}

The compile error is as follows (it wants an init method in which hardcoding a Boolean value works but isn't the logic I want):

Caused by: java.lang.InstantiationException: view.AudioLiveView
    at java.lang.Class.newInstance(Class.java:427)
    at tornadofx.FXKt.find(FX.kt:408)
    at app.ExpertView.<init>(a5_ExpertView.kt:51)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.lang.Class.newInstance(Class.java:442)
    at tornadofx.FXKt.find(FX.kt:408)
    at tornadofx.FXKt.find$default(FX.kt:393)
    at tornadofx.UIComponent.replaceWith(Component.kt:899)
    at tornadofx.UIComponent.replaceWith$default(Component.kt:898)
    at app.MenuView$root$1$1$1$5$1.invoke(MenuView.kt:94)
    at app.MenuView$root$1$1$1$5$1.invoke(MenuView.kt:14)
    at tornadofx.ControlsKt$action$4.handle(Controls.kt:515)
    at tornadofx.ControlsKt$action$4.handle(Controls.kt)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.control.MenuItem.fire(MenuItem.java:462)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1405)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(ContextMenuContent.java:1358)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    ... 9 more
Caused by: java.lang.NoSuchMethodException: view.AudioLiveView.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.newInstance(Class.java:412)
    ... 57 more

回答1:

You should absolutely never manually instantiate a View or Fragment. You can however use find with a special argument that includes the parameters that should be passed to the View or Fragment. Keep in mind that a View will only be instantiated once in the current scope, so if you need to open several UIComponents of the same type, initialized with different parameters, make sure they are Fragments.

See the Components section of the guide for more information:

https://github.com/edvin/tornadofx-guide/blob/master/part1/3.%20Components.md

Search for "Passing Parameters to Views" in the document above.

That said, it's almost always better to use scopes to pass information to views. You can read more about these best practices in the guide.

Note that you cannot add constructor parameters to your ui components, since the framework needs a no args constructor to be able to instantiate the class. Parameters are passed using the by param extension, like this:

class AudioView : Fragment("AudioView") {
    val playFromFile: Boolean by param()

    override val root = vbox {
    }
}

Also note that to be able to get multiple instances in the same scope you need to use Fragment, not View, since Views are singletons within a scope.