I am trying to create a program that runs off of a thread instead of a timeline. Here is my modified program down below. I am not sure why it will not work. Any tips would be appreciated. The thread uses a task in order to start the animation. Thanks for your help.
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ch30 extends Application {
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
FanPane fan = new FanPane();
HBox hBox = new HBox(5);
Button btPause = new Button("Pause");
Button btResume = new Button("Resume");
Button btReverse = new Button("Reverse");
hBox.setAlignment(Pos.CENTER);
hBox.getChildren().addAll(btPause, btResume, btReverse);
BorderPane pane = new BorderPane();
pane.setCenter(fan);
pane.setBottom(hBox);
// Create a scene and place it in the stage
Scene scene = new Scene(pane, 200, 200);
primaryStage.setTitle("Exercise15_28"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
Runnable first = new Begin();
Thread t1 = new Thread(first);
t1.start();
//Timeline animation = new Timeline(
//new KeyFrame(Duration.millis(100), e -> fan.move()));
//animation.setCycleCount(Timeline.INDEFINITE);
//animation.play(); // Start animation
scene.widthProperty().addListener(e -> fan.setW(fan.getWidth()));
scene.heightProperty().addListener(e -> fan.setH(fan.getHeight()));
//btPause.setOnAction(e -> first.wait());
btResume.setOnAction(e -> first.run());
btReverse.setOnAction(e -> fan.reverse());
}
/**
* The main method is only needed for the IDE with limited
* JavaFX support. Not needed for running from the command line.
*/
public static void main(String[] args) {
launch(args);
}
}
class FanPane extends Pane {
private double w = 200;
private double h = 200;
private double radius = Math.min(w, h) * 0.45;
private Arc arc[] = new Arc[4];
private double startAngle = 30;
private Circle circle = new Circle(w / 2, h / 2, radius);
public FanPane() {
circle.setStroke(Color.BLACK);
circle.setFill(Color.WHITE);
getChildren().add(circle);
for (int i = 0; i < 4; i++) {
arc[i] = new Arc(w / 2, h / 2, radius * 0.9, radius * 0.9, startAngle + i * 90, 35);
arc[i].setFill(Color.RED); // Set fill color
arc[i].setType(ArcType.ROUND);
getChildren().addAll(arc[i]);
}
}
private double increment = 5;
public void reverse() {
increment = -increment;
}
public void move() {
setStartAngle(startAngle + increment);
}
public void setStartAngle(double angle) {
startAngle = angle;
setValues();
}
public void setValues() {
radius = Math.min(w, h) * 0.45;
circle.setRadius(radius);
circle.setCenterX(w / 2);
circle.setCenterY(h / 2);
for (int i = 0; i < 4; i++) {
arc[i].setRadiusX(radius * 0.9);
arc[i].setRadiusY(radius * 0.9);
arc[i].setCenterX(w / 2);
arc[i].setCenterY(h / 2);
arc[i].setStartAngle(startAngle + i * 90);
}
}
public void setW(double w) {
this.w = w;
setValues();
}
public void setH(double h) {
this.h = h;
setValues();
}
}
class Begin implements Runnable {
private int times = 1000;
FanPane fan = new FanPane();
public Begin(){
// times = t;
}
#Override
public void run(){
//for (int i = 0; i < times; i++)
//{
fan.move();
}
}
Your thread needs a loop, that repeatedly pauses and then updates the UI. If you want to use an explicit thread instead of the more obvious Timeline, you need to write the code for that loop yourself.
The update of the UI cannot be called from the background thread you are creating: to update the UI on the FX Application thread you must wrap the call to the method(s) that update the UI in Platform.runLater(...).
Related
Making a game using Flutter Flame engine and AndroidStudio for development. The render function uses canvas.save() and canvas.render() functions that ensure that the screen is clear between frames (to prevent duplicate sprite drawing). On IPhone simulator the app runs as expected, however on multiple Android Simulators duplicate sprites are rendered. Can't figure out why this is happening... Any help appreciated!
import 'dart:ui';
import 'package:flame/components/component.dart';
import 'package:flame/flame.dart';
import 'package:flame/game.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ThisGame extends Game {
final SharedPreferences storage;
Size screenSize;
double tileSize;
Player player;
double spriteSize = 128;
double timeElapsed = 0;
ThisGame(this.storage) {
initialize();
}
initialize() async {
resize(await Flame.util.initialDimensions());
player = Player();
player.x = 0;
player.y = 0;
}
#override
void render(Canvas canvas) {
canvas.save();
player.render(canvas);
canvas.restore();
}
#override
void update(double t) {
timeElapsed += t;
if (timeElapsed > 1) {
timeElapsed = 0;
player.y += t * 300;
player.x += t * 300;
}
}
#override
void resize(Size size) {
screenSize = size;
tileSize = screenSize.width / 9;
}
}
class Player extends SpriteComponent {
Player() : super.square(128.0, 'player.png') {
this.angle = 0.0;
}
}
You don't have to handle the render method yourself, just add your SpriteComponent to the game and it will handle the rendering for you.
class ThisGame extends Game {
final SharedPreferences storage;
Size screenSize;
double tileSize;
Player player;
double spriteSize = 128;
double timeElapsed = 0;
ThisGame(this.storage) {
initialize();
}
initialize() async {
resize(await Flame.util.initialDimensions());
player = Player();
player.x = 0;
player.y = 0;
add(player); // <-- Like this
}
#override
void update(double t) {
super.update(t) // Also don't forget to call super
timeElapsed += t;
if (timeElapsed > 1) {
timeElapsed = 0;
player.y += t * 300;
player.x += t * 300;
}
}
#override
void resize(Size size) {
screenSize = size;
tileSize = screenSize.width / 9;
}
}
class Player extends SpriteComponent {
Player() : super.square(128.0, 'player.png') {
this.angle = 0.0;
}
}
Also, don't forget to call super.update(t) in your update override.
I'm trying to build a jigsaw-like app for Android that involves the user moving polygonal pieces around the screen by touch event.
I can draw - for example - a triangle, and fill it. I can also move it smoothly round the screen but it leaves a trace - so that the screen rapidly fills up :-(
So I am wondering how to adjust the onDraw method so that the previous positions of the triangle are not included. Or is there some other technique? The question has been asked once before but did not get a satisfactory answer. I am quite new to Android work so I am sure a kind expert will be able to point me in the right direction!
The view:
public class GameView extends View {
public Triangle T1;
Paint paint = new Paint();
private Path path;
public GameView(Context context) {
super(context);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
initTriangle();
}
public void initTriangle() {
int T1Points[][] = new int[3][2];
T1Points[0][0] = -200;
T1Points[0][1] = -100;
T1Points[1][0] = 200;
T1Points[1][1] = -100;
T1Points[2][0] = 0;
T1Points[2][1] = 100;
float[] position = new float[2];
position[0] = (float) 200.0;
position[1] = (float) 100.0;
T1 = new Triangle("T1", T1Points, position);
path = T1.getPath();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float[] pos = new float[2];
pos[0] = event.getX(); //nextx;
pos[1] = event.getY(); //nexty;
T1.setPosition(pos );
Path path = new Path();
path = T1.getPath();
postInvalidate();
return true;
}
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
}
Triangle class:
public class Triangle {
public int[][] myPoints;
public Path path;
public String myname;
public float[] position;
public Triangle (String name, int[][] newpoints, float[] posn) {
myPoints = new int[4][2];
int i;
for (i = 0; i < 3; i++) {
myPoints[i][0] = newpoints[i][0];
myPoints[i][1] = newpoints[i][1];
}
myPoints[3][0] = newpoints[0][0]; // closed circuit for future needs
myPoints[3][1] = newpoints[0][1];
path = new Path();
position = new float[2];
position[0] = posn[0];
position[1] = posn[1];
myname = name;
updatePath();
}
public void setPosition(float[] newPosition){
position[0] = newPosition[0];
position[1] = newPosition[1];
updatePath();
}
public void updatePath(){
int startx = myPoints[0][0] + Math.round(position[0]);
int starty = myPoints[0][1] + Math.round(position[1]);
path.moveTo(startx,starty);
for (int i = 1; i < myPoints.length; i++)
{
int newx = myPoints[i][0] + Math.round(position[0]);
int newy = myPoints[i][1] + Math.round(position[1]);
path.lineTo(newx,newy);
}
path.close();
}
public Path getPath() {
return path;
}
}
Main Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
uk.mydomain.animatedtriangle.GameView gameView = new uk.mydomain.animatedtriangle.GameView(this);
RelativeLayout relativeLayout = new RelativeLayout(this);
RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT);
relativeLayout.setLayoutParams(relativeParams);
setContentView(relativeLayout);
relativeLayout.addView(gameView);
}
Sorted! Just needed to add a path.reset() at the end of onDraw.
I tried to implement this solution for StackedBar Chart but it turns out that there is no Java method getBarGap() in StackedBar chart. Is there any solution into the latest JavaFX version for this problem?
Basic example:
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class MainApp extends Application
{
private StackedBarChart<String, Number> stackedChart;
private List<EventsObj> eventsObj;
#Override
public void start(Stage stage) throws Exception
{
createStackedChart();
List<EventsObj> testData = generateTestData();
addStackedChartData(testData);
HBox hb = new HBox();
hb.getChildren().add(stackedChart);
Scene scene = new Scene(hb);
stage.setTitle("JavaFX and Maven");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
private void createStackedChart()
{
CategoryAxis xAxis = new CategoryAxis();
xAxis.setLabel("Days");
NumberAxis yAxis = new NumberAxis();
stackedChart = new StackedBarChart<>(xAxis, yAxis);
stackedChart.setCategoryGap(20);
stackedChart.widthProperty().addListener((obs, b, b1) ->
{
// Chart Bar column is not automatically resized. We need to wait for next JavaFX releases to fix this.
Platform.runLater(() -> setMaxBarWidth(stackedChart, xAxis, 40, 10));
});
}
private List<EventsObj> generateTestData()
{
eventsObj = new ArrayList<>();
for (int i = 0; i < 5; i++)
{
eventsObj.add(new EventsObj(String.valueOf(randomDate()), random(2, 60), random(2, 60), random(2, 60), random(2, 60)));
}
return eventsObj;
}
public static int random(int lowerBound, int upperBound)
{
return (lowerBound + (int) Math.round(Math.random() * (upperBound - lowerBound)));
}
private LocalDate randomDate()
{
Random random = new Random();
int minDay = (int) LocalDate.of(1900, 1, 1).toEpochDay();
int maxDay = (int) LocalDate.of(2015, 1, 1).toEpochDay();
long randomDay = minDay + random.nextInt(maxDay - minDay);
LocalDate randomBirthDate = LocalDate.ofEpochDay(randomDay);
return randomBirthDate;
}
private void addStackedChartData(List<EventsObj> data)
{
List<XYChart.Series<String, Number>> dataSeries = new ArrayList<>(data.size());
for (EventsObj data1 : data)
{
final XYChart.Series<String, Number> series1 = new XYChart.Series<>();
series1.setName(data1.getDate());
series1.getData().setAll(
new XYChart.Data<>("Info", data1.getInfo()));
dataSeries.add(series1);
}
stackedChart.getData().setAll(dataSeries);
}
private void setMaxBarWidth(StackedBarChart<String, Number> bc, CategoryAxis xAxis, double maxBarWidth, double minCategoryGap)
{
double barWidth = 0;
do
{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getCategoryGap());
barWidth = (avilableBarSpace / bc.getData().size()) - bc.getCategoryGap();
if (barWidth > maxBarWidth)
{
avilableBarSpace = (maxBarWidth + bc.getCategoryGap()) * bc.getData().size();
bc.setCategoryGap(catSpace - avilableBarSpace - bc.getCategoryGap());
}
}
while (barWidth > maxBarWidth);
do
{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (minCategoryGap + bc.getCategoryGap());
barWidth = Math.min(maxBarWidth, (avilableBarSpace / bc.getData().size()) - bc.getCategoryGap());
avilableBarSpace = (barWidth + bc.getCategoryGap()) * bc.getData().size();
bc.setCategoryGap(catSpace - avilableBarSpace - bc.getCategoryGap());
}
while (barWidth < maxBarWidth && bc.getCategoryGap() > minCategoryGap);
}
}
In a BarChart chart, the different series added are plotted in the different columns from the same category with a barGap, some spacing in between.
But in a StackBarChart chart, the different series are plotted stacked in the same column, hence in this case there is no bar gap.
If you take the setMaxBarWidth() method from this answer, all you need to do is set bc.getBarGap() to 0, and take sbc.getData().size() as 1. In this case there is no need for iterations.
This will be the new method, really simplified:
private void setMaxCategoryWidth(double maxCategoryWidth, double minCategoryGap){
double catSpace = xAxis.getCategorySpacing();
sbc.setCategoryGap(catSpace - Math.min(maxCategoryWidth, catSpace - minCategoryGap));
}
Basically, all you have to do is subtract the desired width of your category from the initial width of the category and put this extra space into the categoryGap.
Now, you can create your chart:
StackedBarChart<String, Number> sbc = new StackedBarChart<>(xAxis, yAxis);
And set your desired size for the categories:
setMaxCategoryWidth(40, 10);
sbc.widthProperty().addListener((obs, b, b1) -> {
Platform.runLater(() -> setMaxCategoryWidth(40, 10));
});
This pic is generated based on the sample 7.4 taken from here.
Now it is another problem that occurred, it seems that the query I'm using only works for the first time but after pressing the next/previous buttons, it is giving me something else !!
Here is the query I used:
for (int i = 0; i <= months.length + 1; i++) {
try {
String a;
if (i < 9) {
a = y + "0" + (i + 1);
} else {
a = y + "" + (i + 1);
}
System.out.println("Année Courante " + a);
conn = DBConnection.connect();
String sql = "select sum(montant_operation) from operations where (select Extract(YEAR_MONTH from date_operation)) = '" + a + "' and typ_operation ='Versement';";
final ResultSet rs = conn.prepareStatement(sql).executeQuery();
if (rs.next()) {
System.out.println(series1.getData().toString());
series1.getData().add(new XYChart.Data<>(months[i], rs.getFloat("sum(montant_operation)")));
}
} catch (SQLException e) {
System.out.println(e);
}
}
But is there a query that works fine one time and then it gives error.
Have a nice day
In your BuildData (by the way, not following Java Naming Conventions, change its name) method you are updating the data of series. In the same method you are adding this series to the chart. By clicking the "next" button, BuildData method is invoked where this chart is added again which is unnecessary. Delete the
Platform.runLater(() -> {
barchart.getData().add(series1);
});
part from the method and add the chart only once in start:
...
...
vbox.getChildren().addAll(box, barchart);
barchart.getData().add(series1);
pane.getChildren().add(vbox);
...
...
The tested SSCCE:
import java.util.Calendar;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BarChartDemo extends Application {
final String[] months = {"Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"};
//Connection conn;
final CategoryAxis month_axis = new CategoryAxis();
final NumberAxis data_axis = new NumberAxis();
final XYChart.Series<String, Number> series1 = new XYChart.Series();
private final BarChart<String, Number> barchart = new BarChart(month_axis, data_axis);
private Integer year = 0;
#Override
public void start(Stage primaryStage) {
year = Calendar.getInstance().get(Calendar.YEAR);
Button btn_next = new Button("NEXT");
Button btn_previous = new Button("PREVIOUS");
HBox box = new HBox(50);
box.getChildren().addAll(btn_previous, btn_next);
box.setAlignment(Pos.TOP_CENTER);
VBox vbox = new VBox(25);
box.setPadding(new Insets(10, 0, 10, 0));
FlowPane pane = new FlowPane(Orientation.VERTICAL);
vbox.getChildren().addAll(box, barchart);
barchart.getData().add(series1);
pane.getChildren().add(vbox);
Scene scene = new Scene(pane);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
BuildData(year);
btn_next.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
year += 1;
BuildData(year);
}
});
btn_previous.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
year -= 1;
BuildData(year);
}
});
}
public static void main(String[] args) {
launch(args);
}
private void BuildData(Integer y) {
series1.setName("Versement");
month_axis.setLabel("Mois de l'Année");
month_axis.setStyle("-fx-font-weight:BOLD;" + "-fx-font-size:15");
data_axis.setLabel("Valeur des Opérations Bancaires");
data_axis.setStyle("-fx-font-weight:BOLD;" + "-fx-font-size:15");
series1.getData().clear(); // clear old values
for (int i = 0; i < months.length; i++) {
series1.getData().add(new XYChart.Data(months[i], i * 10 * (y-2000)));
// try {
// String a;
// if (i < 9) {
// a = y + "0" + (i + 1);
// } else {
// a = y + "" + (i + 1);
// }
// conn = DBConnection.connect();
// String sql = "select sum(montant_operation) from operations where (select Extract(YEAR_MONTH from date_operation)) = '" + a + "' and typ_operation ='Versement';";
// final ResultSet rs = conn.prepareStatement(sql).executeQuery();
// if (rs.next()) {
// series1.getData().add(new XYChart.Data<>(months[i], rs.getFloat("sum(montant_operation)")));
// }
// } catch (SQLException e) {
// System.out.println(e);
// }
}
}
}
I am creating a notepad and I want to have edit text with multiple horizontal lines on it. As I have done with some but It is displaying line when I click on next or enter to the next line. I want that lines already be there. When I am using layour with android:lines="5". It is displaying picture as I have attached.
Please suggest on the same. thanks
EDITED: My EditText is showing like this!!!! A Huge gap on top. Please suggest what to do?
This is the code, based on google's note editorThe output will be shown in the image.When you press the enter,new lines will be added.
public class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(R.color.edit_note_line); //SET YOUR OWN COLOR HERE
}
#Override
protected void onDraw(Canvas canvas) {
//int count = getLineCount();
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if (getLineCount() > count)
count = getLineCount();//for long text with scrolling
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);//first line
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
super.onDraw(canvas);
}
}
For more info,Refer this link.
Simple add this line in your XML
android:gravity="top|left"
Here , my code will draw 15 lines by default and you can get more lines by pressing Enter :-
package com.wysiwyg.main;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.EditText;
public class LineEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LineEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.BLUE); //SET YOUR OWN COLOR HERE
setMinLines(15);
}
#Override
protected void onDraw(Canvas canvas) {
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if(getLineCount() > count){
count = getLineCount();
}
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
// Finishes up by calling the parent method
super.onDraw(canvas);
}
}