JavaFX Events Using Local Variables - events

I have a method that creates an ImageView, called createImageView(), which uses EventHanlders to call vaious events. I would like to be able to use local variables in this method. What I usually do is assign final variables to use within the handle(Event) method.
In the method described below, when I go to use final I get the following errors:
The final local variable initY cannot be assigned, since it is defined in an enclosing type
The final local variable initY cannot be assigned, since it is defined in an enclosing type
The final local variable dragAnchor cannot be assigned, since it is defined in an enclosing type
Of course, removing final gives me this message:
Cannot refer to a non-final variable initX inside an inner class defined in a different method
So what do I have to do to make this method work?
private ImageView createImageView() {
double initX;
double initY;
Point2D dragAnchor;
imgView.setOnMousePressed(new EventHandler < MouseEvent > () {
public void handle(MouseEvent me) {
//when mouse is pressed, store initial position
initX = imgView.getTranslateX();
initY = imgView.getTranslateY();
dragAnchor = new Point2D(me.getSceneX(), me.getSceneY());
me.consume();
}
});
imgView.setOnMouseDragged(new EventHandler < MouseEvent > () {
public void handle(MouseEvent me) {
double dragX = me.getSceneX() - dragAnchor.getX();
double dragY = me.getSceneY() - dragAnchor.getY();
double newXPosition = initX + dragX;
double newYPosition = initY + dragY;
imgView.setTranslateX(newXPosition);
imgView.setTranslateY(newYPosition);
me.consume();
}
});
imgView.setOnMouseEntered(new EventHandler < MouseEvent > () {
public void handle(MouseEvent event) {
imgView.setEffect(new Glow(0.5));
event.consume();
}
});
imgView.setOnMouseExited(new EventHandler < MouseEvent > () {
public void handle(MouseEvent event) {
imgView.setEffect(new Glow(0.0));
event.consume();
}
});
return imgView;
}

Use JavaFX properties instead of primitive types.
For example:
final DoubleProperty initX = new SimpleDoubleProperty();
final ObjectProperty<Point2D> dragAnchor = new SimpleObjectProperty<>();
...
// in event handler
initX.set(
imageView.getTranslateX()
);
dragAnchor.set(
new Point2D(
me.getSceneX(),
me.getSceneY()
)
);

I removed the 3 local variables and made them class variables and it worked. If anyone doesn't mind offering a quick explanation as of why I would appreciate it.

Related

Android - How to make google Maps display a polyline that animates sequenctial flashing dots

I am searching for a way to animate the dots between two markers on a google map in android device.
So what i want in the end is the following line between the two images:
and it would be used like this typical google polyline implementation:
lets say there is a point A and a Point B. if im directing the user to point B, then the line animates to from point A to point B so the user knows to walk in this direction.
to achieve this i thought i could get the points out of the polyLine and remove them and add them back
rapidily. so lets say i had 5 points in the polyLine, i would remove position 1 , then put it back, then remove position 2, and put it back, to simulate this animation.
but it does not work . once hte polyline is set it seems i cannot alter it. you have any suggestions ?
val dotPattern = Arrays.asList(Dot(), Gap(convertDpToPixel(7).toFloat()))
val polyLineOptions: PolylineOptions = PolylineOptions()
.add(usersLocation)
.add(users_destination)
.pattern(dotPattern)
.width(convertDpToPixel(6).toFloat())
dottedPolyLine = googleMap.addPolyline(polyLineOptions)
dottedPolyLine?.points?.removeAt(1) // here as a test if my idea i try removing a point but it looks like a point here means current location or destination so there always 2. i thought a point would be one of the dots.
You can use MapView-based custom view View Canvas animationlike in this answer:
This approach requires
MapView-based
custom
view,
that implements:
drawing over the MapView canvas;
customizing line styles (circles instead of a simple line);
binding path to Lat/Lon coordinates of map
performing animation.
Drawing over the MapView needs to override dispatchDraw().
Customizing line styles needs
setPathEffect()
method of
Paint
class that allows to create create path for "circle stamp" (in
pixels), which will repeated every "advance" (in pixels too),
something like that:
mCircleStampPath = new Path(); mCircleStampPath.addCircle(0,0,
CIRCLE_RADIUS, Path.Direction.CCW); mCircleStampPath.close();
For binding path on screen to Lat/Lon coordinates
Projection.toScreenLocation()
needed, that requires
GoogleMap
object so custom view should implements OnMapReadyCallback for
receive it. For continuous animation
postInvalidateDelayed()
can be used.
but not draw path directly from point A to point B, but from point A to point C that animated from A to B. To get current position of point C you can use SphericalUtil.interpolate() from Google Maps Android API Utility Library. Something like that:
public class EnhancedMapView extends MapView implements OnMapReadyCallback {
private static final float CIRCLE_RADIUS = 10;
private static final float CIRCLE_ADVANCE = 3.5f * CIRCLE_RADIUS; // spacing between each circle stamp
private static final int FRAMES_PER_SECOND = 30;
private static final int ANIMATION_DURATION = 10000;
private OnMapReadyCallback mMapReadyCallback;
private GoogleMap mGoogleMap;
private LatLng mPointA;
private LatLng mPointB;
private LatLng mPointC;
private float mCirclePhase = 0; // amount to offset before the first circle is stamped
private Path mCircleStampPath;
private Paint mPaintLine;
private final Path mPathFromAtoC = new Path();
private long mStartTime;
private long mElapsedTime;
public EnhancedMapView(#NonNull Context context) {
super(context);
init();
}
public EnhancedMapView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public EnhancedMapView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public EnhancedMapView(#NonNull Context context, #Nullable GoogleMapOptions options) {
super(context, options);
init();
}
#Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.save();
drawLineFomAtoB(canvas);
canvas.restore();
// perform one shot animation
mElapsedTime = System.currentTimeMillis() - mStartTime;
if (mElapsedTime < ANIMATION_DURATION) {
postInvalidateDelayed(1000 / FRAMES_PER_SECOND);
}
}
private void drawLineFomAtoB(Canvas canvas) {
if (mGoogleMap == null || mPointA == null || mPointB == null) {
return;
}
// interpolate current position
mPointC = SphericalUtil.interpolate(mPointA, mPointB, (float) mElapsedTime / (float)ANIMATION_DURATION);
final Projection mapProjection = mGoogleMap.getProjection();
final Point pointA = mapProjection.toScreenLocation(mPointA);
final Point pointC = mapProjection.toScreenLocation(mPointC);
mPathFromAtoC.rewind();
mPathFromAtoC.moveTo(pointC.x, pointC.y);
mPathFromAtoC.lineTo(pointA.x, pointA.y);
// change phase for circles shift
mCirclePhase = (mCirclePhase < CIRCLE_ADVANCE)
? mCirclePhase + 1.0f
: 0;
mPaintLine.setPathEffect(new PathDashPathEffect(mCircleStampPath, CIRCLE_ADVANCE, mCirclePhase, PathDashPathEffect.Style.ROTATE));
canvas.drawPath(mPathFromAtoC, mPaintLine);
}
private void init() {
setWillNotDraw(false);
mCircleStampPath = new Path();
mCircleStampPath.addCircle(0,0, CIRCLE_RADIUS, Path.Direction.CCW);
mCircleStampPath.close();
mPaintLine = new Paint();
mPaintLine.setColor(Color.BLACK);
mPaintLine.setStrokeWidth(1);
mPaintLine.setStyle(Paint.Style.STROKE);
mPaintLine.setPathEffect(new PathDashPathEffect(mCircleStampPath, CIRCLE_ADVANCE, mCirclePhase, PathDashPathEffect.Style.ROTATE));
// start animation
mStartTime = System.currentTimeMillis();
postInvalidate();
}
#Override
public void getMapAsync(OnMapReadyCallback callback) {
mMapReadyCallback = callback;
super.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
#Override
public void onCameraMove() {
invalidate();
}
});
if (mMapReadyCallback != null) {
mMapReadyCallback.onMapReady(googleMap);
}
}
public void setPoints(LatLng pointA, LatLng pointB) {
mPointA = pointA;
mPointB = pointB;
}
}
NB! This is just idea, not full tested code.

Gluon Mobile Charm 5.0 Cannot Hide Layer

I have a loading gif for all backend requests. Prior to Charm 5.0.0, it worked fine in which the loading gif would show, backend would finish what it needed to, then the loading gif would be hidden. Now, the loading gif shows, but it doesn't hide.
addLayerFactory(LOADING_GIF, () -> new Layer() {
private final Node root;
private final double sizeX = getGlassPane().getWidth();
private final double sizeY = getGlassPane().getHeight();
{
ProgressIndicator loading = new ProgressIndicator();
loading.setRadius(50);
loading.setStyle("-fx-text-fill:white");
root = new StackPane(loading);
root.setStyle("-fx-background-color: rgba(0,0,0,0);");
getChildren().add(root);
this.setStyle("-fx-background-color:rgba(255,255,255,0.7)");
this.setShowTransitionFactory(v -> {
FadeInTransition ft = new FadeInTransition(v);
ft.setRate(2);
return ft;
});
}
#Override
public void show() {
this.setBackgroundFade(0.0);
super.show();
Layer pane = this;
Task<Integer> task = new Task<Integer>() {
#Override
protected Integer call() throws Exception {
int iterations = 0;
int max = DataService.readOutTime / 1000;
while (iterations <= max) {
Thread.sleep(1000);
iterations++;
}
Platform.runLater(new Runnable() {
#Override
public void run() {
if (pane.isVisible()) {
pane.setShowTransitionFactory(v -> {
FadeOutTransition ft = new FadeOutTransition(v);
ft.setRate(2);
return ft;
});
pane.hide();
MobileApplication.getInstance().showMessage("There was an error in sending your data.");
}
}
});
return iterations;
}
};
Thread thread = new Thread(task);
thread.start();
}
#Override
public void hide() {
this.setBackgroundFade(0.0);
super.hide();
}
#Override
public void layoutChildren() {
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
root.resize(sizeX, sizeY);
resizeRelocate((getGlassPane().getWidth() - sizeX) / 2, (getGlassPane().getHeight() - sizeY) / 2, sizeX, sizeY);
}
});
I have a couple of utility methods that show and hide the loader:
public void showLoader() {
MobileApplication.getInstance().showLayer(App.LOADING_GIF);
}
public void hideLoader() {
MobileApplication.getInstance().hideLayer(App.LOADING_GIF);
}
Interestingly, the custom timeout I created (to hide the loader in case there is a stall in the backend) doesn't hide the layer either.
There is an issue with your code: you are overriding Layer::layoutChildren, but you are not calling super.layoutChildren().
If you check the JavaDoc:
Override this method to add the layout logic for your layer. Care should be taken to call this method in overriden methods for proper functioning of the Layer.
This means that you are getting rid of some important parts of the Layer control, such as animations, events and visibility control.
This should work:
#Override
public void layoutChildren() {
super.layoutChildren();
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
root.resize(sizeX, sizeY);
resizeRelocate(getGlassPane().getWidth() - sizeX) / 2, getGlassPane().getHeight() - sizeY) / 2, sizeX, sizeY);
}
On a side note, for the hide transition, you should use setHideTransitionFactory.
So this is what I have done to solve this. From the Gluon Docs on the hide() method:
If this layer is showing, calling this method will hide it. If a hide transition is present, it is played before hiding the Layer. Care should be taken to call this only once LifecycleEvent.SHOWN has been fired.
Thus, I was realizing that the response from the backend was coming before the layer was fully shown. Thus, I modified the overridden hide() method as follows:
#Override
public void hide() {
if (this.isShowing()) {
this.setOnShown(e -> {
this.setBackgroundFade(0.0);
super.hide();
});
} else {
super.hide();
}
}
So if the layer is still in LifecycleEvent.SHOWING mode when being told to hide, make sure that it hides when it is shown. Otherwise it is already shown so hide it.

How can i use VALA delegates in GTK3 button callback?

I'm trying to understand Vala delegates with Gtk3.
I tested callback and lambda with no problem.
I wanna test a delegate callback, here my code :
using Gtk;
delegate void typeDelegate(Button button);
int main (string[] args) {
Gtk.init (ref args);
typeDelegate cb = cbLabelf;
var window = new Window ();
window.title = "First GTK+ Program";
window.border_width = 10;
window.window_position = WindowPosition.CENTER;
window.set_default_size (350, 70);
window.destroy.connect (Gtk.main_quit);
var button = new Button.with_label ("Click me!");
//button.clicked.connect (cb);
//button.clicked+= cb;
button.clicked.connect+=cb;
window.add (button);
window.show_all ();
Gtk.main ();
return 0;
}
void cbLabelf(Button button)
{
button.label = "tank yu";
}
I also red generated C code ( when i use lambda) to understand.
Here the compil error :
GTKsampleDelegate.vala:20.5-20.30: error: Arithmetic operation not supported for types Gtk.Button.clicked.connect' andtypeDelegate'
button.clicked.connect+=cb;
Well,
Seems that you want to get the intrinsic variable that holds the instance that emitted the signal, I find strange that vala doesn't let you use a delegate variable to obtain it via parameter, yet, you can use one of the forms below: using no delegation variable (A) or bypassing the error with a closure (B).
public class FooSignalClass : Object {
/* Gtk Button.clicked signal has the void f(void) signature */
public signal void on_foo ();
public void foo() {
on_foo();
}
}
public delegate void FooSignalFunc (FooSignalClass fooer);
void on_foo_handler (FooSignalClass fooer) {
long fooer_memory_address = (long)fooer;
GLib.message(#"fooer exists? $(fooer!=null).");
GLib.message(#"address=$fooer_memory_address.");
}
int main () {
var foo_signal = new FooSignalClass();
long fooer_memory_address = (long)foo_signal;
GLib.message(#"foo_signal address=$fooer_memory_address.");
/* Option A: Connect directly without the delegate variable */
foo_signal.on_foo.connect(on_foo_handler);
/* Option B: You cant use a delegate directly, bypass it with a closure */
FooSignalFunc func = on_foo_handler;
foo_signal.on_foo.connect((instance) => {
func(instance);
});
foo_signal.foo();
return 0;
}

Console.ReadLine() passed to C# event

I'm learning RX and would like to use Console.ReadLine as a source for observable sequences.
I know that I can create "IEnumerable" using "yield return", but for my concrete use case I've decided to create a C# event, so that potentially many observers will be able to share the same keyboard input.
Here is my code:
class Program
{
private delegate void OnNewInputLineHandler(string line);
private static event OnNewInputLineHandler OnNewInputLineEvent = _ => {};
static void Main(string[] args)
{
Task.Run((Action) GetInput);
var input = ConsoleInput();
input.Subscribe(s=>Console.WriteLine("1: " + s));
Thread.Sleep(30000);
}
private static void GetInput()
{
while (true)
OnNewInputLineEvent(Console.ReadLine());
}
private static IObservable<string> ConsoleInput()
{
return Observable.Create<string>(
(IObserver<string> observer) =>
{
OnNewInputLineHandler h = observer.OnNext;
OnNewInputLineEvent += h;
return Disposable.Create(() => { OnNewInputLineEvent -= h; });
});
}
}
My problem - when I run the GetInput method as it is shown above, the very first input line is not sent to the sequence (but it is sent to the event handler).
However, if I replace it with the following version, everything works as expected:
private static void GetInput()
{
while (true)
{
var s = Console.ReadLine();
OnNewInputLineEvent(s);
}
}
Could someone shed some light on why this might happen?
You're trying to make life difficult for yourself. There is almost always a way to make things simple with Rx. It's just a matter of learning to think more functionally rather than procedurally.
This is all you need:
class Program
{
static void Main(string[] args)
{
var subscription = ConsoleInput().Subscribe(s => Console.WriteLine("1: " + s));
Thread.Sleep(30000);
subscription.Dispose();
}
private static IObservable<string> ConsoleInput()
{
return
Observable
.FromAsync(() => Console.In.ReadLineAsync())
.Repeat()
.Publish()
.RefCount()
.SubscribeOn(Scheduler.Default);
}
}
This lets multiple subscribers share the one input through the .Publish().RefCount(). And the .SubscribeOn(Scheduler.Default) pushes the subscription out to a new thread - without it you block on a subscription.
If you move Task.Run((Action) GetInput); to after the subscription your code will work as desired. This is because in your original version, the first call of OnNewInputEvent(Console.ReadLine()) is run before you've hooked OnNewInputLineEvent to the observer.OnNext.

How to use events in GWT Frame

I would like to capture all events within a GWT frame. I've found several ways to do this, but they only return mousemove and mouseout events. I also need keypresses, input, etc. The goal is to capture the events and send them to another client by using websockets, and then replicate them on the other side (co-browsing).
I am using a page on the same domain within the frame.
public class ESinkFrame extends Frame implements EventListener {
public ESinkFrame(String src){
super(src);
DOM.sinkEvents(getElement(), Event.KEYEVENTS);
DOM.sinkEvents(getElement(), Event.MOUSEEVENTS);
}
public void onBrowserEvent(Event event) {
System.out.println( "sunk event: " + DOM.eventGetTypeString(event) );
}
}
And when I use it, I also try to attach a different way of grabbing the events.
ESinkFrame frame = new ESinkFrame("http://127.0.0.1:8888/other.html");
RootPanel.get().add(frame);
FrameElement frameElt = frame.getElement().cast();
Document frameDoc = frameElt.getContentDocument();
BodyElement body = frameDoc.getBody();
Element el = body.cast();
DOM.setEventListener(el, new EventListener()
{
public void onBrowserEvent(Event event)
{
Window.alert("test");
}
});
DOM.sinkEvents(el, Event.KEYEVENTS);
Event.addNativePreviewHandler(new NativePreviewHandler(){
public void onPreviewNativeEvent(NativePreviewEvent event) {
String eventName = event.getNativeEvent().getType();
if (event.isFirstHandler() /* && (event.getTypeInt() & Event.MOUSEEVENTS) == 0*/)
System.out.println("PreviewHandler: " + eventName);
}
});

Resources