I would like to know, how to make scrollview swipe effect when mouse is dragged up and down. I'm developing JavaFX application which has ListView & I need to scroll the list when user drags the mouse up and down (like iOS contact list).
Also how to add the user's dragging velocity and speed to the scrolling value?
UPDATED : I got some code here but It doesn't work properly...
import java.util.ArrayList;
import java.util.Arrays;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.animation.TimelineBuilder;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.Event;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.FlowPane;
import javafx.util.Duration;
public class Swipe extends ScrollPane {
private static final int INERTIA_DURATION = 2400;
private static final double CLICK_THRESHOLD = 20;
private static final double CLICK_TIME_THRESHOLD = Integer.parseInt(System.getProperty("click", "400"));
private final double width;
private final double height;
private long startDrag;
private long lastDrag;
private long lastDragDelta;
private int startDragX;
private int startDragY;
private int lastDragX;
private int lastDragY;
private int lastDragStepX;
private int lastDragStepY;
private double dragVelocityX;
private double dragVelocityY;
private boolean clickThresholdBroken;
private Timeline inertiaTimeline = null;
private long lastClickTime = -1;
private final boolean isFiredByMe = false;
public Swipe(double width, double height) {
this.width = width;
this.height = height;
init();
}
private void init() {
setPrefSize(width, height);
setPannable(true);
setHbarPolicy(ScrollBarPolicy.NEVER);
setVbarPolicy(ScrollBarPolicy.NEVER);
setEventHandlers();
ContentPane cp = new ContentPane();
setContent(cp);
}
private void setEventHandlers() {
setOnMousePressed((MouseEvent event) -> {
lastDragX = startDragX = (int) event.getX();
lastDragY = startDragY = (int) event.getY();
lastDrag = startDrag = System.currentTimeMillis();
lastDragDelta = 0;
if (inertiaTimeline != null) {
inertiaTimeline.stop();
}
clickThresholdBroken = false;
});
setOnDragDetected((MouseEvent event) -> {
// Delta of this drag vs. last drag location (or start)
lastDragStepX = (int) event.getX() - lastDragX;
lastDragStepY = (int) event.getY() - lastDragY;
// Duration of this drag step.
lastDragDelta = System.currentTimeMillis() - lastDrag;
// Velocity of last drag increment.
dragVelocityX = (double) lastDragStepX / (double) lastDragDelta;
dragVelocityY = (double) lastDragStepY / (double) lastDragDelta;
// Snapshot of this drag event.
lastDragX = (int) event.getX();
lastDragY = (int) event.getY();
lastDrag = System.currentTimeMillis();
// Calculate distance so far -- have we dragged enough to scroll?
final int dragX = (int) event.getX() - startDragX;
final int dragY = (int) event.getY() - startDragY;
double distance = Math.abs(Math.sqrt((dragX * dragX) + (dragY * dragY)));
int scrollDistX = lastDragStepX;
int scrollDistY = lastDragStepY;
if (!clickThresholdBroken && distance > CLICK_THRESHOLD) {
clickThresholdBroken = true;
scrollDistX = dragX;
scrollDistY = dragY;
}
if (clickThresholdBroken) {
Event.fireEvent(event.getTarget(), new ScrollEvent(
ScrollEvent.SCROLL,
scrollDistX, scrollDistY,
scrollDistX, scrollDistY,
event.isShiftDown(), event.isControlDown(), event.isAltDown(), event.isMetaDown(),
true, false,
event.getX(), event.getY(),
event.getSceneX(), event.getSceneY(),
ScrollEvent.HorizontalTextScrollUnits.NONE, 0,
ScrollEvent.VerticalTextScrollUnits.NONE, 0, 0, null));
}
});
setOnMouseReleased((MouseEvent event) -> {
handleRelease(event);
});
setOnMouseClicked((MouseEvent event) -> {
final long time = System.currentTimeMillis();
if (clickThresholdBroken || (lastClickTime != -1 && (time - lastClickTime) < CLICK_TIME_THRESHOLD)) {
event.consume();
}
lastClickTime = time;
});
}
private void handleRelease(final MouseEvent me) {
if (clickThresholdBroken) {
// Calculate last instantaneous velocity. User may have stopped moving
// before they let go of the mouse.
final long time = System.currentTimeMillis() - lastDrag;
dragVelocityX = (double) lastDragStepX / (time + lastDragDelta);
dragVelocityY = (double) lastDragStepY / (time + lastDragDelta);
// determin if click or drag/flick
final int dragX = (int) me.getX() - startDragX;
final int dragY = (int) me.getY() - startDragY;
// calculate complete time from start to end of drag
final long totalTime = System.currentTimeMillis() - startDrag;
// if time is less than 300ms then considered a quick flick and whole time is used
final boolean quick = totalTime < 300;
// calculate velocity
double velocityX = quick ? (double) dragX / (double) totalTime : dragVelocityX; // pixels/ms
double velocityY = quick ? (double) dragY / (double) totalTime : dragVelocityY; // pixels/ms
final int distanceX = (int) (velocityX * INERTIA_DURATION); // distance
final int distanceY = (int) (velocityY * INERTIA_DURATION); // distance
//
DoubleProperty animatePosition = new SimpleDoubleProperty() {
double lastMouseX = me.getX();
double lastMouseY = me.getY();
#Override
protected void invalidated() {
final double mouseX = me.getX() + (distanceX * get());
final double mouseY = me.getY() + (distanceY * get());
final double dragStepX = mouseX - lastMouseX;
final double dragStepY = mouseY - lastMouseY;
if (Math.abs(dragStepX) >= 1.0 || Math.abs(dragStepY) >= 1.0) {
Event.fireEvent(me.getTarget(), new ScrollEvent(
ScrollEvent.SCROLL,
dragStepX, dragStepY,
(distanceX * get()), (distanceY * get()),
me.isShiftDown(), me.isControlDown(), me.isAltDown(), me.isMetaDown(),
true, true,
me.getX(), me.getY(),
me.getSceneX(), me.getSceneY(),
ScrollEvent.HorizontalTextScrollUnits.NONE, 0,
ScrollEvent.VerticalTextScrollUnits.NONE, 0,
0, null));
}
lastMouseX = mouseX;
lastMouseY = mouseY;
}
};
// animate a slow down from current velocity to zero
inertiaTimeline = TimelineBuilder.create()
.keyFrames(
new KeyFrame(Duration.ZERO, new KeyValue(animatePosition, 0)),
new KeyFrame(Duration.millis(INERTIA_DURATION), new KeyValue(animatePosition, 1d, Interpolator.SPLINE(0.0513, 0.1131, 0.1368, 1.0000)))
).build();
inertiaTimeline.play();
}
}
private class ContentPane extends FlowPane {
private ArrayList getList() {
String[] list = {
"Kerrie Batts", "Raina Huffstutler", "Kip Kukowski", "Trish Sullivan", "Kyla Hollingsworth", "Gearldine Leavy", "Major Langdon", "Avery Rusin", "Hedy Messina", "Audry Felps", "Tianna Robbins", "Marian Tranmer", "Lashaunda Bivona", "Leighann Schwab", "Emanuel Volpe", "Neida Geist", "Edda Placencia", "Olevia Hippe", "Fernando Cohen", "Danette Dorsett"};
ArrayList<String> nameList = new ArrayList();
nameList.addAll(Arrays.asList(list));
return nameList;
}
public ContentPane() {
setPrefSize(215, 271);
ArrayList<String> nameList = getList();
Element[] element = new Element[nameList.size()];
for (int i = 0; i < nameList.size(); i++) {
String name = nameList.get(i);
Element el = element[i] = new Element(210, 25, name);
getChildren().add(el);
}
}
}
private class Element extends AnchorPane {
private double width;
private double height;
private String name;
public Element(double width, double height, String name) {
this.width = width;
this.height = height;
this.name = name;
init();
}
private Label createName() {
Label label = new Label(name);
label.setPrefSize(width, height);
label.setAlignment(Pos.CENTER_LEFT);
AnchorPane.setLeftAnchor(label, 5.0);
label.setStyle("-fx-font-family: Calibri; -fx-font-size: 14;");
return label;
}
private void init() {
setPrefSize(width, height);
getChildren().add(createName());
}
public void setPrefSize(double width, double height) {
this.width = width;
this.height = height;
}
public void setName(String name) {
this.name = name;
}
}
}
I figured out myself. There is two classes. 1st one is for calculate the velocity of the movement.
VelocityTracker.java
import javafx.scene.input.MouseEvent;
public final class VelocityTracker {
private static final boolean DEBUG = false;
private static final boolean localLOGV = DEBUG;
private static final int NUM_PAST = 10;
private static final int MAX_AGE_MILLISECONDS = 200;
private static final int POINTER_POOL_CAPACITY = 20;
private static Pointer sRecycledPointerListHead;
private static int sRecycledPointerCount;
private static final class Pointer {
public Pointer next;
public int id;
public float xVelocity;
public float yVelocity;
public final float[] pastX = new float[NUM_PAST];
public final float[] pastY = new float[NUM_PAST];
public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel
}
private Pointer mPointerListHead; // sorted by id in increasing order
private int mLastTouchIndex;
public VelocityTracker() {
clear();
}
public void clear() {
releasePointerList(mPointerListHead);
mPointerListHead = null;
mLastTouchIndex = 0;
}
public void addMovement(MouseEvent ev) {
final int historySize = 0;
final int lastTouchIndex = mLastTouchIndex;
final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
mLastTouchIndex = finalTouchIndex;
if (mPointerListHead == null) {
mPointerListHead = obtainPointer();
}
final float[] pastX = mPointerListHead.pastX;
final float[] pastY = mPointerListHead.pastY;
final long[] pastTime = mPointerListHead.pastTime;
pastX[finalTouchIndex] = (float) ev.getX();
pastY[finalTouchIndex] = (float) ev.getY();
pastTime[finalTouchIndex] = System.currentTimeMillis();
}
public void computeCurrentVelocity(int units) {
computeCurrentVelocity(units, Float.MAX_VALUE);
}
public void computeCurrentVelocity(int units, float maxVelocity) {
final int lastTouchIndex = mLastTouchIndex;
for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
final long[] pastTime = pointer.pastTime;
int oldestTouchIndex = lastTouchIndex;
int numTouches = 1;
final long minTime = pastTime[lastTouchIndex] - MAX_AGE_MILLISECONDS;
while (numTouches < NUM_PAST) {
final int nextOldestTouchIndex = (oldestTouchIndex + NUM_PAST - 1) % NUM_PAST;
final long nextOldestTime = pastTime[nextOldestTouchIndex];
if (nextOldestTime < minTime) { // also handles end of trace sentinel
break;
}
oldestTouchIndex = nextOldestTouchIndex;
numTouches += 1;
}
if (numTouches > 3) {
numTouches -= 1;
}
final float[] pastX = pointer.pastX;
final float[] pastY = pointer.pastY;
final float oldestX = pastX[oldestTouchIndex];
final float oldestY = pastY[oldestTouchIndex];
final long oldestTime = pastTime[oldestTouchIndex];
float accumX = 0;
float accumY = 0;
for (int i = 1; i < numTouches; i++) {
final int touchIndex = (oldestTouchIndex + i) % NUM_PAST;
final int duration = (int) (pastTime[touchIndex] - oldestTime);
if (duration == 0) {
continue;
}
float delta = pastX[touchIndex] - oldestX;
float velocity = (delta / duration) * units; // pixels/frame.
accumX = (accumX == 0) ? velocity : (accumX + velocity) * .5f;
delta = pastY[touchIndex] - oldestY;
velocity = (delta / duration) * units; // pixels/frame.
accumY = (accumY == 0) ? velocity : (accumY + velocity) * .5f;
}
if (accumX < -maxVelocity) {
accumX = -maxVelocity;
} else if (accumX > maxVelocity) {
accumX = maxVelocity;
}
if (accumY < -maxVelocity) {
accumY = -maxVelocity;
} else if (accumY > maxVelocity) {
accumY = maxVelocity;
}
pointer.xVelocity = accumX;
pointer.yVelocity = accumY;
}
}
public float getXVelocity() {
Pointer pointer = getPointer(0);
return pointer != null ? pointer.xVelocity : 0;
}
public float getYVelocity() {
Pointer pointer = getPointer(0);
return pointer != null ? pointer.yVelocity : 0;
}
public float getXVelocity(int id) {
Pointer pointer = getPointer(id);
return pointer != null ? pointer.xVelocity : 0;
}
public float getYVelocity(int id) {
Pointer pointer = getPointer(id);
return pointer != null ? pointer.yVelocity : 0;
}
private Pointer getPointer(int id) {
for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
if (pointer.id == id) {
return pointer;
}
}
return null;
}
private static Pointer obtainPointer() {
if (sRecycledPointerCount != 0) {
Pointer element = sRecycledPointerListHead;
sRecycledPointerCount -= 1;
sRecycledPointerListHead = element.next;
element.next = null;
return element;
}
return new Pointer();
}
private static void releasePointer(Pointer pointer) {
if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
pointer.next = sRecycledPointerListHead;
sRecycledPointerCount += 1;
sRecycledPointerListHead = pointer;
}
}
private static void releasePointerList(Pointer pointer) {
if (pointer != null) {
int count = sRecycledPointerCount;
if (count >= POINTER_POOL_CAPACITY) {
return;
}
Pointer tail = pointer;
for (;;) {
count += 1;
if (count >= POINTER_POOL_CAPACITY) {
break;
}
Pointer next = tail.next;
if (next == null) {
break;
}
tail = next;
}
tail.next = sRecycledPointerListHead;
sRecycledPointerCount = count;
sRecycledPointerListHead = pointer;
}
}
}
And this is second class
import java.util.concurrent.atomic.AtomicReference;
import javafx.animation.FadeTransition;
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Bounds;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
public class VerticlePane extends Region {
private ObjectProperty<Node> content;
private final VelocityTracker tracker = new VelocityTracker();
private final Rectangle clipRect = new Rectangle();
private Rectangle scrollNode;
private boolean magnet, autoTranslate = true;
private DoubleProperty targetY = new SimpleDoubleProperty(), controlHeight = new SimpleDoubleProperty(), viewHeight = new SimpleDoubleProperty();
private double startValue, endValue;
private HBox indicator;
private StringProperty indicatorValue = new SimpleStringProperty("Pull Up to Update...");
public VerticlePane() {
setFocusTraversable(false);
setClip(clipRect);
contentProperty().addListener((ObservableValue<? extends Node> observable, Node oldValue, Node newValue) -> {
getChildren().clear();
if (newValue != null) {
getChildren().addAll(newValue, createRectangle());
}
layout();
});
final AtomicReference<MouseEvent> deltaEvent = new AtomicReference<>();
final AtomicReference<TranslateTransition> currentTransition1 = new AtomicReference<>();
final AtomicReference<TranslateTransition> currentTransition2 = new AtomicReference<>();
final AtomicReference<Timeline> timeline = new AtomicReference<>();
setOnMousePressed((me) -> {
tracker.addMovement(me);
deltaEvent.set(me);
startValue = me.getY();
if (currentTransition1.get() != null) {
currentTransition1.get().stop();
}
if (currentTransition2.get() != null) {
currentTransition2.get().stop();
}
if (timeline.get() != null) {
timeline.get().stop();
}
});
setOnMouseDragged((event) -> {
tracker.addMovement(event);
double delta = event.getY() - deltaEvent.get().getY();
targetY.set(content.get().getTranslateY() + delta);
content.get().setTranslateY(targetY.get());
controlHeight.set(content.get().getLayoutBounds().getHeight());
viewHeight.set(getHeight());
double scrollTargetY = targetY.divide(controlHeight.divide(viewHeight.get()).get()).get();
double lastInstanceHeight = controlHeight.subtract(viewHeight.get()).multiply(-1).get();
scrollNode.relocate(1.0, (getHeight() - 5.0));
scrollNode.setArcHeight(5.0);
scrollNode.setArcWidth(5.0);
scrollNode.setHeight(viewHeight.divide(controlHeight.divide(viewHeight.get()).get()).get());
scrollNode.setVisible(true);
scrollNode.setOpacity(0.25);
if (targetY.lessThan(0).and(targetY.greaterThan(lastInstanceHeight)).get()) {
scrollNode.setTranslateY(Math.abs(scrollTargetY));
} else if (targetY.greaterThanOrEqualTo(0).get()) {
scrollNode.setHeight(scrollNode.getHeight() - targetY.get());
}
if (targetY.get() < lastInstanceHeight) {
double scrollNodeHeight = scrollNode.getHeight() - (lastInstanceHeight - targetY.get());
double scrollNodeTranslateY = viewHeight.subtract(viewHeight.divide(controlHeight.divide(viewHeight.get()).get()).get()).add(lastInstanceHeight - targetY.get()).get();
scrollNode.setHeight(scrollNodeHeight);
scrollNode.setTranslateY(scrollNodeTranslateY);
}
deltaEvent.set(event);
});
setOnMouseReleased((me) -> {
FadeTransition ft = new FadeTransition(Duration.millis(300), scrollNode);
ft.setFromValue(0.25);
ft.setToValue(0.00);
tracker.addMovement(me);
tracker.computeCurrentVelocity(500);
endValue = me.getY();
controlHeight.set(content.get().getLayoutBounds().getHeight());
viewHeight.set(getHeight());
float velocityY = tracker.getYVelocity();
targetY.set(scrollNode(velocityY, currentTransition1, content.get()));
TranslateTransition tt = bb(currentTransition2, scrollNode, Math.abs((targetY.divide(controlHeight.divide(viewHeight.get()).get()).get())));
tt.setOnFinished((ae) -> {
ft.play();
});
});
}
public double getStartValue() {
return startValue;
}
public double getEndValue() {
return endValue;
}
public double getControlHeight() {
return controlHeight.get();
}
public double getViewHeight() {
return viewHeight.get();
}
public VerticlePane(double prefWidth, double prefHeight) {
this();
setPrefSize(prefWidth, prefHeight);
}
public VerticlePane(double prefWidth, double prefHeight, boolean magnet) {
this(prefWidth, prefHeight);
setMagnet(magnet);
}
public final void setMagnet(boolean magnet) {
this.magnet = magnet;
}
private TranslateTransition bb(AtomicReference<TranslateTransition> currentTransition, Node node, Double targetY) {
TranslateTransition translate = new TranslateTransition(new Duration(1000), node);
if (node != null) {
translate.setInterpolator(Interpolator.SPLINE(0.0513, 0.1131, 0.1368, 1.0000));
if (targetY != null) {
translate.setFromY(node.getTranslateY());
translate.setToY(targetY);
}
translate.play();
currentTransition.set(translate);
}
return translate;
}
private Double scrollNode(float velocityY, AtomicReference<TranslateTransition> currentTransition, Node node) {
final Bounds b = node.getLayoutBounds();
Double backBouncingY = null;
Double targetY = null;
if (node != null) {
TranslateTransition translate = new TranslateTransition(new Duration(1000), node);
translate.setInterpolator(Interpolator.SPLINE(0.0513, 0.1131, 0.1368, 1.0000));
if (Math.abs(velocityY) < 10) {
velocityY = 0;
}
targetY = node.getTranslateY();
double controlHeight = b.getHeight() - indicator.heightProperty().add(10).get();
double viewHeight = getHeight();
if (controlHeight < viewHeight && targetY < controlHeight) {
targetY = 0.0;
} else if (targetY > 0) {
targetY = 0.0;
} else if ((targetY < (controlHeight - viewHeight) * -1)) {
targetY = (controlHeight - viewHeight) * -1;
} else {
targetY += velocityY;
if (controlHeight < viewHeight && targetY < controlHeight) {
targetY = -25.0;
backBouncingY = 0.0;
} else if (targetY > 0) {
targetY = 25.0;
backBouncingY = 0.0;
} else if (targetY < (controlHeight - viewHeight) * -1) {
targetY = (controlHeight - viewHeight) * -1 - 25;
backBouncingY = (controlHeight - viewHeight) * -1;
}
}
//Magnet
if (magnet) {
double dragRegion = (viewHeight / 2) * -1;
double instances = controlHeight / viewHeight;
double lastInstance = ((controlHeight - viewHeight) * -1);
double newInstanceValue = dragRegion;
if (targetY > dragRegion) {
targetY = 0.0;
}
if (instances > 1) {
for (int i = 1; i < instances - 1; i++) {
double instanceValue = (viewHeight * i) * -1;
if (targetY < newInstanceValue && targetY > instanceValue || targetY < instanceValue && targetY > (instanceValue + dragRegion)) {
targetY = instanceValue;
}
newInstanceValue += (viewHeight * -1);
}
}
if (targetY < (lastInstance - dragRegion) && targetY > lastInstance) {
targetY = lastInstance;
}
}
//
if (targetY != null) {
translate.setFromY(node.getTranslateY());
translate.setToY(targetY);
}
if (backBouncingY != null) {
final Double fbackFlingY = backBouncingY;
translate.setOnFinished((ae) -> {
currentTransition.set(null);
TranslateTransition translate1 = new TranslateTransition(new Duration(300), node);
if (fbackFlingY != null) {
translate1.setFromY(node.getTranslateY());
translate1.setToY(fbackFlingY);
}
translate1.play();
currentTransition.set(translate1);
});
} else {
translate.setOnFinished((ae) -> {
currentTransition.set(null);
});
}
translate.play();
currentTransition.set(translate);
}
return targetY;
}
private Rectangle createRectangle() {
if (scrollNode == null) {
scrollNode = new Rectangle(4.0, 4.0, Color.BLACK);
}
scrollNode.setVisible(false);
return scrollNode;
}
public ObjectProperty<Node> contentProperty() {
if (content == null) {
content = new SimpleObjectProperty<>(this, "content");
}
return content;
}
public final void setContent(Node value) {
contentProperty().set(
new VBox(10) {
{
getChildren().addAll(value, indicator());
}
}
);
contentProperty().get().layoutBoundsProperty().addListener((ObservableValue<? extends Bounds> obs, Bounds ov, Bounds nv) -> {
Double containerHeight = nv.getHeight();
Double flingHeight = prefHeightProperty().get();
if (autoTranslate) {
if (flingHeight <= containerHeight) {
contentProperty().get().setTranslateY((containerHeight - flingHeight - 34) * -1);
}
}
});
}
private HBox indicator() {
return indicator = new HBox(10) {
{
setVisible(false);
setAlignment(Pos.CENTER);
getChildren().addAll(
new ImageView(new Image(getClass().getResourceAsStream("Indicator.gif"))),
new Label() {
{
textProperty().bind(indicatorValue);
setStyle("-fx-font-family: Roboto; -fx-font-size: 14px;");
}
}
);
}
};
}
public void setAutoTranslate(Boolean autoTranslate) {
this.autoTranslate = autoTranslate;
}
public void setTranslateZero() {
this.contentProperty().get().setTranslateY(0);
}
public HBox getIndicator() {
return indicator;
}
public void showIndicator() {
indicator.setVisible(true);
}
public void hideIndicator() {
indicator.setVisible(false);
}
public void setIndicatorValue(String value) {
indicatorValue.setValue(value);
}
public final Node getContent() {
return content == null ? null : content.get();
}
#Override
protected void layoutChildren() {
super.layoutChildren();
clipRect.setWidth(getWidth());
clipRect.setHeight(getHeight());
}
}
Scroll Bar disabled why?
It should not extend:
public class VerticlePane extends Control
Related
I am following this tutorial on how to create a Java2D platform game. I followed the code precisely, but when I tried to run it, the player moves way too fast when moving left and when jumping and falling compared to when it's moving right.
My player class looks like this:
package Entity;
import TileMap.*;
import java.awt.*;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
public class Player extends MapObject {
// Player stats
private int health, maxHealth, fire, maxFire, fireCost, fireBallDamage, scratchDamage, scratchRange;
private boolean dead, flinching, firing, scratching, gliding;
private long flinchTimer;
// private ArrayList<FireBall> fireballs;
private ArrayList<BufferedImage[]> sprites;
private final int[] numFrames = { 2, 8, 1, 2, 4, 2, 5 };
// Animation actions
private static final int IDLE = 0;
private static final int WALKING = 1;
private static final int JUMPING = 2;
private static final int FALLING = 3;
private static final int GLIDING = 4;
private static final int FIREBALL = 5;
private static final int SCRATCHING = 6;
private int currentAction;
// Constructor
public Player(TileMap tm) {
super(tm);
width = height = 30;
cWidth = cHeight = 20;
moveSpeed = 0.3;
maxSpeed = 1.6;
stopSpeed = 0.4;
fallSpeed = 0.15;
maxFallSpeed = 4.0;
jumpStart = -4.8;
stopJumpSpeed = 0.3;
facingRight = true;
health = maxHealth = 5;
fire = maxFire = 2500;
fireCost = 200;
fireBallDamage = 5;
//fireBalls = new ArrayList<FireBall>();
scratchDamage = 8;
scratchRange = 40;
// Load sprites
try {
BufferedImage spritesheet =
ImageIO.read( getClass().getResourceAsStream("/Sprites/Player/playersprites.gif"));
sprites = new ArrayList<BufferedImage[]>();
// Loop through animation actions
for (int i = 0; i < 7; i++){
BufferedImage[] bi = new BufferedImage[numFrames[i]];
for (int j = 0; j < numFrames[i]; j++) {
// numFrames[6] = SCRATCH has width = 60
if (i != 6) {
bi[j] = spritesheet.getSubimage(j * width, i * height, width, height);
} else {
bi[j] = spritesheet.getSubimage(j * width * 2, i * height, width, height);
}
}
sprites.add(bi); // Add BufferedImageArray to animations list
}
} catch (Exception e) { e.printStackTrace(); }
animation = new Animation();
currentAction = IDLE;
animation.setFrames(sprites.get(IDLE));
animation.setDelay(400);
}
// Getters
public int getHealth() { return health; }
public int getMaxHealth() { return maxHealth; }
public int getFire() { return fire; }
public int getMaxFire() { return maxFire; }
// Setters
public void setFiring() { firing = true; }
public void setScratching() { scratching = true; }
public void setGliding(boolean b) { gliding = b; }
private void getNextPosition() {
// Movement
if (left) {
dx -= moveSpeed;
if ( dx < -maxSpeed) { dx = 0; }
} else if (right) {
dx += moveSpeed;
if (dx > maxSpeed) { dx = maxSpeed; }
} else {
if (dx > 0) {
dx -= stopSpeed;
if ( dx < 0 ) { dx = 0; }
} else if (dx < 0) {
dx += stopSpeed;
if (dx > 0) { dx = 0; }
}
}
// Cannot attack while moving, unless in the air
if ( currentAction == SCRATCHING || currentAction == FIREBALL && !( jumping || falling)) { dx = 0; }
// Jumping
if (jumping && !falling) {
dy += jumpStart;
falling = true;
}
if (falling) {
if ( dy > 0 && gliding) { dy += fallSpeed * 0.1; }
else { dy += fallSpeed; }
if ( dy > 0 ) { jumping = false; }
if ( dy < 0 && !jumping ) { dy += maxFallSpeed; }
if ( dy > maxFallSpeed ) { dy = maxFallSpeed; }
}
}
public void update() {
// Update position
getNextPosition();
checkTileMapCollision();
setPosition(xtemp, ytemp);
// Set animations
if (scratching) {
if (currentAction != SCRATCHING) {
currentAction = SCRATCHING;
animation.setFrames(sprites.get(SCRATCHING));
animation.setDelay(50);
width = 60;
}
} else if (firing) {
if (currentAction != FIREBALL) {
currentAction = FIREBALL;
animation.setFrames(sprites.get(FIREBALL));
animation.setDelay(100);
width = 30;
}
} else if (dy > 0) {
if (gliding) {
if (currentAction != GLIDING) {
currentAction = GLIDING;
animation.setFrames(sprites.get(GLIDING));
animation.setDelay(100);
width = 30;
}
} else if (currentAction != FALLING) {
currentAction = FALLING;
animation.setFrames(sprites.get(FALLING));
animation.setDelay(100);
width = 30;
}
} else if (dy < 0) {
if ( currentAction != JUMPING) {
currentAction = JUMPING;
animation.setFrames(sprites.get(JUMPING));
animation.setDelay(-1);
width = 30;
}
} else if ( left || right ) {
if ( currentAction != WALKING) {
currentAction = WALKING;
animation.setFrames(sprites.get(WALKING));
animation.setDelay(40);
width = 30;
}
} else {
if ( currentAction != IDLE) {
currentAction = IDLE;
animation.setFrames(sprites.get(IDLE));
animation.setDelay(400);
width = 30;
}
}
animation.update();
// Set direction
if ( currentAction != SCRATCHING && currentAction != FIREBALL) {
if ( right) { facingRight = true; }
if ( left ) { facingRight = false; }
}
}
public void draw(Graphics2D g) {
setMapPosition();
// Draw player
if (flinching) {
long elapsed = (System.nanoTime() - flinchTimer) / 1000000;
if (elapsed / 100 % 2 == 0) { return; } // Gives the appearance of blinking
}
if (facingRight) {
g.drawImage(animation.getImage(), (int) (x + xmap - width/2), (int) (y + ymap - height/2), null);
} else {
g.drawImage(animation.getImage(), (int) (x + xmap - width/2 + width), (int) (y + ymap - height/2),
-width, height, null);
}
}
}
I tried changing
private void getNextPosition() {
// Movement
if (left) {
dx -= moveSpeed;
if ( dx < -maxSpeed) { dx = -maxSpeed; }
to
private void getNextPosition() {
// Movement
if (left) {
dx -= moveSpeed;
if ( dx < -maxSpeed) { dx = 0; }
Which helped for the left-movement, but it created a lag in the graphics, so I tried changing theAnimation-class which looks like this:
package Entity;
import java.awt.image.BufferedImage;
public class Animation {
private BufferedImage[] frames;
private int currentFrame;
private long startTime, delay;
private boolean playedOnce;
public void Animation() {
playedOnce = false;
}
public void setFrames(BufferedImage[] frames) {
this.frames = frames;
currentFrame = 0;
startTime = System.nanoTime();
playedOnce = false;
}
public void setDelay(long d) { delay = d; }
public void setFrame(int i) { currentFrame = i; }
public void update() {
if (delay == -1) { return; }
long elapsed = (System.nanoTime() - startTime) / 1000000;
if (elapsed > delay) {
currentFrame++;
startTime = System.nanoTime();
}
if (currentFrame == frames.length) {
currentFrame = 0;
playedOnce = true;
}
}
public int getFrame() { return currentFrame; }
public BufferedImage getImage() { return frames[currentFrame]; }
public boolean hasPlayedOnce() { return playedOnce; }
}
Which I thought was where I could fix the problem, but I am uncertain as I am new to gamelogic. Is there any way I can slow down the movement of the player? All help is appreciated!
I'm building a custom ASCII terminal java display for make game or application.
I currently have a problem with Swing repaint processing. Some parts of the view is paint but after a little time, they disappear from the screen. This happens quickly when you click anywhere on the screen.
A example of error :
I read the Painting in AWT and Swing but I don't find the solution.
This is the current state of the AsciiPanel state.
package ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.awt.image.ShortLookupTable;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.RepaintManager;
/**
* JPanel with a ASCII render system
*
* #author julien MAITRE
*
*/
public class AsciiPanel extends JPanel {
private Dimension size;
private BufferedImage[] character;
private Color defaultCharacterColor;
private Color defaultCharacterBackgroundColor;
private Dimension characterSize;
private AsciiTerminalDataCell[][] terminal;
private AsciiTerminalDataCell[][] oldTerminal;
private Image image;
private Graphics2D graphics;
private int scale;
public AsciiPanel(Dimension dimension, String tilesetFile, int characterWidth, int characterHeight) {
this(dimension, tilesetFile, characterWidth, characterHeight, 1);
}
public AsciiPanel(Dimension dimension, String tilesetFile, int characterWidth, int characterHeight, int scale) {
this.size = dimension;
this.characterSize = new Dimension(characterWidth, characterHeight);
this.scale = scale;
this.defaultCharacterColor = Color.WHITE;
this.defaultCharacterBackgroundColor = Color.BLACK;
terminal = new AsciiTerminalDataCell[size.height][size.width];
oldTerminal = new AsciiTerminalDataCell[size.height][size.width];
for(int i = 0; i < size.height; i++){
for(int j = 0; j < size.width; j++){
terminal[i][j] = new AsciiTerminalDataCell();
oldTerminal[i][j] = new AsciiTerminalDataCell();
}
}
this.setPreferredSize(new Dimension(size.width*characterSize.width*scale, size.height*characterSize.height*scale));
try {
character = new BufferedImage[256];
BufferedImage tilesets = ImageIO.read(getClass().getResource(tilesetFile));
// Recuperation of the background color
BufferedImage imageBackgroundColor = tilesets.getSubimage(0, 0, 1, 1);
int color = imageBackgroundColor.getRGB(0, 0);
Color m_characterBackgroundColor = Color.getColor(null, color);
// Modification of characters background
Image characterBackgroundColorModified = createImage(new FilteredImageSource(tilesets.getSource(), new AsciiBackgroundFilter(m_characterBackgroundColor)));
// Creation of tileset with a modification of the background color
BufferedImage tilesetsModified = new BufferedImage(tilesets.getWidth(), tilesets.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics graphicsTilesetsModified = tilesetsModified.getGraphics();
graphicsTilesetsModified.setColor(Color.BLACK);
graphicsTilesetsModified.fillRect(0, 0, tilesetsModified.getWidth(), tilesetsModified.getHeight());
// Draw in a BufferedImage for characters recuperation
graphicsTilesetsModified.drawImage(characterBackgroundColorModified, 0, 0, this);
for(int i = 0; i < 256; i++){
int x = (i%16)*characterSize.width;
int y = (i/16)*characterSize.height;
character[i] = new BufferedImage(characterSize.width, characterSize.height, BufferedImage.TYPE_INT_ARGB);
character[i].getGraphics().drawImage(tilesetsModified, 0, 0, characterSize.width, characterSize.height, x, y, x+characterSize.width, y+characterSize.height, this);
}
}
catch (IOException ex) {
Logger.getLogger(AsciiTerminal.class.getName()).log(Level.SEVERE, null, ex);
}
this.setLayout(null);
}
public void write(int positionX, int positionY, char character, Color characterColor){
this.write(positionX, positionY, character, characterColor, defaultCharacterBackgroundColor);
}
public void write(int positionX, int positionY, AsciiTerminalDataCell character){
this.write(positionX, positionY, character.data, character.dataColor, character.backgroundColor);
}
public void write(int positionX, int positionY, char character, Color characterColor, Color characterBackgroundColor){
if(positionX < 0 || positionX > size.width - 1){
throw new IllegalArgumentException("X position between [0 and "+size.width+"]");
}
if(positionY < 0 || positionY > size.height - 1){
throw new IllegalArgumentException("Y position between [0 and "+size.height+"]");
}
terminal[positionY][positionX].data = character;
terminal[positionY][positionX].dataColor = characterColor;
terminal[positionY][positionX].backgroundColor = characterBackgroundColor;
}
public void writeString(int positionX, int positionY, String string, Color characterColor){
writeString(positionX, positionY, string, characterColor, defaultCharacterBackgroundColor);
}
public void writeString(int positionX, int positionY, String string, Color characterColor, Color characterBackgroundColor){
for(char c : string.toCharArray()){
this.write(positionX, positionY, c, characterColor, characterBackgroundColor);
positionX++;
}
}
public AsciiTerminalDataCell readCurrent(int x, int y){
return this.oldTerminal[y][x];
}
public AsciiTerminalDataCell readNext(int x, int y){
return this.terminal[y][x];
}
public void clear(){
clear(0, 0, size.width, size.height);
}
public void clear(int x, int y, int width, int height){
if(x < 0 || x > size.width - 1){
throw new IllegalArgumentException("X position between [0 and "+(size.width-1)+"]");
}
if(y < 0 || y > size.height - 1){
throw new IllegalArgumentException("Y position between [0 and "+(size.height-1)+"]");
}
if(width < 1){
throw new IllegalArgumentException("Width under 1");
}
if(height < 1){
throw new IllegalArgumentException("Height under 1");
}
if(width+x > size.width || height+y > size.height){
throw new IllegalArgumentException("Clear over the terminal");
}
for(int i = y; i < y + height; i++){
for(int j = x; j < x + width; j++) {
write(j, i, (char)0, defaultCharacterColor, defaultCharacterBackgroundColor);
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(image == null) {
image = this.createImage(this.getPreferredSize().width, this.getPreferredSize().height);
graphics = (Graphics2D)image.getGraphics();
graphics.setColor(defaultCharacterBackgroundColor);
graphics.fillRect(0, 0, this.getWidth(), this.getHeight());
}
for(Component component : getComponents()) {
component.paint(graphics);
}
for(int i = 0; i < size.height; i++){
for(int j = 0; j < size.width; j++){
if( terminal[i][j].data == oldTerminal[i][j].data &&
terminal[i][j].dataColor.equals(oldTerminal[i][j].dataColor) &&
terminal[i][j].backgroundColor.equals(oldTerminal[i][j].backgroundColor)) {
continue;
}
LookupOp lookupOp = setColorCharacter(terminal[i][j].backgroundColor, terminal[i][j].dataColor);
graphics.drawImage(lookupOp.filter(character[terminal[i][j].data], null), j*characterSize.width*scale, i*characterSize.height*scale, characterSize.width*scale, characterSize.height*scale, this);
oldTerminal[i][j].data = terminal[i][j].data;
oldTerminal[i][j].dataColor = terminal[i][j].dataColor;
oldTerminal[i][j].backgroundColor = terminal[i][j].backgroundColor;
}
}
g.drawImage(image, 0, 0, this);
}
private LookupOp setColorCharacter(Color bgColor, Color fgColor){
short[] red = new short[256];
short[] green = new short[256];
short[] blue = new short[256];
short[] alpha = new short[256];
// Recuperation of compound colors of foreground character color
short dcr = (short) fgColor.getRed();
short dcg = (short) fgColor.getGreen();
short dcb = (short) fgColor.getBlue();
// Recuperation of compound colors of background character color
short bgr = (short) bgColor.getRed();
short bgg = (short) bgColor.getGreen();
short bgb = (short) bgColor.getBlue();
for(short j = 0; j < 256; j++){
// if is foreground color
if(j != 0){
/**
* Calculation of j*255/dcr .
* Cross product
* dcr = 180 255
* j = ? X
* Distribute the requested color [0 to 255] on the character color [0 to X]
*/
// Red
if(dcr != 0){
red[j] = (short)(j*dcr/255);
}
else{
red[j] = 0;
}
// green
if(dcg != 0){
green[j] = (short)(j*dcg/255);
}
else{
green[j] = 0;
}
// Blue
if( dcb != 0){
blue[j] = (short)(j*dcb/255);
}
else{
blue[j] = 0;
}
// Alpha
alpha[j] = 255;
}
// else is background color
else {
red[j] = bgr;
green[j] = bgg;
blue[j] = bgb;
alpha[j] = 255;
}
}
short[][] data = new short[][]{red, green, blue, alpha};
LookupTable lookupTable = new ShortLookupTable(0, data);
LookupOp lookupOp = new LookupOp(lookupTable, null);
return lookupOp;
}
public Color getDefaultCharacterColor(){
return this.defaultCharacterColor;
}
public void setDefaultCharacterColor(Color color){
this.defaultCharacterColor = color;
}
public Color getDefaultCharacterBackgroundColor() {
return defaultCharacterBackgroundColor;
}
public void setDefaultCharacterBackgroundColor(Color defaultCharacterBackgroundColor) {
this.defaultCharacterBackgroundColor = defaultCharacterBackgroundColor;
}
public Dimension getCharacterSize() {
return characterSize;
}
public int getScale() {
return scale;
}
public AsciiTerminalDataCell getCell(int x, int y) {
return terminal[y][x];
}
}
ChiptuneTracker main class :
package main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URISyntaxException;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import ui.AsciiPanel;
import ui.AsciiTerminalDataCell;
import ui.CustomAsciiTerminal;
public class ChiptuneTracker {
public static final String TITLE = "ChiptuneTracker";
public static final int WINDOW_WIDTH = 29;
public static final int WINDOW_HEIGHT = 18;
public static final String TILESET_FILE = "/assets/wanderlust.png";
public static final String ICON_FILE = "/assets/icon.png";
public static final int CHARACTER_WIDTH = 12;
public static final int CHARACTER_HEIGHT = 12;
public static final int TARGET_FPS = 60;
public static final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
public static final int SCALE = 3;
public static final boolean CUSTOM_WINDOW = true;
private static ChiptuneTracker instance = new ChiptuneTracker();
private CustomAsciiTerminal asciiTerminal;
private AsciiPanel asciiPanel;
private boolean initSampleView = true;
private boolean initPatternView = true;
private Data data = new Data();
private DataManager dataManager;
private boolean changeData = false;
private Chanels chanels = new Chanels();
private View currentView;
private MenuView menuView;
private SampleView sampleView;
private PatternView patternView;
private ChiptuneTracker() {
asciiTerminal = new CustomAsciiTerminal(TITLE, new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT), TILESET_FILE, CHARACTER_WIDTH, CHARACTER_HEIGHT, SCALE, ICON_FILE, CUSTOM_WINDOW);
asciiPanel = asciiTerminal.getAsciiPanel();
asciiPanel.setDefaultCharacterBackgroundColor(Color.DARK_GRAY);
asciiPanel.setDefaultCharacterColor(Color.WHITE);
dataManager = new DataManager();
}
public void init() {
menuView = new MenuView(this);
sampleView = new SampleView(this);
patternView = new PatternView(this);
changeView(sampleView);
}
public void run() {
long lastLoopTime = System.nanoTime();
boolean stopRender = true;
while(true) {
long now = System.nanoTime();
double updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ChiptuneTracker.OPTIMAL_TIME;
// Screenshot
KeyEvent event = asciiTerminal.getEvent();
if(event != null) {
if(event.getKeyCode() == KeyEvent.VK_F12) {
try {
BufferedImage image = new BufferedImage(WINDOW_WIDTH * CHARACTER_WIDTH * SCALE, WINDOW_HEIGHT * CHARACTER_HEIGHT * SCALE, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
asciiPanel.paint(graphics);
boolean save = false;
int i = 0;
do {
File file = new File("screenshot-" + i + ".png");
if(!file.exists()) {
ImageIO.write(image, "PNG", file);
save = true;
}
i++;
} while(!save);
} catch (Exception e) {
JOptionPane.showMessageDialog(ChiptuneTracker.getInstance().getAsciiTerminal(), e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
finally {
asciiTerminal.setEvent(null);
}
}
}
// Update
currentView.update(delta);
chanels.update();
// Paint
asciiPanel.clear();
currentView.paint();
asciiTerminal.revalidate();
asciiTerminal.repaint();
try {
long value = (lastLoopTime - System.nanoTime() + ChiptuneTracker.OPTIMAL_TIME) / 1000000;
if(value > 0) {
Thread.sleep(value);
}
else {
Thread.sleep(5);
}
} catch (InterruptedException e) {
}
}
}
public void changeView(View nextView) {
if(currentView != null) {
currentView.quit();
}
currentView = nextView;
asciiPanel.clear();
currentView.init();
}
public static ChiptuneTracker getInstance() {
return instance;
}
public CustomAsciiTerminal getAsciiTerminal() {
return asciiTerminal;
}
public AsciiPanel getAsciiPanel() {
return asciiPanel;
}
public boolean isInitPatternView() {
return initPatternView;
}
public boolean isInitSampleView() {
return initSampleView;
}
public void setInitPatternView(boolean initPatternView) {
this.initPatternView = initPatternView;
}
public void setInitSampleView(boolean initSampleView) {
this.initSampleView = initSampleView;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public DataManager getDataManager() {
return dataManager;
}
public boolean isChangeData() {
return changeData;
}
public void setChangeData(boolean changeData) {
this.changeData = changeData;
}
public Chanels getChanels() {
return chanels;
}
public MenuView getMenuView() {
return menuView;
}
public SampleView getSampleView() {
return sampleView;
}
public PatternView getPatternView() {
return patternView;
}
public static void main(String[] args) {
ChiptuneTracker chiptuneTracker = ChiptuneTracker.getInstance();
chiptuneTracker.init();
chiptuneTracker.run();
}
}
Is there any solution to resolve that?
Thanks!
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
For my binary tree program, I have been using JTextFields as nodes and I have managed to make it so when I click left button and select two of the JTextFields, it will draw a line between them. The problem is if I want to drag the JtextField around, I want the line to be following as well between two of the midpoints of the JTextField. I don't know if it's possible with using only paintComponent or not. I did try it but it's like a one off thing and it will leave a trail of all the previously drawn lines.
Here, is the code so far, there are other classes so some of the bits won't work. The code has been fiddled around a bit as well for testing.
public class BinaryTreeMainPnl extends JPanel {
public static Graphics g1;
public int Width = 75;
public int Height = 45;
public String tempNode1 = "";
public int tempNode1Pos = 0;
public int tempNode2Pos = 0;
public String tempNode2 = "";
public static boolean leftBtnSelected;
public static boolean rightBtnSelected;
private static int x1, y1 = 50;
private static int x2, y2 = 500;
JToggleButton leftBtn = new JToggleButton("Left");
JToggleButton rightBtn = new JToggleButton("Right");
JTextField[] myArray = new JTextField[60];
ArrayList<String> nodeNames = new ArrayList<String>();
JFrame MainFrame;
JTextField txtFld1 = new JTextField("Enter Questions here");
JButton btn1 = new JButton("Submit");
public BinaryTreeMainPnl(JFrame frame) {
super();
setSize(800, 700);
MainFrame = frame;
readInput();
leftBtn.setFont(new Font("Arial", Font.BOLD, 15));
leftBtn.setForeground(Color.blue);
leftBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
rightBtn.setSelected(false);
leftBtnSelected = leftBtn.getModel().isSelected();
rightBtnSelected = false;
System.out.println(leftBtnSelected);
System.out.println(rightBtnSelected);
}
});
add(leftBtn);
rightBtn.setFont(new Font("Arial", Font.BOLD, 15));
rightBtn.setForeground(Color.green);
rightBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
leftBtn.setSelected(false);
rightBtnSelected = rightBtn.getModel().isSelected();
leftBtnSelected = false;
System.out.println(leftBtnSelected);
System.out.println(rightBtnSelected);
}
});
add(rightBtn);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
updateLine(g);
}
public void readInput() {
btn1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
identifyNodeNames(txtFld1.getText());
displayNodes(nodeNames);
new TreeSorting().treeSort(nodeNames);
}
});
this.add(txtFld1);
this.add(btn1);
}
public void displayNodes(ArrayList<String> nodeNames) {
for (int i = 0; i < nodeNames.size(); i++) {
String currentSetText = nodeNames.get(i);
myArray[i] = new JTextField(currentSetText);
myArray[i].setEditable(false);
}
for (int i = 0; i < nodeNames.size(); i++) {
int I = i;
myArray[I].addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent evt) {
int x = evt.getX() + myArray[I].getX();
int y = evt.getY() + myArray[I].getY();
myArray[I].setBounds(x, y, myArray[I].getWidth(), myArray[I].getWidth());
System.out.println(myArray[I] + "dragged");
}
});
myArray[I].addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent evt) {
if (leftBtnSelected) {
if (tempNode1.equals("")) {
tempNode1 = myArray[I].getText();
System.out.println(tempNode1 + "clicked");
} else {
tempNode2 = myArray[I].getText();
System.out.println(tempNode2 + "Clicked as well");
leftBtn.setSelected(false);
leftBtnSelected = false;
x1 = 40;
y1 = 40;
x2 = 400;
y2 = 400;
updateLine(g1);
System.out.println("asdasd");
}
}
if (rightBtnSelected) {
}
}
public void moveComponent(MouseEvent evt) {
}
});
System.out.println("I " + I);
System.out.println(myArray[I].getText());
add(myArray[I]);
}
MainFrame.revalidate();
}
public int findMidPoint(JTextField temp) {
Point p = temp.getLocation();
Dimension d = temp.getSize();
return p.x + (d.width) / 2;
}
int coun = 0;
public void updateLine(Graphics g) {
g.drawLine(x1, x2, y1, y2);
x1 = x1 + 10;
y1 = y1 + 10;
y2 = y2 + 10;
x2 = x2 + 10;
System.out.println("Line Updated" + coun);
coun++;
}
public void identifyNodeNames(String answer) {
int arrayCounter = 0;
int lastNodePosition = 0;
for (int i = 0; i < answer.length(); i++) {
char c = answer.charAt(i);
if (c == ',') {
nodeNames.add(arrayCounter, answer.substring(lastNodePosition, i + 1).replaceAll(",", "").replaceAll(" ", ""));
lastNodePosition = i + 1;
arrayCounter++;
}
if (i == answer.length() - 1) {
nodeNames.add(arrayCounter, answer.substring(lastNodePosition, answer.length()).replaceAll(" ", ""));
}
}
}
}
import java.util.ArrayList;
public class TreeSorting {
public static int arrayLength;
String[][] Child;
String root = "";
boolean nodeSorted = false;
int parentCounter = 1;
public void treeSort(ArrayList<String> passedQuestions) {
// declaring nodes
arrayLength = passedQuestions.size();
System.out.println(arrayLength + "ARRAY LENGTH");
Child = new String[arrayLength][3];
for (int i = 0; i < arrayLength; i++) {
Child[i][0] = passedQuestions.get(i);
}
//initially calling the mainprocess with parentCounter 1;
root = Child[0][0];
mainProcess(1);
}
public void mainProcess(int parentCounter) {
if (parentCounter < Child.length) {
System.out.println(parentCounter);
sortingTree(Child[parentCounter][0], root, 0); //where the next node is passed on the tree is sorted recursively
for (int i = 0; i < Child.length; i++) {
System.out.println(Child[i][0]);
System.out.println(Child[i][1] + "," + Child[i][2]);
}
}
}
public void sortingTree(String CurrentNode, String PreviousNode, int PreviousPosition) {
nodeSorted = false;// node is not sorted in the beginning
if (isAfter(CurrentNode.toLowerCase(), PreviousNode.toLowerCase())) {
System.out.println(Child[PreviousPosition][2]);
if (Child[PreviousPosition][2] == null) { //checks if the right of the node is empty, if found empty the node is placed there.
Child[PreviousPosition][2] = CurrentNode;
nodeSorted = true; // if the node finds a position in the array node is sorted.
} else {
sortingTree(CurrentNode, Child[PreviousPosition][2], getPositionInArray(Child[PreviousPosition][2]));
//if the array position was not empty, the loop will process again this time with the item found in the filled position.
}
} else if (Child[PreviousPosition][1] == null) { // if the left of the node is empty, the item will be placed there
Child[PreviousPosition][1] = CurrentNode;
nodeSorted = true;
} else {
sortingTree(CurrentNode, Child[PreviousPosition][1], getPositionInArray(Child[PreviousPosition][1]));
//if the array position was not empty, the loop will process again this time with the item found in the filled position.
}
if (nodeSorted) { // if the node finds a position in the array, the nodeCounter increments and the next node in the question is processed.
parentCounter++;
mainProcess(parentCounter);
}
}
public int getPositionInArray(String node) {
int position = 0;
loop:
for (int i = 0; i < Child.length; i++) {
if (Child[i][0].equals(node)) {
position = i;
break loop;
}
}
return position;
}
public boolean isAfter(String CurrentNode, String PreviousNode) {
int loopLength = determineLoopLength(CurrentNode, PreviousNode);
boolean result = false;
String tempCheck = "";
loop:
for (int i = 0; i < loopLength; i++) {
if ((int) CurrentNode.charAt(i) > (int) PreviousNode.charAt(i)) {
result = true;
break loop;
}
if ((int) CurrentNode.charAt(i) < (int) PreviousNode.charAt(i)) {
result = false;
break loop;
} else if (CurrentNode.charAt(i) == PreviousNode.charAt(i) && CurrentNode.length() > PreviousNode.length()) {
System.out.println("I'm here");
tempCheck = tempCheck + CurrentNode.charAt(i) + "";
if (i == loopLength - 1 && tempCheck.equals(PreviousNode)) {
result = true;
break loop;
}
}
}
return result;
}
public int determineLoopLength(String CurrentNode, String PreviousNode) {
int loopLength = 0;
if (CurrentNode.length() < PreviousNode.length()) {
loopLength = CurrentNode.length();
}
if (PreviousNode.length() < CurrentNode.length()) {
loopLength = PreviousNode.length();
} else {
loopLength = CurrentNode.length();
}
return loopLength;
}
}
WTF, been working on this,.....
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class DragMyFields extends JPanel {
private static final int PREF_W = 1000;
private static final int PREF_H = 800;
private static final int COLS = 8;
private static final int DELTA_Y = 120;
private static final int MAX_DEPTH = 3;
private MySimpleTreeNode<JTextField> treeRoot = new MySimpleTreeNode<>();
private MyMouse myMouse = new MyMouse();
public DragMyFields() {
setLayout(null); // this is *** BAD ***
// much better would be to create a custom layout
JTextField field = new JTextField(COLS);
field.addMouseListener(myMouse);
field.addMouseMotionListener(myMouse);
field.setSize(field.getPreferredSize());
int x = (PREF_W - field.getPreferredSize().width) / 2;
int y = DELTA_Y;
field.setLocation(x, y);
add(field);
treeRoot.setNode(field);
recursiveCreateTree(treeRoot, MAX_DEPTH, x, y);
}
private void recursiveCreateTree(MySimpleTreeNode<JTextField> node, int depth, int x, int y) {
if (depth == 0) {
return;
}
JTextField leftField = new JTextField(COLS);
JTextField rightField = new JTextField(COLS);
MySimpleTreeNode<JTextField> leftNode = new MySimpleTreeNode<>(leftField);
MySimpleTreeNode<JTextField> rightNode = new MySimpleTreeNode<>(rightField);
node.setLeft(leftNode);
node.setRight(rightNode);
int multiplier = 4;
for (int i = 0; i < MAX_DEPTH - depth; i++) {
multiplier *= 2;
}
int xL = x - getPreferredSize().width / multiplier;
int xR = x + getPreferredSize().width / multiplier;
y += DELTA_Y;
leftField.setSize(leftField.getPreferredSize());
rightField.setSize(rightField.getPreferredSize());
leftField.setLocation(xL, y);
rightField.setLocation(xR, y);
leftField.addMouseListener(myMouse);
leftField.addMouseMotionListener(myMouse);
rightField.addMouseListener(myMouse);
rightField.addMouseMotionListener(myMouse);
add(leftField);
add(rightField);
recursiveCreateTree(leftNode, depth - 1, xL, y);
recursiveCreateTree(rightNode, depth - 1, xR, y);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
recursiveDraw(g, treeRoot);
}
private void recursiveDraw(Graphics g, MySimpleTreeNode<JTextField> node) {
MySimpleTreeNode<JTextField> left = node.getLeft();
MySimpleTreeNode<JTextField> right = node.getRight();
Point p = getNodeCenter(node);
if (left != null) {
Point p2 = getNodeCenter(left);
g.drawLine(p.x, p.y, p2.x, p2.y);
recursiveDraw(g, left);
}
if (right != null) {
Point p2 = getNodeCenter(right);
g.drawLine(p.x, p.y, p2.x, p2.y);
recursiveDraw(g, right);
}
}
private Point getNodeCenter(MySimpleTreeNode<JTextField> node) {
JTextField field = node.getNode();
Point location = field.getLocation();
Dimension size = field.getSize();
return new Point(location.x + size.width / 2, location.y + size.height / 2);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouse extends MouseAdapter {
Component source = null;
private Point pressedP;
private Point pressedLoc;
private Point parentP;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
source = e.getComponent();
parentP = source.getParent().getLocationOnScreen();
pressedLoc = source.getLocationOnScreen();
pressedP = e.getLocationOnScreen();
}
#Override
public void mouseReleased(MouseEvent e) {
moveComponent(e);
source = null;
pressedP = null;
pressedLoc = null;
}
#Override
public void mouseDragged(MouseEvent e) {
if (source == null) {
return;
}
moveComponent(e);
}
private void moveComponent(MouseEvent e) {
Point p = e.getLocationOnScreen();
int x = pressedLoc.x + p.x - pressedP.x - parentP.x;
int y = pressedLoc.y + p.y - pressedP.y - parentP.y;
Point newP = new Point(x, y);
source.setLocation(newP);
repaint();
}
}
private static void createAndShowGui() {
DragMyFields mainPanel = new DragMyFields();
JFrame frame = new JFrame("DragMyFields");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
class MySimpleTreeNode<T> {
private T node;
private MySimpleTreeNode<T> left;
private MySimpleTreeNode<T> right;
public MySimpleTreeNode() {
// default constructor
}
public MySimpleTreeNode(T node) {
this.node = node;
}
public void setNode(T node) {
this.node = node;
}
public T getNode() {
return node;
}
public MySimpleTreeNode<T> getLeft() {
return left;
}
public void setLeft(MySimpleTreeNode<T> left) {
this.left = left;
}
public MySimpleTreeNode<T> getRight() {
return right;
}
public void setRight(MySimpleTreeNode<T> right) {
this.right = right;
}
}
I am trying to make a puzzle game but the problem is that I can't do the EventHandler setOnMouseClicked.Also I want to know how to make possible that to re-arrange the puzzle so it shows random images in every-coordinate. This is how far I got.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class eliss extends Application{
#Override
public void start(Stage primaryStage) {
ImageView view[][] = new ImageView[5][5];
Image imag = new Image("http://images.cdn.autocar.co.uk/sites/autocar.co.uk/files/styles/gallery_slide/public/ferrari-laferrari-zfye-059_1.jpg?itok=hfLNxUD9",600,600,false,true);
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setVgap(2);
pane.setHgap(2);
PuzzleImage(view, imag, pane);
Scene scene = new Scene(pane,1100,1100);
primaryStage.setTitle("Elis");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] x)
{
launch(x);
}
public void PuzzleImage( ImageView view[][],Image imag,GridPane pane)
{
for(int i=0;i<5;i++)
{
for(int j=0;j<5;j++)
{
if(j==4 && i==4) view[i][j]=null;
else{
view[i][j]=new ImageView(imag);
Rectangle2D rect = new Rectangle2D(120*i,120*j,120,120);
view[i][j].setViewport(rect);
pane.add(view[i][j], i, j);
}
}
}
}
}
What you are asking is too much for a question on StackOverflow. However, I got a little bit of spare time, so I quickly drafted up some code to demonstrate what you need:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PuzzleGame extends Application {
private Image image = new Image("http://images.cdn.autocar.co.uk/sites/autocar.co.uk/files/styles/gallery_slide/public/ferrari-laferrari-zfye-059_1.jpg?itok=hfLNxUD9",600,600,false,true);
private static double SCENE_WIDTH = 1024;
private static double SCENE_HEIGHT = 768;
public static int TILE_ROW_COUNT = 5;
public static int TILE_COLUMN_COUNT = 5;
public static double TILE_SIZE = 120;
public static double offsetX = (SCENE_WIDTH - TILE_ROW_COUNT * TILE_SIZE) / 2;
public static double offsetY = (SCENE_HEIGHT - TILE_COLUMN_COUNT * TILE_SIZE) / 2;
List<Cell> cells = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
// create grid
for (int x = 0; x < TILE_ROW_COUNT; x++) {
for (int y = 0; y < TILE_COLUMN_COUNT; y++) {
// create tile
ImageView tile = new ImageView(image);
Rectangle2D rect = new Rectangle2D(TILE_SIZE * x, TILE_SIZE * y, TILE_SIZE, TILE_SIZE);
tile.setViewport(rect);
// consider empty cell, let it remain empty
if (x == (TILE_ROW_COUNT - 1) && y == (TILE_COLUMN_COUNT - 1)) {
tile = null;
}
cells.add(new Cell(x, y, tile));
}
}
// shuffle cells
shuffle();
// create playfield
Pane pane = new Pane();
// put tiles on playfield, assign event handler
for (int i = 0; i < cells.size(); i++) {
Cell cell = cells.get(i);
Node imageView = cell.getImageView();
// consider empty cell
if (imageView == null)
continue;
// click-handler: swap tiles, check if puzzle is solved
imageView.addEventFilter(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
moveCell((Node) mouseEvent.getSource());
});
// position images on scene
imageView.relocate(cell.getLayoutX(), cell.getLayoutY());
pane.getChildren().add(cell.getImageView());
}
Scene scene = new Scene(pane, SCENE_WIDTH, SCENE_HEIGHT);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* Swap images of cells randomly
*/
public void shuffle() {
Random rnd = new Random();
for (int i = 0; i < 1000; i++) {
int a = rnd.nextInt(cells.size());
int b = rnd.nextInt(cells.size());
if (a == b)
continue;
// skip bottom right cell swap, we want the empty cell to remain there
if( cells.get(a).isEmpty() || cells.get(b).isEmpty())
continue;
swap( cells.get(a), cells.get(b));
}
}
public void swap( Cell cellA, Cell cellB) {
ImageView tmp = cellA.getImageView();
cellA.setImageView(cellB.getImageView());
cellB.setImageView(tmp);
}
public boolean checkSolved() {
boolean allSolved = true;
for (Cell cell : cells) {
if (!cell.isSolved()) {
allSolved = false;
break;
}
}
System.out.println("Solved: " + allSolved);
return allSolved;
}
public void moveCell(Node node) {
// get current cell using the selected node (imageview)
Cell currentCell = null;
for (Cell tmpCell : cells) {
if (tmpCell.getImageView() == node) {
currentCell = tmpCell;
break;
}
}
if (currentCell == null)
return;
// get empty cell
Cell emptyCell = null;
for (Cell tmpCell : cells) {
if (tmpCell.isEmpty()) {
emptyCell = tmpCell;
break;
}
}
if (emptyCell == null)
return;
// check if cells are swappable: neighbor distance either x or y must be 1 for a valid move
int steps = Math.abs(currentCell.x - emptyCell.x) + Math.abs(currentCell.y - emptyCell.y);
if (steps != 1)
return;
System.out.println("Transition: " + currentCell + " -> " + emptyCell);
// cells are swappable => create path transition
Path path = new Path();
path.getElements().add(new MoveToAbs(currentCell.getImageView(), currentCell.getLayoutX(), currentCell.getLayoutY()));
path.getElements().add(new LineToAbs(currentCell.getImageView(), emptyCell.getLayoutX(), emptyCell.getLayoutY()));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(100));
pathTransition.setNode(currentCell.getImageView());
pathTransition.setPath(path);
pathTransition.setOrientation(PathTransition.OrientationType.NONE);
pathTransition.setCycleCount(1);
pathTransition.setAutoReverse(false);
final Cell cellA = currentCell;
final Cell cellB = emptyCell;
pathTransition.setOnFinished(actionEvent -> {
swap( cellA, cellB);
checkSolved();
});
pathTransition.play();
}
private static class Cell {
int x;
int y;
ImageView initialImageView;
ImageView currentImageView;
public Cell(int x, int y, ImageView initialImageView) {
super();
this.x = x;
this.y = y;
this.initialImageView = initialImageView;
this.currentImageView = initialImageView;
}
public double getLayoutX() {
return x * TILE_SIZE + offsetX;
}
public double getLayoutY() {
return y * TILE_SIZE + offsetY;
}
public ImageView getImageView() {
return currentImageView;
}
public void setImageView(ImageView imageView) {
this.currentImageView = imageView;
}
public boolean isEmpty() {
return currentImageView == null;
}
public boolean isSolved() {
return this.initialImageView == currentImageView;
}
public String toString() {
return "[" + x + "," + y + "]";
}
}
// absolute (layoutX/Y) transitions using the pathtransition for MoveTo
public static class MoveToAbs extends MoveTo {
public MoveToAbs(Node node) {
super(node.getLayoutBounds().getWidth() / 2, node.getLayoutBounds().getHeight() / 2);
}
public MoveToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
// absolute (layoutX/Y) transitions using the pathtransition for LineTo
public static class LineToAbs extends LineTo {
public LineToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
public static void main(String[] args) {
launch(args);
}
}
You can use Collections.shuffle to generate a random permutation of a List This can be used to place the tiles in a random order:
private static final int COLUMN_COUNT = 5;
private static final int ROW_COUNT = 5;
private static void fillGridPane(GridPane pane, ImageView[][] view, Image imag) {
List<ImageView> images = new ArrayList<>(24);
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0, end = i == (COLUMN_COUNT - 1) ? ROW_COUNT - 1 : ROW_COUNT; j < end; j++) {
ImageView iv = new ImageView(imag);
images.add(iv);
view[i][j] = iv;
Rectangle2D rect = new Rectangle2D(120 * i, 120 * j, 120, 120);
iv.setViewport(rect);
}
}
Collections.shuffle(images);
Iterator<ImageView> iter = images.iterator();
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0, end = i == (COLUMN_COUNT - 1) ? ROW_COUNT - 1 : ROW_COUNT; j < end; j++) {
pane.add(iter.next(), i, j);
}
}
}
The onMouseClicked event handler can be attached to the GridPane and MouseEvent.getTarget can be used to get the node that was clicked:
pane.setOnMouseClicked(event -> {
if (move(event)) {
if (checkWin()) {
new Alert(Alert.AlertType.INFORMATION, "You Win!").show();
}
}
});
private int emptyTileX = 4;
private int emptyTileY = 4;
private boolean move(MouseEvent event) {
Object target = event.getTarget();
if (target instanceof ImageView) {
ImageView iv = (ImageView) target;
int row = GridPane.getRowIndex(iv);
int column = GridPane.getColumnIndex(iv);
int dx = Math.abs(column - emptyTileX);
int dy = Math.abs(row - emptyTileY);
if ((dx == 0 && dy == 1) || (dx == 1 && dy == 0)) {
// swap image and empty tile, if they are next to each other
GridPane.setConstraints(iv, emptyTileX, emptyTileY);
emptyTileX = column;
emptyTileY = row;
return true;
}
}
return false;
}
To check the winning condition, you can simply check the positions of the ImageViews in the GridPane
private boolean checkWin() {
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0, end = i == (COLUMN_COUNT - 1) ? ROW_COUNT - 1 : ROW_COUNT; j < end; j++) {
ImageView iv = view[i][j];
if (GridPane.getColumnIndex(iv) != i || GridPane.getRowIndex(iv) != j) {
return false;
}
}
}
return true;
}
I'm writing an android program and i just ran into a problem.
My program serves to create graphs, run some specific algorithms on them (Dijsktra, BelmanFord, etc.), save the graphs to SD Card and loading them back.
The problem: If i save a little bigger, more complex graph i get a stackoverflow error..
Serialization:
public void createExternalStoragePublicGraph(String filename) {
File dir = Environment.getExternalStoragePublicDirectory("grapher");
try {
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, filename);
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(graphDrawerView.getGraph());
objectOutputStream.close();
} catch (IOException e) {
Log.w("ExternalStorage", "Error Writing" + filename, e);
}
}
Deserialization:
public Graph loadExternalStoragePublicGraph(String filename) {
File file = new File(Environment.getExternalStoragePublicDirectory("grapher"), filename);
Graph graph = null;
try {
FileInputStream fint = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fint);
graph = (Graph) ois.readObject();
ois.close();
} catch (Exception e) {
Log.e("Deserialization error:", e.getMessage());
e.printStackTrace();
}
return graph;
}
Graph class:
package com.cslqaai.grapher;
import android.util.Log;
import java.io.Serializable;
import java.util.LinkedList;
public class Graph implements Serializable {
private String name;
private boolean directed = false;
private boolean weighted = false;
private LinkedList<Vertex> vertexes = new LinkedList<Vertex>();
private LinkedList<Edge> edges = new LinkedList<Edge>();
// --------------------------------------------------------------------------------------------------------------------------------------
public Graph(boolean weighted, boolean directed) {
this.directed = directed;
this.weighted = weighted;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void add(AbstractGraphObject ago) {
if (ago instanceof Vertex) {
this.vertexes.add((Vertex) ago);
} else if (ago instanceof Edge) {
this.edges.add((Edge) ago);
} else {
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isWeighted() {
return this.weighted;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isDirected() {
return this.directed;
}
// --------------------------------------------------------------------------------------------------------------------------------------\
public LinkedList<Edge> getEdges() {
return this.edges;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public LinkedList<Vertex> getVertexes() {
return this.vertexes;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void reset() {
for (int i = 0; i < this.edges.size(); i++) {
this.edges.get(i).setColorToDefault();
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
void remove(AbstractGraphObject ago) {
if (ago instanceof Vertex) {
Log.d("Graph", "Remove Vertex from graph");
this.vertexes.remove((Vertex) ago);
} else if (ago instanceof Edge) {
Log.d("Graph", "Remove Edge to graph");
this.edges.remove((Edge) ago);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setWeighted(boolean weighted) {
this.weighted = weighted;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setDirected(boolean directed) {
this.directed = directed;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public String getName() {
return this.name;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setName(String name) {
this.name = name;
}
}
// ----------------------------------------------------------------------------------------------------------------------------------------
Vertex class:
package com.cslqaai.grapher;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
public class Vertex extends AbstractGraphObject {
public static final int RADIUS_SIZE = 20;
public static final int SURROUNDING_RADIUS_SIZE = 30;
private static Layer defaultVertexLayer = AbstractGraphObject.defaultLayer;
private static int no = 0;
private static Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint paintColored = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final int id;
private String name = null;
private Coord coord;
private Coord cachedCoord;
private ArrayList<Edge> edges = new ArrayList<Edge>();
private boolean colored = false;
// --------------------------------------------------------------------------------------------------------------------------------------
static {
Vertex.paint.setColor(0xFF0FF5F5);
Vertex.paintColored.setColor(Color.RED);
Vertex.textPaint.setStyle(Paint.Style.FILL);
Vertex.textPaint.setColor(Color.BLACK);
Vertex.textPaint.setTextAlign(Paint.Align.CENTER);
Vertex.textPaint.setTextSize(20);
Vertex.textPaint.setTypeface(Typeface.create("Helvetica", Typeface.BOLD));
Vertex.textBgPaint.setStyle(Paint.Style.FILL);
Vertex.textBgPaint.setColor(0xFF0FF5F5);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex(Coord coord) {
super(Vertex.defaultVertexLayer);
this.id = Vertex.no++;
this.coord = coord;
this.recalculate();
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex(int x, int y) {
this(new Coord(x, y));
}
// --------------------------------------------------------------------------------------------------------------------------------------
#Override
public void recalculate() {
this.cachedCoord = new Coord(Math.round((Vertex.baseX + this.coord.getX()) * Vertex.scaleFactor), Math.round((Vertex.baseY + this.coord.getY()) * Vertex.scaleFactor));
this.onScreen = this.cachedCoord.getX() + Vertex.RADIUS_SIZE > 0 && this.cachedCoord.getY() + Vertex.RADIUS_SIZE > 0
&& this.cachedCoord.getX() - Vertex.RADIUS_SIZE < Vertex.screenWidth && this.cachedCoord.getY() - Vertex.RADIUS_SIZE < this.cachedCoord.getY();
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Coord getCoord() {
return this.coord;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Coord getCachedCoord() {
return this.cachedCoord;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getRadiusSize() {
return Vertex.RADIUS_SIZE;
}
// --------------------------------------------------------------------------------------------------------------------------------------
#Override
public void draw(Canvas canvas) {
this.recalculate();
if (!this.onScreen) {
return;
}
canvas.drawCircle(this.cachedCoord.getX(), this.cachedCoord.getY(), Vertex.RADIUS_SIZE * Vertex.scaleFactor, Vertex.paint);
if (this.name != null) {
float width = Vertex.textPaint.measureText(this.name) + 10;
float height = Vertex.textPaint.getTextSize() + 5;
canvas.drawRect(this.cachedCoord.getX() - width / 2, this.cachedCoord.getY() - height / 2, this.cachedCoord.getX() + width / 2, this.cachedCoord.getY() + height / 2, Vertex.textBgPaint);
canvas.drawText(this.name, this.cachedCoord.getX(), this.cachedCoord.getY() + height * 0.25f, Vertex.textPaint);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
private boolean searchingCoordOn(float radius, float pX, float pY) {
return this.onScreen && ((Math.pow(pX - this.cachedCoord.getX(), 2) + Math.pow(pY - this.cachedCoord.getY(), 2)) < (Math.pow(radius * Vertex.scaleFactor, 2)));
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isOnCoord(float pX, float pY) {
return this.searchingCoordOn(Vertex.RADIUS_SIZE, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isInCoordSurroundings(float pX, float pY) {
return this.searchingCoordOn(Vertex.SURROUNDING_RADIUS_SIZE, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void addEdge(Edge edge) {
if (!this.edges.contains(edge)) {
this.edges.add(edge);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public ArrayList<Edge> getEdges() {
return this.edges;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void removeEdge(Edge edge) {
if (this.edges.contains(edge)) {
this.edges.remove(edge);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean hasEdge(Edge edge) {
return this.edges.contains(edge);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void setDefaultLayer(Layer layer) {
Vertex.defaultVertexLayer = layer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static Layer getDefaultLayer() {
return Vertex.defaultVertexLayer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void remove(Vertex vertex) {
Iterator<Edge> edges = vertex.getEdges().iterator();
while (edges.hasNext()) {
Edge e = edges.next();
edges.remove();
Edge.remove(e);
}
Vertex.defaultVertexLayer.remove(vertex);
if (Vertex.graph != null) {
Vertex.graph.remove(vertex);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean hasName() {
return this.name != null;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setName(String name) {
this.name = name;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public String getName() {
return this.name;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getId() {
return this.id;
}
// --------------------------------------------------------------------------------------------------------------------------------------
}
Edge class:
package com.cslqaai.grapher;
import android.graphics.*;
import java.util.ArrayList;
import java.util.LinkedList;
public class Edge extends AbstractGraphObject {
public static final float STROKE_WIDTH = 5f;
public static final float SENSOR_WIDTH = 50f;
public static final float SURROUNDING_SENSOR_WIDTH = 15f;
public static final float TRIANGLE_SIZE = 8f;
private static Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint paintColored = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Layer defaultEdgeLayer = AbstractGraphObject.defaultLayer;
private static int no = 0;
private final int id;
private int weight = 1;
private Coord cachedSourceCoord;
private Coord cachedTargetCoord;
private Vertex sourceVertex;
private Vertex targetVertex;
private Coord weightCoord;
private boolean colored = false;
// --------------------------------------------------------------------------------------------------------------------------------------
static {
Edge.paint.setColor(0xFFFFFFFF);
Edge.paint.setStrokeWidth(Edge.STROKE_WIDTH * Edge.scaleFactor);
Edge.paint.setStrokeCap(Paint.Cap.ROUND);
Edge.paint.setStyle(Paint.Style.FILL_AND_STROKE);
Edge.paintColored.setColor(0xFFFF0000);
Edge.paintColored.setStrokeWidth(Edge.STROKE_WIDTH * Edge.scaleFactor);
Edge.paintColored.setStrokeCap(Paint.Cap.ROUND);
Edge.paintColored.setStyle(Paint.Style.FILL_AND_STROKE);
Edge.textPaint.setStyle(Paint.Style.FILL);
Edge.textPaint.setColor(Color.BLACK);
Edge.textPaint.setTextAlign(Paint.Align.CENTER);
Edge.textPaint.setTextSize(20);
Edge.textPaint.setTypeface(Typeface.create("Helvetica", Typeface.BOLD));
Edge.textBgPaint.setStyle(Paint.Style.FILL);
Edge.textBgPaint.setColor(Color.WHITE);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Edge(Vertex sourceVertex, Vertex targetVertex) {
super(Edge.defaultEdgeLayer);
this.id = Edge.no++;
this.sourceVertex = sourceVertex;
this.targetVertex = targetVertex;
this.sourceVertex.addEdge(this);
this.targetVertex.addEdge(this);
this.recalculate();
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void recalculate() {
this.cachedSourceCoord = new Coord(Math.round((Edge.baseX + this.sourceVertex.getCoord().getX()) * Edge.scaleFactor), Math.round((Edge.baseY + this.sourceVertex.getCoord().getY()) * Edge.scaleFactor));
this.cachedTargetCoord = new Coord(Math.round((Edge.baseX + this.targetVertex.getCoord().getX()) * Edge.scaleFactor), Math.round((Edge.baseY + this.targetVertex.getCoord().getY()) * Edge.scaleFactor));
Line line = new Line(this.cachedSourceCoord, this.cachedTargetCoord);
this.weightCoord = line.getMiddle();
this.onScreen = Edge.screenBottomLine.hasIntersection(line)
|| Edge.screenLeftLine.hasIntersection(line)
|| Edge.screenRightLine.hasIntersection(line)
|| Edge.screenTopLine.hasIntersection(line)
|| this.cachedSourceCoord.getX() > 0 && this.cachedSourceCoord.getX() < Edge.screenWidth && this.cachedSourceCoord.getY() > 0 && this.cachedSourceCoord.getY() < Edge.screenHeight
|| this.cachedTargetCoord.getX() > 0 && this.cachedTargetCoord.getX() < Edge.screenWidth && this.cachedTargetCoord.getY() > 0 && this.cachedTargetCoord.getY() < Edge.screenHeight;
}
// --------------------------------------------------------------------------------------------------------------------------------------
#Override
public void draw(Canvas canvas) {
this.recalculate();
if (!this.onScreen) {
return;
}
canvas.drawLine(this.cachedSourceCoord.getX(), this.cachedSourceCoord.getY(), this.cachedTargetCoord.getX(), this.cachedTargetCoord.getY(), this.colored ? Edge.paintColored : Edge.paint);
if (Edge.graph != null && Edge.graph.isDirected()) {
Line line = new Line(this.cachedSourceCoord, this.cachedTargetCoord);
Coord v = line.getVector();
float t = (float) ((Vertex.RADIUS_SIZE + 5) / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord t1 = new Coord((int) (this.cachedTargetCoord.getX() - t * v.getX()), (int) (this.cachedTargetCoord.getY() - t * v.getY()));
if (!line.isOnLine(t1)) {
t1 = new Coord((int) (this.cachedTargetCoord.getX() + t * v.getX()), (int) (this.cachedTargetCoord.getY() + t * v.getY()));
}
t = (float) ((Vertex.RADIUS_SIZE + 5 + Edge.TRIANGLE_SIZE) / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord p = new Coord((int) (this.cachedTargetCoord.getX() - t * v.getX()), (int) (this.cachedTargetCoord.getY() - t * v.getY()));
if (!line.isOnLine(p)) {
p = new Coord((int) (this.cachedTargetCoord.getX() + t * v.getX()), (int) (this.cachedTargetCoord.getY() + t * v.getY()));
}
v = line.getNormalVector().getVector();
t = (float) ((Edge.TRIANGLE_SIZE) / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord t2 = new Coord((int) (p.getX() - t * v.getX()), (int) (p.getY() - t * v.getY()));
Coord t3 = new Coord((int) (p.getX() + t * v.getX()), (int) (p.getY() + t * v.getY()));
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(t1.getX(), t1.getY());
path.lineTo(t2.getX(), t2.getY());
path.lineTo(t3.getX(), t3.getY());
path.lineTo(t1.getX(), t1.getY());
path.close();
canvas.drawPath(path, Edge.paint);
}
if (Edge.graph != null && Edge.graph.isWeighted()) {
float width = Edge.textPaint.measureText(Integer.toString(this.weight)) + 10;
float height = Edge.textPaint.getTextSize() + 5;
canvas.drawRect(this.weightCoord.getX() - width / 2, this.weightCoord.getY() - height / 2, this.weightCoord.getX() + width / 2, this.weightCoord.getY() + height / 2, Edge.textBgPaint);
canvas.drawText(Integer.toString(this.weight), this.weightCoord.getX(), this.weightCoord.getY() + height * 0.25f, Edge.textPaint);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
private boolean searchingCoordOn(float distance, float pX, float pY) {
Coord p = new Coord((int) pX, (int) pY);
Coord v = (new Line(this.cachedSourceCoord, this.cachedTargetCoord)).getNormalVector().getVector();
float t = (float) (distance / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord c1 = new Coord((int) (this.cachedSourceCoord.getX() - t * v.getX()), (int) (this.cachedSourceCoord.getY() - t * v.getY()));
Coord c2 = new Coord((int) (this.cachedSourceCoord.getX() + t * v.getX()), (int) (this.cachedSourceCoord.getY() + t * v.getY()));
Coord c3 = new Coord((int) (this.cachedTargetCoord.getX() - t * v.getX()), (int) (this.cachedTargetCoord.getY() - t * v.getY()));
Coord v1 = new Coord(c2.getX() - c1.getX(), c2.getY() - c1.getY());
Coord v2 = new Coord(c3.getX() - c1.getX(), c3.getY() - c1.getY());
v = Coord.minus(p, c1);
return this.onScreen
&& 0 <= Coord.dotProduct(v, v1) && Coord.dotProduct(v, v1) <= Coord.dotProduct(v1, v1)
&& 0 <= Coord.dotProduct(v, v2) && Coord.dotProduct(v, v2) <= Coord.dotProduct(v2, v2);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isOnCoord(float pX, float pY) {
return this.searchingCoordOn(Edge.SENSOR_WIDTH / 2, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isInCoordSurroundings(float pX, float pY) {
return this.searchingCoordOn(Edge.SURROUNDING_SENSOR_WIDTH / 2, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex getSourceVertex() {
return this.sourceVertex;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex getTargetVertex() {
return this.targetVertex;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void setDefaultLayer(Layer layer) {
Edge.defaultEdgeLayer = layer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static Layer getDefaultLayer() {
return Edge.defaultEdgeLayer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void remove(Edge edge) {
edge.getSourceVertex().removeEdge(edge);
edge.getTargetVertex().removeEdge(edge);
Edge.defaultEdgeLayer.remove(edge);
if (Edge.graph != null) {
Edge.graph.remove(edge);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setWeight(int weight) {
this.weight = weight;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getWeight() {
return this.weight;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getId() {
return this.id;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setColored() {
this.colored = true;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setColorToDefault() {
this.colored = false;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static ArrayList<Edge> getEdgesBetween(Vertex v1, Vertex v2) {
ArrayList<Edge> edges = v1.getEdges();
ArrayList<Edge> edgesRet = new ArrayList<Edge>();
for (Edge edge : edges) {
if (v2.hasEdge(edge)) {
edgesRet.add(edge);
}
}
return edges;
}
// --------------------------------------------------------------------------------------------------------------------------------------
}
The reason for this is that the "normal" serialization would for each entry recursively invoke another writeObject(e), and for long lists this would give a StackOverflowError. The iterative serialization avoids this.
I found this on stackoverflow. I think my problem is related to my long LinkedLists..
Any ideas, advices how to properly serialize and deserialize my Graph object?
Thank You In Advance!
As a fast fix, you could try substuting the LinkedList List implemetnations with ArrayLists. You didn't give enough details (the exact exception and the implementations of the vertexes and edges would be helpful), but what you quoted might be right: we run out of memory because of the recursive calls. I didn't try, but the ArrayList default Java serialization would avoid this issue.
For a really good solution, the serialization methods of the graph objects could be re-implemented, or a tested and more memory efficient library could be used (eg. kryo).
This question would also be helpful.
Try runnning your program with more RAM. StackOverFlowError means, that the stack is filled and cant take any other method. What you could also do, is run the deserialization in another Thread; They have a seperate stack. There are several ways to do so, I recommend using the classes from java.util.concurrent.