Feel free to contribute and help streamline a pragmatic UI stack with Kotlin, RxJava, and JavaFX. Speaking of stacks, this project may be used in conjunction with TornadoFX and RxKotlin. Please make sure no extension naming conventions conflict with these two other libraries :)
Features
RxKotlinFX is the convergence of interoperability between RxJava, JavaFX, and Kotlin.
RxKotlinFX contains Kotlin extensions to RxJavaFX as well as additional Observable operators specific to JavaFX. It also is in exporatory stages to add helpful Node extension functions that return Observables. This exploration is inspired by the JavaFX/Kotlin interop project TornadoFX. Where TornadoFX handles layouts, node extensions, DI, and other JavaFX/Kotlin interoperations, this library will seek to integrate RxJava with JavaFX in the same spirit using Kotlin. The vision is to add useful extensions that put Observable streams as properties and functions on JavaFX components, especially where ObservableValue properties are not readily available.
RxJavaFX Extensions
The core API implements RxJavaFX static factories as extension functions.
Target Type
Extension Function
Description
Observable<T>
toBinding()
Subscribes the Observable<T> to a JavaFX Binding implementation. Calling dispose() will unsubscribe from the Observable<T>
Observable<T>
toLazyBinding()
Subscribes the Observable<T> to a lazy JavaFX Binding implementation, delaying subscription until a value is needed. Calling dispose() will unsubscribe from the Observable<T>
Property<T>
bind(observable: Observable<T>)
Binds a Property<T> to the emissions of an Observable<T>, and returns the Binding
Binding<T>
addTo(compositeBinding: CompositeBinding)
Adds the Binding to a CompositeBinding, and returns the Binding
ObservableValue<T>
toObservable()
Turns a JavaFX ObservableValue<T> into an RxJava Observable<T> that emits the latest value
ObservableValue<T>
toObservableChanges()
Turns a JavaFX ObservableValue<T> into an RxJava Observable<Change<T>> that emits the old value and new value as a pair
Dialog<T>
toObservable<T>
Returns an Observable<T> that emits the given result of Dialog<T>. Will be empty if no response.
Node, Window, Scene
events(eventType: EventType)
Creates an Observable emitting events of the given EventType
Node, MenuItem, ContextMenu
actionEvents()
Creates an Observable that emits an ActionEvent every time one occurs
ObservableList<T>
onChangedObservable()
Returns an Observable<ObservableList<T>> that emits the entire ObservableList<T> every time it is modified.
ObservableList<T>
additions()
Creates an Observable<T> emitting T items that were added to the ObservableList<T>
ObservableList<T>
removals()
Creates an Observable<T> emitting T items that were removed from the ObservableList<T>
ObservableList<T>
updates()
Creates an Observable<T> emitting T items whose specified properties were updated in the ObservableList<T>
ObservableList<T>
changes()
Creates an Observable<ListChange<T>> emitting ListChange<T> items, which pairs each item with an ADDED, REMOVED, or UPDATED flag
ObservableList<T>
distinctChanges()
Creates an Observable<ListChange<T>> emitting distinctListChange<T> items. It will only emit the first ADDED item T and not emit dupes, and will only emit the REMOVED item T when no more dupes exist
ObservableList<T>
distinctChanges(mapper: (T) -> R)
Creates an Observable<ListChange<T>> emitting distinctListChange<T> items based off the mapper's definition of a distinct value R. It will only emit the first ADDED item T and not emit dupes, and will only emit the REMOVED item T when no more dupes exist
ObservableList<T>
distinctMappingChanges(mapper: (T) -> R)
Creates an Observable<ListChange<R>> emitting distinctListChange<R> mappings based off the mapper's definition of a distinct value R. It will only emit the first ADDED item R and not emit dupes, and will only emit the REMOVED item R when no more dupes exist
Observable of Button ActionEvents
val myButton =Button("Press Me")
val subscription = myButton.actionEvents().subscribe { println("Pressed!") }
Creating a Reactive Binding
val myButton =Button("Press Me")
val countBinding = myButton.actionEvents().map { 1 }
.scan(0, { x,y -> x + y })
.map { it.toString() }
.toBinding()
val myLabel =Label()
myLabel.textProperty().bind(countBinding)
val dialog =Alert(AlertType.CONFIRMATION, "Are you sure you want to continue?")
dialog.toObservable().filter { it ==ButtonType.YES }
.subscribe { println("You pressed YES") }
Using and Disposing CompositeBinding
val binding1:Binding=...
val binding2:Binding=...
//adding one at a timeval bindings =CompositeBinding()
val bindings += binding1
val bindings += binding2
//or all at onceval bindings =CompositeBinding(binding1,binding2)
//do stuff, then dispose Bindings
bindings.dispose()
Operators
RxKotlinFX has a growing list of operators placed as extension functions onto Observable that aid interoperability with JavaFX.
Operator
Description
observeOnFx()
Schedules the emissions to be observed on the JavaFX thread
subscribeOnFx()
Schedules the source Observable to emit items on the JavaFX thread
doOnNextFx()
Executes the specified action on the FX thread for each emission
doOnErrorFx()
Executes the specified action on the FX thread when an error is emitted
doOnCompleteFx()
Executes the specified action on the FX thread when the Observable calls onComplete()
doOnSubscribeFx()
Executes the specified action on the FX thread when the Observable is first subscribed
doOnTerminateFx()
Executes the specified action on the FX thread when the Observable calls onComplete() or onError()
doOnDisposeFx()
Executes the specified action on the FX thread when the Observable is unsubscribed
doOnNextCount()
Executes the specified action with the cumulative count of emissions for that emission
doOnErrorCount()
Executes the specified action with the cumulative count of emissions when an error is emitted
doOnCompleteCount()
Executes the specified action with the total emission count when onComplete() is called
doOnNextCountFx()
Same as doOnNextCount() except action is executed on FX thread
doOnErrorCountFx()
Same as doOnErrorCount() except action is executed on FX thread
doOnCompleteCountFx()
Same as doOnCompleteCount() except action is executed on FX thread
The doOnXXXCount() operators are especially helpful for providing a status update of how many items have been "processed" by an Observable.
val source =Observable.range(1,1000)
val processedCountLabel =Label()
source.map { it *10 }
.doOnNextFx { processedCountLabel.text ="Processed $it items" }
.subsribe { doSomethingWith(it) }
Control Extensions
The rest of the project will likely add convenient extension functions to emit events as Observable values, much like the TornadoFX project has done. For example, helpful Observable extension functions and properties can be added to TableView and ListView, such as selection events.
val tableView:TableView<MyItem> =...
val selections:Observable<MyItem> = tableView.itemSelections
val rowIndexSelections:Observable<Int> = tableView.rowIndexSelections
Check releases as well the Nodes code file to see a list of available extensions. Feel free to contribute if you see any missing.
Bugs and Feedback
For bugs, questions and discussions please use the Github Issues.
LICENSE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
请发表评论