Can any of you FX Gurus tell me why this code appears to freeze FX screen updates? The loop is continuing to run in the animation thread, but after a while the screen stops updating.
I know, by the way, that using Thread.sleep in this way might upset some people, but this is code for students in an intro course to allow them to create animations without doing any event handling.
The exercise for students is to convert the animation to make it bounce an array of 100's of balls moving in random directions. After the conversion, the freeze tends to come much earlier than with a single ball.
Thanks in advance!
Here's the main class...
package week6code;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Bouncing Balls exercise starter
*
* #author Sam Scott
*/
public class BouncingBalls extends Application {
/**
* Sets up the stage and starts the main thread. Your drawing code should
* NOT go here.
*
* #param stage The first stage
*/
#Override
public void start(Stage stage) {
stage.setTitle("Bouncing Balls!"); // window title here
Canvas canvas = new Canvas(400, 300); // canvas size here
Group root = new Group();
Scene scene = new Scene(root);
root.getChildren().add(canvas);
stage.setScene(scene);
stage.show();
GraphicsContext gc = canvas.getGraphicsContext2D();
// This code starts a "thread" which will run your animation
Thread t = new Thread(() -> animate(gc));
t.start();
}
/**
* Animation thread. This is where you put your animation code.
*
* #param gc The drawing surface
*/
public void animate(GraphicsContext gc) {
// YOUR CODE HERE!
// intial positions and speeds
Ball ball = new Ball(100, 50, -1, -1, 10, Color.RED);
while (true) // loop forever
{
// draw screen
gc.setFill(Color.YELLOW);
gc.fillRect(0, 0, 400, 300);
ball.draw(gc);
// moving
ball.moveOneStep();
// bouncing
if (ball.getX() <= 0 || ball.getX() >= 400 - (ball.getSize() - 1)) {
ball.bounceX();
}
if (ball.getY() <= 0 || ball.getY() >= 300 - (ball.getSize() - 1)) {
ball.bounceY();
}
// pause
pause(1000 / 60);
}
}
/**
* Use this method instead of Thread.sleep(). It handles the possible
* exception by catching it, because re-throwing it is not an option in this
* case.
*
* #param duration Pause time in milliseconds.
*/
public static void pause(int duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException ex) {
}
}
/**
* Exits the app completely when the window is closed. This is necessary to
* kill the animation thread.
*/
#Override
public void stop() {
System.exit(0);
}
/**
* Launches the app
*
* #param args unused
*/
public static void main(String[] args) {
launch(args);
}
}
And here's the Ball class.
package week6solutions;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
/**
* An example of an object that can draw and move itself.
*
* #author Sam Scott
*/
public class Ball {
private double x, y, xSpeed, ySpeed;
private final int size;
private final Color c;
/**
* Creates a Ball instance.
*
* #param x Initial x position (left)
* #param y Initial y position (top)
* #param xSpeed Number of pixels to move horizontally in each step
* (negative for left, positive for right)
* #param ySpeed Number of pixels to move vertically in each step (negative
* for up, positive for down)
* #param size Diameter of ball
* #param c Color of ball
*/
public Ball(double x, double y, double xSpeed, double ySpeed, int size, Color c) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.size = size;
this.c = c;
}
/**
* Increment x and y using the values of xSpeed and ySpeed
*/
public void moveOneStep() {
x += xSpeed;
y += ySpeed;
}
/**
* Reverses the x direction by multiplying it by -1
*/
public void bounceX() {
xSpeed *= -1;
}
/**
* Reverses the y direction by multiplying it by -1
*/
public void bounceY() {
ySpeed *= -1;
}
/**
* Draw the ball in its current location on a Graphics object
*
* #param g The GraphicsContext object to draw on
*/
public void draw(GraphicsContext g) {
g.setFill(c);
g.fillOval((int) Math.round(x), (int) Math.round(y), size, size);
}
/**
* #return the current x location
*/
public double getX() {
return x;
}
/**
* #return the current y location
*/
public double getY() {
return y;
}
/**
* #return the size of the ball
*/
public int getSize() {
return size;
}
}
I still don't understand why you insist on teaching to do this the wrong way if the right way is so trivial and in fact very close to your code.
Just replace all the threading stuff with an AnimationTimer. Here is your updated main code. The rest just remains as before.
package week6code;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Bouncing Balls exercise starter
*
* #author Sam Scott
*/
public class BouncingBallsDoneRight extends Application {
/**
* Sets up the stage and starts the main thread. Your drawing code should
* NOT go here.
*
* #param stage The first stage
*/
#Override
public void start(Stage stage) {
stage.setTitle("Bouncing Balls!"); // window title here
Canvas canvas = new Canvas(400, 300); // canvas size here
Group root = new Group();
Scene scene = new Scene(root);
root.getChildren().add(canvas);
stage.setScene(scene);
stage.show();
GraphicsContext gc = canvas.getGraphicsContext2D();
// This code starts an AnimationTimer which will run your animation
AnimationTimer at = new AnimationTimer() {
Ball ball = new Ball(100, 50, -1, -1, 10, Color.RED);
#Override
public void handle(long arg0) {
// draw screen
gc.setFill(Color.YELLOW);
gc.fillRect(0, 0, 400, 300);
ball.draw(gc);
// moving
ball.moveOneStep();
// bouncing
if (ball.getX() <= 0 || ball.getX() >= 400 - (ball.getSize() - 1)) {
ball.bounceX();
}
if (ball.getY() <= 0 || ball.getY() >= 300 - (ball.getSize() - 1)) {
ball.bounceY();
}
}
};
at.start();
}
/**
* Launches the app
*
* #param args unused
*/
public static void main(String[] args) {
launch(args);
}
}
I tried to stay as close to your original code as possible. An even better solution for the task would be to use the scene graph and not the canvas.
The main problem is that JavaFX is not thread-safe (https://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm).
That is the reason of freeze. Each access, modification or drawing to UI should be done on UI thread.
That could be done with Platform.runLater().
Platform.runLater(() -> {
/*your code here*/
});
Related
Edit:Code is available on
github(https://github.com/goxr3plus/DragAdjustableLabel)
Question
I made a custom control in javaFX which I don't know how to name it. It actually does not exist in the javaFX packages but I saw it in other programs like LMMS where it's been used to control the volume.
The problem
When the mouse is reaching the top or bottom of the monitor screen nothing can be done. Have a look at the code to see what I'm talking about. How to solve the problem? Feel free to edit the title to a more appropriate ones.
import java.awt.Robot;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class FX3 extends Application {
#Override
public void start(Stage stage) throws Exception {
// Root-BorderPane
BorderPane borderPane = new BorderPane(new VolumeLabel(0, 100));
borderPane.setStyle("-fx-background-color:black;");
// Scene
stage.setScene(new Scene(borderPane, 200, 200, Color.BLACK));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
// TODO SpecialLabel
class VolumeLabel extends Label {
int screenX, screenY, previousY, minimumValue, maximumValue;
IntegerProperty currentValue = new SimpleIntegerProperty(15);
/**
* Constructor
*/
public VolumeLabel(int minimumValue, int maximumValue) {
this.minimumValue = minimumValue;
this.maximumValue = maximumValue;
textProperty().bind(currentValue.asString().concat(" %"));
setStyle(
"-fx-background-color:white; -fx-text-fill:black; -fx-padding:-2 8 -2 8; -fx-background-radius: 15; -fx-font-size:20; -fx-cursor:open-hand;");
// MouseListeners
setOnMousePressed(m -> {
screenX = (int) m.getScreenX();
screenY = (int) m.getScreenY();
//setCursor(Cursor.NONE); //Uncommend this line to make the cursor invisible
});
setOnMouseDragged(m -> {
setCurrentValue(
getCurrentValue() + (m.getScreenY() == previousY ? 0 : m.getScreenY() > previousY ? -1 : 1));
previousY = (int) m.getScreenY();
});
setOnMouseReleased(m -> {
// When the mouse is released -> move it to the initial position
try {
new Robot().mouseMove(screenX, screenY);
} catch (Exception e) {
e.printStackTrace();
}
setCursor(Cursor.OPEN_HAND);
});
}
/**
* Returns the Current Value of VolumeLabel
*
* #return
*/
public int getCurrentValue() {
return currentValue.get();
}
/**
* Setting the Current Value of VolumeLabel
*
* #param value
*/
public void setCurrentValue(int value) {
//System.out.println("Value:" + value + ", CurrentValue:" + currentValue.get());
if (value >= minimumValue && value <= maximumValue)
currentValue.set(value);
}
}
}
Here is the answer(the code also in on github):
import java.awt.Robot;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Cursor;
import javafx.scene.control.Label;
import javafx.stage.Screen;
/**
* A custom Node which is working like a Slider. <br>
* <b>!</b> When you hold the mouse on it and drag upside the value is
* increased. <br>
* <b>!</b> When you hold the mouse on it and drag down side the value is
* decreased.
*
* <br>
* Usage:
*
* <pre>
* <code>
* //initialize
* DragAdjustableLabel dragAdjustableLabel = new DragAdjustableLabel(10, 0, 100);
*
* //add it for example to a BorderPane
* primaryStage.setScene(new Scene(new BorderPane(dragAdjustableLabel)));
*</code>
* </pre>
*
* #author GOXR3PLUS
* #version 1.0
*/
public class DragAdjustableLabel extends Label {
// Variables
private int screenX;
private int screenY;
private int previousY;
private int minimumValue;
private int maximumValue;
/**
* The current value of the DragAdjustableLabel
*/
private IntegerProperty currentValue;
/**
* Constructor
*
* #param minimumValue
* Minimum Value that the slider can have
* #param maximumValue
* Maximum Value that the slider can have
*/
public DragAdjustableLabel(int currentValue, int minimumValue, int maximumValue) {
this.currentValue = new SimpleIntegerProperty(currentValue);
this.minimumValue = minimumValue;
this.maximumValue = maximumValue;
// Add a costume style class
this.getStyleClass().add("drag-adjustable-label");
setCursor(Cursor.OPEN_HAND);
textProperty().bind(this.currentValue.asString().concat(" %"));
// when the mouse is pressed
setOnMousePressed(m -> {
screenX = (int) m.getScreenX();
screenY = (int) m.getScreenY();
setCursor(Cursor.NONE); // comment this line to make the cursor
// visible
});
// when the mouse is dragged
setOnMouseDragged(m -> {
// calculate the monitor height
double screenHeight = Screen.getPrimary().getBounds().getHeight();
// !if the mouse has reached the the top of the monitor
// or
// ! if the mouse has reached the bottom of the monitor
if (m.getScreenY() <= 0 || m.getScreenY() + 10 >= screenHeight) {
resetMouse();
return;
}
// Calculate the current value
setCurrentValue(
getCurrentValue() + (m.getScreenY() == previousY ? 0 : m.getScreenY() > previousY ? -1 : 1));
previousY = (int) m.getScreenY();
});
// when the mouse is released
setOnMouseReleased(m -> {
resetMouse();
setCursor(Cursor.OPEN_HAND);
});
}
/**
* Reset the mouse to the default position(which is the center of the
* element)
*
* #param x
* #param y
*/
private void resetMouse() {
try {
new Robot().mouseMove(screenX, screenY);
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Set the current value
*
* #param value
*/
public void setCurrentValue(int value) {
// if the value is between the limits
if (value >= minimumValue && value <= maximumValue)
currentValue.set(value);
}
public int getCurrentValue() {
return currentValue.get();
}
public IntegerProperty currentValueProperty() {
return currentValue;
}
}
There's a breakout game that's been partially created for me; currently it only has a bouncing ball and a bat; there are no bricks which the ball can hit.
I need to add code to generate bricks but I'm struggling; I have no idea how to approach this as I'm not very good with Java GUI.
I have included the classes where the code needs to be added; the areas which need the code are written in comments
Modelbreakout class:
package breakout;
import java.util.Observable;
import static breakout.Global.*;
/**
* Model of the game of breakout
* The active object ActiveModel does the work of moving the ball
* #author Mike Smith University of Brighton
*/
public class ModelBreakout extends Observable
{
private GameObject ball; // The ball
private GameObject bricks[]; // The bricks
private GameObject bat; // The bat
private ModelActivePart am = new ModelActivePart( this );
private Thread activeModel = new Thread( am );
private int score = 0;
public void createGameObjects()
{
ball = new GameObject(W/2, H/2, BALL_SIZE, BALL_SIZE, Colour.RED );
bat = new GameObject(W/2, H - BRICK_HEIGHT*4, BRICK_WIDTH*3,
BRICK_HEIGHT, Colour.GRAY);
bricks = new GameObject[BRICKS];
// *[1]**********************************************************
// * Fill in code to place the bricks on the board *
// **************************************************************
}
public void startGame() { activeModel.start(); }
public GameObject getBall() { return ball; }
public GameObject[] getBricks() { return bricks; }
public GameObject getBat() { return bat; }
public void addToScore( int n ) { score += n; }
public int getScore() { return score; }
public void stopGame() { }
/**
* Move the bat dist pixels. (-dist) is left or (+dist) is right
* #param dist - Distance to move
*/
public void moveBat( float dist )
{
// *[2]**********************************************************
// * Fill in code to prevent the bat being moved off the screen *
// **************************************************************
Debug.trace( "Model: Move bat = %6.2f", dist );
bat.moveX(dist);
//modelChanged();
}
/**
* Model has changed so notify observers so that they
* can redraw the current state of the game
*/
public void modelChanged()
{
setChanged(); notifyObservers();
}
}
ViewBreakout class:
package breakout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
import static breakout.Global.*;
/**
* Displays a graphical view of the game of breakout
* Uses Garphics2D would need to be re-implemented for Android
* #author Mike Smith University of Brighton
*/
public class ViewBreakout extends JFrame implements Observer
{
private ControllerBreakout controller;
private GameObject ball; // The ball
private GameObject[] bricks; // The bricks
private GameObject bat; // The bat
private int score = 0; // The score
private long timeTaken = 0; // How long
private int frames = 0; // Frames output
private final static int RESET_AFTER = 200;
/**
* Construct the view of the game
*/
public ViewBreakout()
{
setSize( W, H ); // Size of window
addKeyListener( new Transaction() ); // Called when key press
setDefaultCloseOperation(EXIT_ON_CLOSE);
Timer.startTimer();
}
/**
* Code called to draw the current state of the game
* Uses draw: Draw a shape
* fill: Fill the shape
* setPaint: Colour used
* drawString: Write string on display
* #param g Graphics context to use
*/
public void drawActualPicture( Graphics2D g )
{
frames++;
// White background
g.setPaint( Color.white );
g.fill( new Rectangle2D.Float( 0, 0, W, H ) );
Font font = new Font("Monospaced",Font.BOLD,24);
g.setFont( font );
// Blue playing border
g.setPaint( Color.blue ); // Paint Colour
g.draw( new Rectangle2D.Float( B, M, W-B*2, H-M-B ) );
// Display the ball
display( g, ball );
// Display the bricks that make up the game
// *[3]**********************************************************
// * Fill in code to display bricks (A brick may not exist) *
// **************************************************************
// Display the bat
display( g, bat );
// Display state of game
g.setPaint( Color.black );
FontMetrics fm = getFontMetrics( font );
String fmt = "BreakOut: Score = [%6d] fps=%5.1f";
String text = String.format(fmt, score,
frames/(Timer.timeTaken()/1000.0)
);
if ( frames > RESET_AFTER )
{ frames = 0; Timer.startTimer(); }
g.drawString( text, W/2-fm.stringWidth(text)/2, (int)M*2 );
}
private void display( Graphics2D g, GameObject go )
{
switch( go.getColour() )
{
case GRAY: g.setColor( Color.gray );
break;
case BLUE: g.setColor( Color.blue );
break;
case RED: g.setColor( Color.red );
break;
}
g.fill( new Rectangle2D.Float( go.getX(), go.getY(),
go.getWidth(), go.getHeight() ) );
}
/**
* Called from the model when its state has changed
* #param aModel Model to be displayed
* #param arg Any arguments
*/
#Override
public void update( Observable aModel, Object arg )
{
ModelBreakout model = (ModelBreakout) aModel;
// Get from the model the ball, bat, bricks & score
ball = model.getBall(); // Ball
bricks = model.getBricks(); // Bricks
bat = model.getBat(); // Bat
score = model.getScore(); // Score
//Debug.trace("Update");
repaint(); // Re draw game
}
/**
* Called by repaint to redraw the Model
* #param g Graphics context
*/
#Override
public void update( Graphics g ) // Called by repaint
{
drawPicture( (Graphics2D) g ); // Draw Picture
}
/**
* Called when window is first shown or damaged
* #param g Graphics context
*/
#Override
public void paint( Graphics g ) // When 'Window' is first
{ // shown or damaged
drawPicture( (Graphics2D) g ); // Draw Picture
}
private BufferedImage theAI; // Alternate Image
private Graphics2D theAG; // Alternate Graphics
public void drawPicture( Graphics2D g ) // Double buffer
{ // to avoid flicker
if ( theAG == null )
{
Dimension d = getSize(); // Size of curr. image
theAI = (BufferedImage) createImage( d.width, d.height );
theAG = theAI.createGraphics();
}
drawActualPicture( theAG ); // Draw Actual Picture
g.drawImage( theAI, 0, 0, this ); // Display on screen
}
/**
* Need to be told where the controller is
* #param aPongController The controller used
*/
public void setController(ControllerBreakout aPongController)
{
controller = aPongController;
}
/**
* Methods Called on a key press
* calls the controller to process
*/
class Transaction implements KeyListener // When character typed
{
#Override
public void keyPressed(KeyEvent e) // Obey this method
{
// Make -ve so not confused with normal characters
controller.userKeyInteraction( -e.getKeyCode() );
}
#Override
public void keyReleased(KeyEvent e)
{
// Called on key release including specials
}
#Override
public void keyTyped(KeyEvent e)
{
// Send internal code for key
controller.userKeyInteraction( e.getKeyChar() );
}
}
}
ModelActivePart class:
package breakout;
import static breakout.Global.*;
/**
* A class used by the model to give it an active part.
* Which moves the ball every n millesconds and implements
* an appropirate action on a collision involving the ball.
* #author Mike Smith University of Brighton
*/
public class ModelActivePart implements Runnable
{
private ModelBreakout model;
private boolean runGame = true; // Assume write to is atomic
public ModelActivePart(ModelBreakout aBreakOutModel)
{
model = aBreakOutModel;
}
/**
* Stop game, thread will finish
*/
public void stopGame() { runGame = false; }
/**
* Code to position the ball after time interval
* and work out what happens next
*/
#Override
public void run()
{
final float S = 6; // Units to move the ball
try
{
GameObject ball = model.getBall(); // Ball in game
GameObject bricks[] = model.getBricks(); // Bricks
GameObject bat = model.getBat(); // Bat
while (runGame)
{
double x = ball.getX();
double y = ball.getY();
// Deal with possible edge of board hit
if (x >= W - B - BALL_SIZE) ball.changeDirectionX();
if (x <= 0 + B ) ball.changeDirectionX();
if (y >= H - B - BALL_SIZE)
{
ball.changeDirectionY(); model.addToScore( HIT_BOTTOM );
}
if (y <= 0 + M ) ball.changeDirectionY();
ball.moveX(S); ball.moveY(S);
// As only a hit on the bat/ball is detected it is assumed to be
// on the top or bottom of the object
// A hit on the left or right of the object
// has an interesting affect
boolean hit = false;
// *[4]**********************************************************
// * Fill in code to check if a brick has been hit *
// * Remember to remove a brick in the array (if hit) *
// * [remove] - set the array element to null *
// **************************************************************
if (hit)
ball.changeDirectionY();
if (bat.hitBy(ball) == GameObject.Collision.HIT)
ball.changeDirectionY();
model.modelChanged(); // Model changed refresh screen
Thread.sleep(20); // About 50 Hz
}
} catch (Exception e)
{
Debug.error("ModelActivePart - stopped\n%s", e.getMessage() );
}
}
}
Now I don't expect you to do everything for me, I just want to know how to draw one brick on the screen; from there onwards I could probably to the rest myself.
If you want the whole package; the download link to my project is here
Any help is appreciated :)
For a blue brick at position 0, 0 modify the createGameObjects method of the ModelBreakout class:
public void createGameObjects()
{
ball = new GameObject(W / 2, H / 2, BALL_SIZE, BALL_SIZE, Colour.RED);
bat = new GameObject(W / 2, H - BRICK_HEIGHT * 4, BRICK_WIDTH * 3,
BRICK_HEIGHT, Colour.GRAY);
bricks = new GameObject[BRICKS];
// *[1]**********************************************************
// * Fill in code to place the bricks on the board *
// **************************************************************
bricks[0] = new GameObject(0, 0, BRICK_HEIGHT, BRICK_WIDTH, Colour.BLUE);
}
Then draw the bricks in the drawActualPicture method of the ViewBreakout class:
// Display the bricks that make up the game
// *[3]**********************************************************
// * Fill in code to display bricks (A brick may not exist) *
// **************************************************************
for (GameObject brick : bricks)
{
if (null != brick)
{
display ( g, brick );
}
}
This is a great assignment if it's coursework.
im using the Canvas class to make a screensaver as a schoolproject.
But the window generated by Canvas doesnt show my objects on it (current time)
until i minimize it an resize it again. After that all things works fine.
so what is wrong?
thank you for coming answers!
with kind regards
leon
those are the classes, i peronally think that the problem is in the class Start or BufferedCanvas
import java.awt.*;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class Start
{
int fensterX = 900;
int fensterY = 700;
Farbengenerator fg = new Farbengenerator();
BufferedCanvas c =newBufferedCanvas("Bild",fensterX,fensterY);
Zeit z = new Zeit();
SchriftParameter sp = new SchriftParameter();
public void zeichneText(){
double x = 100;
double y = 100;
double fy =0.01;
double fx =0.02;
int red=0;
int green=0;
int blue=0;
double colourGrowRate=0.05;
String uhr;
c.setFont(sp.setzteSchrift());
c.setForegroundColour(Color.BLACK);
c.setBackgroundColour(Color.WHITE);
for(int i=0;i<100;i++){
c.drawString("Starting...",(int)x,(int)y);
c.updateAndShow();
try{Thread.sleep(50);}
catch(Exception e){};
c.updateAndShow();
}
CreateButton d = new CreateButton();
d.run();
while(true) {
c.erase();
uhr = z.erstelleZeit();
c.drawString(uhr,(int)x,(int)y);
if((int)x >fensterX-93 || (int)x <5){
fx = fx * (-1);
red=fg.gibROT();
green=fg.gibGRUEN();
blue=fg.gibBLAU();
Color colour = new Color(red,green,blue);
c.setForegroundColour(colour);
}
if((int)y > fensterY-1 || (int)y < 46){
fy = fy * (-1);
red=fg.gibROT();
green=fg.gibGRUEN();
blue=fg.gibBLAU();
Color colour = new Color(red,green,blue);
c.setForegroundColour(colour);
}
if((int)colourGrowRate>=1){
fg.generiereFarbe();
colourGrowRate = 0.05;
}
colourGrowRate=colourGrowRate+colourGrowRate;
x = x + fx;
y = y + fy;
c.updateAndShow();
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferStrategy;
public class BufferedCanvas
{
private JFrame frame;
private CanvasPane canvas;
private Graphics2D graphic;
private Color backgroundColour;
private Image canvasImage;
BufferStrategy buff;
/**
* Create a BufferedCanvas with default height,
width and background colour
* (300, 300, white).
* #param title title to appear in Canvas Frame
*/
public BufferedCanvas(String title)
{
this(title, 300, 300, Color.white);
}
/**
* Create a BufferedCanvas with default background colour (white).
* #param title title to appear in Canvas Frame
* #param width the desired width for the canvas
* #param height the desired height for the canvas
*/
public BufferedCanvas(String title, int width, int height)
{
this(title, width, height, Color.white);
}
/**
* Create a BufferedCanvas.
* #param title title to appear in Canvas Frame
* #param width the desired width for the canvas
* #param height the desired height for the canvas
* #param bgClour the desired background colour of the canvas
*/
public BufferedCanvas(String title, int width, int height, Color bgColour)
{
frame = new JFrame();
canvas = new CanvasPane();
frame.setContentPane(canvas);
frame.setTitle(title);
canvas.setPreferredSize(new Dimension(width, height));
backgroundColour = bgColour;
frame.pack();
frame.createBufferStrategy(2);
buff = frame.getBufferStrategy();
graphic = (Graphics2D)buff.getDrawGraphics();
setVisible(true);
}
/**
* Set the canvas visibility and brings canvas to the front of screen
* when made visible. This method can also be used to bring an already
* visible canvas to the front of other windows.
* #param visible boolean value representing the desired visibility of
* the canvas (true or false)
*/
public void setVisible(boolean visible)
{
if(graphic == null) {
// first time: instantiate the offscreen image and fill it with
// the background colour
Dimension size = canvas.getSize();
canvasImage = canvas.createImage(size.width, size.height);
graphic = (Graphics2D)canvasImage.getGraphics();
graphic.setColor(backgroundColour);
graphic.fillRect(0, 0, size.width, size.height);
graphic.setColor(Color.black);
}
frame.setVisible(true);
}
/**
* Update the canvas and show the new image.
*/
public void updateAndShow(){
buff.show();
}
/**
* Provide information on visibility of the Canvas.
* #return true if canvas is visible, false otherwise
*/
public boolean isVisible()
{
return frame.isVisible();
}
/**
* Draw a given shape onto the canvas.
* #param shape the shape object to be drawn on the canvas
*/
public void draw(Shape shape)
{
graphic.draw(shape);
//canvas.repaint();
}
/**
* Fill the internal dimensions of a given shape with the current
* foreground colour of the canvas.
* #param shape the shape object to be filled
*/
public void fill(Shape shape)
{
graphic.fill(shape);
//canvas.repaint();
}
/**
* Erase the whole canvas.
*/
public void erase()
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
Dimension size = canvas.getSize();
graphic.fill(new Rectangle(0, 0, size.width, size.height));
graphic.setColor(original);
//canvas.repaint();
}
/**
* Erase a given shape's interior on the screen.
* #param shape the shape object to be erased
*/
public void erase(Shape shape)
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
graphic.fill(shape); // erase by filling background colour
graphic.setColor(original);
//canvas.repaint();
}
/**
* Erases a given shape's outline on the screen.
* #param shape the shape object to be erased
*/
public void eraseOutline(Shape shape)
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
graphic.draw(shape); // erase by drawing background colour
graphic.setColor(original);
//canvas.repaint();
}
/**
* Draws an image onto the canvas.
* #param image the Image object to be displayed
* #param x x co-ordinate for Image placement
* #param y y co-ordinate for Image placement
* #return returns boolean value representing whether the image was
* completely loaded
*/
public boolean drawImage(Image image, int x, int y)
{
boolean result = graphic.drawImage(image, x, y, null);
//canvas.repaint();
return result;
}
/**
* Draws a String on the Canvas.
* #param text the String to be displayed
* #param x x co-ordinate for text placement
* #param y y co-ordinate for text placement
*/
public void drawString(String text, int x, int y)
{
graphic.drawString(text, x, y);
//canvas.repaint();
}
/**
* Erases a String on the Canvas.
* #param text the String to be displayed
* #param x x co-ordinate for text placement
* #param y y co-ordinate for text placement
*/
public void eraseString(String text, int x, int y)
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
graphic.drawString(text, x, y);
graphic.setColor(original);
//canvas.repaint();
}
/**
* Draws a line on the Canvas.
* #param x1 x co-ordinate of start of line
* #param y1 y co-ordinate of start of line
* #param x2 x co-ordinate of end of line
* #param y2 y co-ordinate of end of line
*/
public void drawLine(int x1, int y1, int x2, int y2)
{
graphic.drawLine(x1, y1, x2, y2);
//canvas.repaint();
}
/**
* Draws a dot/pixel on the Canvas.
* #param x x co-ordinate of dot
* #param y y co-ordinate of dot
*/
public void drawDot(int x, int y)
{
graphic.drawLine(x, y, x, y);
//canvas.repaint();
}
/**
* Sets the foreground colour of the Canvas.
* #param newColour the new colour for the foreground of the Canvas
*/
public void setForegroundColour(Color newColour)
{
graphic.setColor(newColour);
}
/**
* Returns the current colour of the foreground.
* #return the colour of the foreground of the Canvas
*/
public Color getForegroundColour()
{
return graphic.getColor();
}
/**
* Sets the background colour of the Canvas.
* #param newColour the new colour for the background of the Canvas
*/
public void setBackgroundColour(Color newColour)
{
backgroundColour = newColour;
graphic.setBackground(newColour);
}
/**
* Returns the current colour of the background
* #return the colour of the background of the Canvas
*/
public Color getBackgroundColour()
{
return backgroundColour;
}
/**
* changes the current Font used on the Canvas
* #param newFont new font to be used for String output
*/
public void setFont(Font newFont)
{
graphic.setFont(newFont);
}
/**
* Returns the current font of the canvas.
* #return the font currently in use
**/
public Font getFont()
{
return graphic.getFont();
}
/**
* Sets the size of the canvas.
* #param width new width
* #param height new height
*/
public void setSize(int width, int height)
{
canvas.setPreferredSize(new Dimension(width, height));
Image oldImage = canvasImage;
canvasImage = canvas.createImage(width, height);
graphic = (Graphics2D)canvasImage.getGraphics();
graphic.drawImage(oldImage, 0, 0, null);
frame.pack();
}
/**
* Returns the size of the canvas.
* #return The current dimension of the canvas
*/
public Dimension getSize()
{
return canvas.getSize();
}
/**
* Waits for a specified number of milliseconds before finishing.
* This provides an easy way to specify a small delay which can be
* used when producing animations.
* #param milliseconds the number
*/
public void wait(int milliseconds)
{
try
{
Thread.sleep(milliseconds);
}
catch (Exception e)
{
// ignoring exception at the moment
}
}
/************************************************************************
* Nested class CanvasPane - the actual canvas component contained in the
* Canvas frame. This is essentially a JPanel with added capability to
* refresh the image drawn on it.
*/
private class CanvasPane extends JPanel
{
public void paint(Graphics g)
{
g.drawImage(canvasImage, 0, 0, null);
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
public class CreateButton extends JFrame implements ActionListener{
public void run() {
createAndShowGUI();
}
public CreateButton() {
// set layout for the frame
this.getContentPane().setLayout(new FlowLayout());
JButton button1 = new JButton();
button1.setText("closeApp");
//set actionlisteners for the buttons
button1.addActionListener(this);
// define a custom short action command for the button
button1.setActionCommand("closeApp");
// add buttons to frame
add(button1);
}
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new CreateButton();
//Display the window.
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent ae) {
String action = ae.getActionCommand();
if (action.equals("closeApp")) {
System.exit(1);
}
}
}
import java.awt.*;
public class SchriftParameter
{
public Font setzteSchrift(){
Font f = new Font("Fixed",1,24);
return (f);
}
}
public class Farbengenerator
{
int r=0;
int g=0;
int b=0;
public void generiereFarbe(){
if (r<255&&g==0&&b==0){
r++;
}
else if (r==255&&g<255&&b==0){
g++;
}
else if (r>0&&g==255&&b==0){
r= r-1;
}
else if (r==0&&g==255&&b<255){
b++;
}
else if (r==0&&g>0&&b==255){
g=g-1;
}
else if (r<255&&g==0&&b==255){
r++;
}
else if (r==255&&g==0&&b>0){
b=b-1;
}
}
public int gibROT () {
return(r);
}
public int gibGRUEN () {
return(g);
}
public int gibBLAU () {
return(b);
}
}
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class Zeit
{
public String erstelleZeit(){
DateFormat df = new SimpleDateFormat("HH:mm:ss");
Date d = new Date();
String uhr = df.format(d);
return (uhr);
}
}
I've messed around with Java a little bit and I decided to take a course to learn some more and I have been assigned a fairly easy assignment. I have it mostly done and I know what's left must be simple, but I cannot seem to quite get it. So far I have been able to successfully create a program that has 1 bouncing ball, but I would now like to allow the user to input the amount of balls to bounce. I have tried a few different loops in my Ball class, but none of them work. Would anyone be willing to give a quick hand? I am almost sure it would require either an Array or ArrayList and just storing the balls in it but I have yet to find a solution that works. I have looked at other problems like this on the website but none have quite solved my problem. Thank you if you can help!
Main Class :
public class mainClass {
/**
/**
* Frame to hold a bouncing ball panel, implemented in the BallPanel class.
* Controls the animation of the ball via pauses and calls to BallPanel's move
* method.
*
* #author Michael Peterson modified by Mr O Aug 2012
*/
public static class BallTest extends JFrame {
// size of the window
private static final int WINDOW_WIDTH = 500;
private static final int WINDOW_HEIGHT = 300;
// panel containing the bouncing ball
private BallPanel ballPanel;
/**
* Pause command used to control the speed of the bouncing ball animation.
* Currently pauses for 20 ms. Use smaller values for faster animation and
* vice versa.
*/
public static void pause() {
try {
Thread.sleep(20); // pause for 20 ms
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}//end of catch
}//end of pause method
/**
* Creates a new instance of BallTest
*/
public BallTest() {
super("Bouncing Ball"); // set frame name
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setLayout(new BorderLayout());
ballPanel = new BallPanel();
add(ballPanel);
center(this);
setVisible(true);
// infinite animation loop, program halts when window is closed.
while (true) {
pause();
ballPanel.move(ballPanel);
}//end of while loop of animation
} //end of BallTest Constructor
/**
* Helper routine to center a frame on the screen (will cause problems if
* frame is bigger than the screen!)
*/
public static void center(JFrame frame) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Point center = ge.getCenterPoint();
int w = frame.getWidth();
int h = frame.getHeight();
int x = center.x - w / 2, y = center.y - h / 2;
frame.setBounds(x, y, w, h);
frame.validate();
}//end of center method
}//end of BallPanel Class
public static void main(String[] args) {
BallTest t = new BallTest(); //make a BallTest object
}//end of main method
}//end of Fall2012Lab11StarterCode class
Ball Class :
public class Ball extends JPanel {
private int bxCoord; //the ball's x coordinate
private int byCoord; //the ball's y coordinate
private int bHeight; //the ball's height
private int bWidth; //the ball's weight
private int bRise; //the ball's y change
private int bRun; //the ball's x change
private Color bColor; //the ball's color
//Constructor
public Ball() {
bxCoord = setStartBxCoord();
byCoord = setStartByCoord();
bHeight = setStartBHeight();
bWidth = setStartBWidth();
bRise = setStartBRise();
bRun = setStartBRun();
bColor = setStartColor();
}
/**
* The setters, getters, and initial value for the ball's x coordinate
*/
public void setBxCoord(int xCoord) {
bxCoord = xCoord;
}
public int setStartBxCoord() {
int xCoord;
xCoord = (int) (Math.random() * 51);
return xCoord;
}
public int getBxCoord() {
return bxCoord;
}
/**
* The setters, getters, and initial value for the ball's y coordinate
*/
public void setByCoord(int yCoord) {
bxCoord = yCoord;
}
public int setStartByCoord() {
int yCoord;
yCoord = (int) (Math.random() * 51);
return yCoord;
}
public int getByCoord() {
return byCoord;
}
/**
* The setters, getters, and initial value for the ball's x height
*/
public void setBHeight(int height) {
bHeight = height;
}
public int setStartBHeight() {
int height;
height = (int) (10 + Math.random() * 11);
return height;
}
public int getBHeight() {
return bHeight;
}
public void setBWidth(int width) {
bWidth = width;
}
/**
* The setters, getters, and initial value for the ball's x width
*/
public int setStartBWidth() {
int width;
width = (int) (10 + Math.random() * 11);
return width;
}
public int getBWidth() {
return bWidth;
}
/**
* The setters, getters, and initial value for the ball's rise
*/
public void setBRise(int rise) {
bRise = rise;
}
public int setStartBRise() {
int rise;
rise = (int) (Math.random() * 11);
return rise;
}
public int getBRise() {
return bRise;
}
/**
* The setters, getters, and initial value for the ball's run
*/
public void setBRun(int run) {
bRun = run;
}
public int setStartBRun() {
int run;
run = (int) (Math.random() * 11);
return run;
}
public int getBRun() {
return bRun;
}
/**
* The movement of the ball in the x and y direction
*/
public void moveX(){
bxCoord += bRun;
}
public void moveY(){
byCoord += bRise;
}
/**
* The setters, getters, and initial value for the ball's color
*/
public void setColor(Color color) {
bColor = color;
}
public Color setStartColor() {
int red = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
Color ranColor = new Color(red, green, blue);
return ranColor;
}
public Color getbColor() {
return bColor;
}
/**
* Computes the next position for the balls and updates their positions.
*/
public void move(BallPanel ballPanel) {
// If ball is approaching a wall, reverse direction
if ((getBxCoord() < (0 - getBRun())) || (getBxCoord() > (ballPanel.getWidth() - getBWidth()))) {
setBRun(-getBRun());
}
if ((getByCoord() < (0 - getBRise())) || (getByCoord() > (ballPanel.getHeight() - getBHeight()))) {
setBRise(-getBRise());
}
// "Move" ball according to values in rise and run
moveX();
moveY();
} // end method move
}//end of Ball Class
Ball Panel Class :
public class BallPanel extends JPanel {
Ball ball = new Ball(); //creat a ball.
/**
* Creates a new instance of BallPanel
*/
public BallPanel() {
super();
}
/**
* The move method moves the ball and repaints the panel
* PreCondtion: A panel containing a ball has been created
* PostCondition: The position of a ball on the panel has moved. The panell
* is repainted with the ball in the new position.
* #param ballPanel The name of the panel on which the ball is found
*/
public void move(BallPanel ballPanel) {
ball.move(ballPanel);
repaint();
}
/**
* Paints the balls at their current positions within the panel.
* PreCondition: A graphics object has been created and needs to be displayed
* PostCondition: The graphics object g has been displayed
* #param g The graphic object to be displayed
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black); // set color black
g.fillRect(0, 0, getWidth(), getHeight()); // paint background
// Paint the Ball
g.setColor(ball.getbColor());
g.fillOval(ball.getBxCoord(), ball.getByCoord(),
ball.getBWidth(), ball.getBHeight());
}//end of paintComponent method
}//end of BallPanel class
Well I'm answering you purely on previous codes i have done similar to this, because I didn't have the time to test out yours.
Your ballPanel class should look something more like this:
import java.util.ArrayList;
public class BallPanel extends JPanel{
private ArrayList<Ball> BallList = new ArrayList<Ball>();
private int num;
public BallPanel(int numberOfBalls){
super();
num = numberOfBalls;
for(int i = 0; i<num; i++){BallList.add(new Ball());}
}
//the rest of your methods, using for loops for the balls
Also I think you can use this instead of ArrayList (this is easier):
Ball[] BallList = new Ball[numberOfBalls];
then an example of your move method should look like this:
public void move(BallPanel ballPanel){
for(int i = 0; i<num; i++){
BallList[i].move(ballPanel);
}
repaint();
}
I had a similar issues with a Bouncing Ball program. Tried the previously posted code, but the public void move(BallPanel area isn't working. When I access an array in that location the ball stops moving. Here is my current code for move:
public void move(BallPanel ballPanel, ArrayList<Ball> ballList) {
for(int i = 0; i<1; i++){
System.out.println("mmm" + i);
ballList.get(i).move(ballPanel);
}
repaint();
Secondly as shown the paintComponent area above, only ball is used to paint the ball. Is that OK that paintComponent is called multiple times but only has a variable ball in it. Here is what that section looks like:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black); // set color black
g.fillRect(0, 0, getWidth(), getHeight()); // paint background
// Paint the Ball
g.setColor(ball.getbColor());
g.fillOval(ball.getBxCoord(), ball.getByCoord(),
ball.getBWidth(), ball.getBHeight());
I have created an arc. I want to do certain things on different arcs when an arc is clicked. How do I know if an arc is touched or not? Can someone please provide some code for onTouch method to do such a calculation. Also please explain it a little bit.
package com.example.android.customviews;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
/**
* Limit Indicator is used to show any kind of limits such as Balance and Actual
* Amount of wallet present in an account. In order to use this in the XML
* Layout, please include the following: <br />
* <br />
*
* xmlns:custom="http://schemas.android.com/apk/res/com.example.android.customviews"
*
* <br /> <br />
*
* Following custom attributes are provided: <br />
* <br />
*
* custom:borderColor <br />
* custom:borderRadius <br />
* custom:outerCircleRadius <br />
* custom:text <br />
* custom:textSize <br />
* custom:innerCircleColor <br />
*
* #author Syed Ahmed Hussain
*/
public class LimitIndicator extends ViewGroup {
// ============================================================================================
// Variables Declaration
private int mInnerCircleColor;
private int mBorderColor;
private int mTextColor;
private float mTextSize;
private String mTitleText = "";
private float mHalfOfBorderWidth = 0.0f;
private float mOuterCircleRadius = 2.0f;
private float mBorderWidth = 30.0f;
private Paint mDialPaint, mTextPaint, mBorderPaint, mInnerCirclePaint;
private float mCenterX = 100.0f;
private float mCenterY = 100.0f;
private int mTotalProgressInDegrees;
private int mTotalProgress = -1;
// Start Angle should be 90 degrees to create a clockwise illusion.
private int mStartAngle = 270;
// This should be the one which provides us a percentage wise drawing
private int mSweepAngle = 1;
private RectF mBorderBounds = null;
// ============================================================================================
// Constructors
public LimitIndicator(Context pContext) {
super(pContext);
Log.d("LimitIndicator", "LimitIndicator(Context pContext) called");
initialize();
}
public LimitIndicator(Context pContext, AttributeSet pAttrs) {
super(pContext, pAttrs);
Log.d("LimitIndicator", "LimitIndicator(Context pContext, AttributeSet pAttrs) called");
TypedArray typedArray = pContext.obtainStyledAttributes(pAttrs, R.styleable.LimitIndicator, 0, 0);
try {
mOuterCircleRadius = typedArray.getDimension(R.styleable.LimitIndicator_outerCircleRadius, mOuterCircleRadius);
mTextColor = typedArray.getColor(R.styleable.LimitIndicator_textColor, Color.WHITE);
mTitleText = typedArray.getString(R.styleable.LimitIndicator_text);
mTextSize = typedArray.getDimension(R.styleable.LimitIndicator_textSize, 25);
mTotalProgress = typedArray.getInteger(R.styleable.LimitIndicator_numerator, mTotalProgress);
mBorderColor = typedArray.getColor(R.styleable.LimitIndicator_borderColor, Color.BLACK);
mBorderWidth = typedArray.getDimension(R.styleable.LimitIndicator_borderRadius, mBorderWidth);
mInnerCircleColor = typedArray.getColor(R.styleable.LimitIndicator_innerCircleColor, Color.GREEN);
} finally {
typedArray.recycle();
}
initialize();
}
// ============================================================================================
// Initialization
/**
* Initialize all elements
*/
private void initialize() {
// Set up the paint for the dial
mDialPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDialPaint.setStyle(Paint.Style.FILL);
mDialPaint.setColor(Color.GRAY);
// Set up the paint for the label text
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTypeface(Typeface.SANS_SERIF);
mTextPaint.setTextAlign(Align.CENTER);
mTextPaint.setColor(mTextColor);
mTextPaint.setTextSize(mTextSize);
// Set up the paint for the border
mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setStrokeWidth(mBorderWidth);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setDither(true);
mInnerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerCirclePaint.setStyle(Paint.Style.FILL);
mInnerCirclePaint.setColor(mInnerCircleColor);
// mBorderPaint.setStrokeJoin(Paint.Join.ROUND);
// mBorderPaint.setStrokeCap(Paint.Cap.ROUND);
mBorderBounds = new RectF(getLeft(), getTop(), getRight(), getBottom());
}
// ============================================================================================
// Drawing on surface
#Override
protected void onDraw(Canvas pCanvas) {
super.onDraw(pCanvas);
Log.d("LimitIndicator", "OnDraw called");
Log.d("Measured Spec Width", mCenterX + "");
Log.d("Measured Spec Height", mCenterY + "");
pCanvas.drawCircle(mCenterX, mCenterY, mOuterCircleRadius, mDialPaint);
pCanvas.drawCircle(mCenterX, mCenterY, (float) (mOuterCircleRadius - mBorderWidth + 1) , mInnerCirclePaint);
pCanvas.drawText(mTitleText, mCenterX, mCenterY + 5, mTextPaint);
pCanvas.drawArc(mBorderBounds, mStartAngle, mSweepAngle, false, mBorderPaint);
if (mSweepAngle < mTotalProgressInDegrees) {
mSweepAngle+=3;
mBorderPaint.setStrokeWidth(mBorderWidth++);
invalidate();
}
}
#Override
protected void onLayout(boolean pChanged, int pLeft, int pTop, int pRight, int pBottom) {
Log.d("LimitIndicator", "OnLayout called");
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).layout(0, 0, pRight, pBottom);
}
}
#Override
protected void onSizeChanged(int pW, int pH, int pOldw, int pOldh) {
super.onSizeChanged(pW, pH, pOldw, pOldh);
Log.d("LimitIndicator", "OnSizeChanged called");
float xPad = (getPaddingLeft() + getPaddingRight());
float yPad = (getPaddingTop() + getPaddingBottom());
// To draw Circle in the middle
mCenterX = (float) ((pW - xPad) * 0.5);
mCenterY = (float) ((pH - yPad) * 0.5);
// This (mBorderBounds.bottom needs to be fixed. Width &
// Height should be equal in order
// to create a perfect circle. Otherwise an
// Oval will be created! :P
// Bounds for creating an arc
mHalfOfBorderWidth = (float) (mBorderWidth * 0.5);
mBorderBounds.right = mCenterX + mOuterCircleRadius - mHalfOfBorderWidth;
mBorderBounds.left = mCenterX - mOuterCircleRadius + mHalfOfBorderWidth;
mBorderBounds.top = mCenterY - mOuterCircleRadius + mHalfOfBorderWidth;
mBorderBounds.bottom = mCenterY + mOuterCircleRadius - mHalfOfBorderWidth;
}
// =========================================================================================================
/**
* Start the progress/animation. Use this method to start the animated view.
*/
public void startProgress() {
if (mTotalProgress >= 0) {
float progressInDegrees = mTotalProgress;
mTotalProgressInDegrees = (int) (progressInDegrees/100 * 360);
invalidate();
}
}
#Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent pEvent) {
return super.dispatchPopulateAccessibilityEvent(pEvent);
}
// =========================================================================================================
// Getters && Setters!
/**
* #return the dialRadius
*/
public float getDialRadius() {
return mOuterCircleRadius;
}
/**
* #param pDialRadius
* the dialRadius to set
*/
public void setDialRadius(float pDialRadius) {
mOuterCircleRadius = pDialRadius;
}
/**
* #return the textSize
*/
public float getTextSize() {
return mTextSize;
}
/**
* #param pTextSize the textSize to set
*/
public void setTextSize(float pTextSize) {
mTextSize = pTextSize;
}
/**
* #return the textColor
*/
public int getTextColor() {
return mTextColor;
}
/**
* #param pTextColor the textColor to set
*/
public void setTextColor(int pTextColor) {
mTextColor = pTextColor;
}
/**
* #return the borderColor
*/
public int getBorderColor() {
return mBorderColor;
}
/**
* #param pBorderColor the borderColor to set
*/
public void setBorderColor(int pBorderColor) {
mBorderColor = pBorderColor;
}
/**
* #return the innerCircleColor
*/
public int getInnerCircleColor() {
return mInnerCircleColor;
}
/**
* #param pInnerCircleColor the innerCircleColor to set
*/
public void setInnerCircleColor(int pInnerCircleColor) {
mInnerCircleColor = pInnerCircleColor;
}
/**
* #return the titleText
*/
public String getTitleText() {
return mTitleText;
}
/**
* #param pTitleText the titleText to set
*/
public void setTitleText(String pTitleText) {
mTitleText = pTitleText;
}
/**
* #return the outerCircleRadius
*/
public float getOuterCircleRadius() {
return mOuterCircleRadius;
}
/**
* #param pOuterCircleRadius the outerCircleRadius to set
*/
public void setOuterCircleRadius(float pOuterCircleRadius) {
mOuterCircleRadius = pOuterCircleRadius;
}
/**
* #return the borderWidth
*/
public float getBorderWidth() {
return mBorderWidth;
}
/**
* #param pBorderWidth the borderWidth to set
*/
public void setBorderWidth(float pBorderWidth) {
mBorderWidth = pBorderWidth;
}
/**
* #return the totalProgress
*/
public int getTotalProgress() {
return mTotalProgress;
}
/**
* #param pTotalProgress the totalProgress to set
*/
public void setTotalProgress(int pTotalProgress) {
mTotalProgress = pTotalProgress;
}
}
EDIT
#Override
public boolean onTouchEvent(MotionEvent pEvent) {
double x = pEvent.getX();
double y = pEvent.getY();
double x1 = x - mCenterX;
double y1 = y - mCenterY;
double distance = Math.sqrt(x1*x1 + y1*y1);
RectF topBoundingRect = new RectF(mCenterX - mOuterCircleRadius, mCenterY - mOuterCircleRadius, mCenterX + mOuterCircleRadius, mCenterY);
if (Math.abs(distance - mOuterCircleRadius) <= MAX_TOUCH_TOLERANCE && topBoundingRect.contains((float) x, (float) y)) {
// the user is touching the arc. Which arc is tapped? How do I know that?
}
return true;
}
I won't post code for this, since I'm not completely comfortable working with Java UIs, but the math behind what you're describing shouldn't be too hard.
To make sure that I understand what you're doing: you have a circular arc defined by some center point (x0, y0), a radius r, a start angle θ0, and an end angle θ1. You then want to take a test point (x, y) and determine whether the user clicked on the circle.
This problem is a lot easier to solve if we translate everything back to the origin, since trigonometry is always easier relative to the origin. So let's let
x' = x - x0
y' = y - y0
Now that you have x' and y', we can determine how far away it is from the center of the circle by computing
dist = √(x'2 + y'2)
If this value isn't close to the radius r, then there's no way that the point clicked is anywhere near the arc. Since the arc mathematically is infinitely small, you probably want to set up some "tolerance" for when the user clicks on the arc. You could, for example, define some constant TOLERANCE and then assume the user is clicking on the circumference of the circle if
|dist - r| ≤ TOLERANCE
Now, this assumes that the arc is just the border of the circle. If you are drawing a filled-in circular arc, you can instead just check whether
dist ≤ r + TOLERANCE
This checks whether the point is inside the circle at all.
Now, at this point you can check whether the point is in/inside the circle at all. The next question is whether they clicked on a part of the circle that's part of the arc. To do this, you can compute the angle θ at which the the point is relative to the center of the circle by using Math.atan2 and computing Math.atan2(y', x'). This gives you back an angle θ (in radians). You can then check whether θ0 ≤ θ ≤ θ1, which will then tell you if they clicked on the part of the circle you care about.
In short:
Compute x' and y' from x, y, x0, and y0.
Compute dist from x' and y'.
Determine whether they hit the circle / circumference by using the above math.
Use Math.atan2 to get the angle θ
See if θ is in the range you want.
Hope this helps!