Remove rectangle objects from linked list - java

I'm trying to a make simple project by myself.This project works like the game of Tetris.Rectangle Objects are created with timer and randomly in 3 different colors.objects are kept on the list.
I checked the alignment of Y with moveRectangle() and control() function.
If three of the same color side by side or on top of block,they will be removed and upper blocks will replace them.
That's my problem.I've tried every way but I couldn't do that.
That works for creating the rectangle objects and controlling objects x and y coordinates;
package p1;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.Timer;
public class DropObject {
private int yukseklik;
private int block;
private Color r;
//private static int sayac=-1;
private static int cntr;
private Rectangle object;
private static int sx=60;
private static int [] sy=new int[9];
public static int a=3;
public static Color [] colorR=new Color[25];
public DropObject(int yukseklik,int x,int y,int size,Color r)
{
x=sx;
//y=sy;
this.r=EventRect.RColor();
this.yukseklik=yukseklik;
//set();
this.object=new Rectangle(x,y,size,size);
}
/*public void set(){
colorR[cntr]=this.r;
//System.out.println("Renk: "+colorR[cntr]);
cntr++;
}*/
public static int getSX(){
return sx;
}
public static void setSX(int x){
sx=x;
}
public static int getSY(){
return sy[a];
}
public static void setSY(int y){
sy[a]=y;
}
public Color getColor(){
//colorR[++sayac]=this.r;
return this.r;
}
public int getYukseklik(){
return yukseklik;
}
public void setYukseklik(int yukseklik){
this.yukseklik=yukseklik;
}
public Rectangle getObject(){
return object;
}
public void setObject(Rectangle object){
this.object=object;
this.object.x=sx;
}
}
EventRect class provides objects movements and specifies objects y coordinate position;
package p1;
import java.awt.Button;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
//import java.util.Timer;
import java.util.TimerTask;
import javax.swing.Timer;
import java.util.Random;
import javax.swing.JPanel;
public class EventRect extends JPanel {
private static int yukseklik=340;
private static Color BlockC;
private DropObject koordinat;
private int zheight=yukseklik;
private int fheight=yukseklik;
private int sheight=yukseklik;
private int theight=yukseklik;
private int foheight=yukseklik;
private int fiheight=yukseklik;
private int ssheight=yukseklik;
private int seheight=yukseklik;
private int seiheight=yukseklik;
private int counter=-1;
private int counter1=-1;
private int counter0=-1;
private List<DropObject> objects=new LinkedList<DropObject>();
private LinkedList<TimerTask> tasklist=new LinkedList<TimerTask>();
Timer timet;
int i=1;
public EventRect(){
addKeyListener(new InputKey());
setFocusable(true);
//System.out.println((objects.size()));
koordinat=new DropObject(yukseklik,60,-20,20,BlockC);
objects.add(koordinat);
startSampling();
//timet=new Timer(10,this);
//timet.start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d=(Graphics2D)g;
for(DropObject o : objects){
Rectangle r=o.getObject();
g2d.setColor(o.getColor());
//System.out.println(r.x);
g2d.fillRect(r.x, r.y, r.width+1, r.height+1);
// System.out.println(objects.size());
}
}
public void moveRectangle(){
for(int i=(objects.size()-1);i>=(objects.size()-1);i--){
DropObject o=objects.get(i);
Rectangle r=o.getObject();
if(DropObject.a==0){
DropObject.setSY(zheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==1){
DropObject.setSY(fheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==2){
DropObject.setSY(sheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==3){
DropObject.setSY(theight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==4){
DropObject.setSY(foheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==5){
DropObject.setSY(fiheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==6){
DropObject.setSY(ssheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==7){
DropObject.setSY(seheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==8){
DropObject.setSY(seiheight);
o.setYukseklik(DropObject.getSY());
}
if(o.getYukseklik()>=0){
if(r.y<o.getYukseklik()){
r.y+=r.height;
o.setObject(r);
}else{
o.setYukseklik(o.getYukseklik()-r.height);
}
}
if(r.y == o.getYukseklik()){
control();
objects.add(new DropObject(o.getYukseklik(),60,-20,20,BlockC));
}
}
}
void control(){
if(DropObject.a==0){
zheight-=20;
DropObject.setSY(zheight);
}
if(DropObject.a==1){
fheight-=20;
DropObject.setSY(fheight);
}
if(DropObject.a==2){
sheight-=20;
DropObject.setSY(sheight);
}
if(DropObject.a==3){
theight-=20;
DropObject.setSY(theight);
}
if(DropObject.a==4){
foheight-=20;
DropObject.setSY(foheight);
}
if(DropObject.a==5){
fiheight-=20;
DropObject.setSY(fiheight);
}
if(DropObject.a==6){
ssheight-=20;
DropObject.setSY(ssheight);
}
if(DropObject.a==7){
seheight-=20;
DropObject.setSY(seheight);
}
if(DropObject.a==8){
seiheight-=20;
DropObject.setSY(seiheight);
}
}
void startSampling(){
TimerTask task=new TimerTask(){
public void run(){
moveRectangle();
repaint();
}
};
java.util.Timer timer=new java.util.Timer();
timer.scheduleAtFixedRate(task,0,150);
tasklist.add(task);
}
void stopSampling(){
if(tasklist.isEmpty()){
return;
}
tasklist.removeFirst().cancel();
}
public static Color RColor(){
Color [] Array={Color.blue,Color.pink,Color.gray};
Random random=new Random();
BlockC=Array[random.nextInt(Array.length)];
return BlockC;
}
}
Keyboard movements;
package p1;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class InputKey extends KeyAdapter{
public void keyPressed(KeyEvent e){
int keys=e.getKeyCode();
if(keys==KeyEvent.VK_RIGHT){
//System.out.println("saga kaydi");
if(DropObject.getSX()<160)
DropObject.setSX(DropObject.getSX()+20);
if(DropObject.a<8){
DropObject.a++;
System.out.println("a nin degeri: "+DropObject.a);
}
}
else if(keys==KeyEvent.VK_LEFT){
if(DropObject.getSX()>0)
DropObject.setSX(DropObject.getSX()-20);
if(DropObject.a>0){
DropObject.a--;
}
}
}
}
package p1;
import java.awt.Color;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Main implements Runnable{
#Override
public void run(){
JFrame frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container=frame.getContentPane();
container.setLayout(new GridLayout(0,1));
container.add(new EventRect());
frame.setSize(197,400);
frame.setVisible(true);
//container.setBackground(Color.BLUE);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main());
}
}

Here is my heavily modified solution based on your original code and idea. It implements an object oriented approach to tracking the blocks. It uses a list of blocks to represent each column. It also has an ArrayList to store a reference to all the blocks on the screen so that we can loop through for drawing. Sorry it won't be in whatever your native language was, but hopefully you can still understand it. The main code you need is stored in the BlockBuster class, which handles the adjacency detection and removal of blocks. You should separate out each class into its own file.
Block.java
package p1;
import java.awt.Color;
import java.awt.Rectangle;
import java.util.Random;
public class Block {
/**
* Color
*/
private Color color;
/**
* The pixel coordinate bounding rectangle for the block.
*/
private Rectangle bounds;
/**
* the integer column
*/
private int column;
/**
* Creates a new instance of a block at the specified
*
* #param y Distance in pixels from the top of the form.
* #param column The integer column index (0-8)
* #param size The integer size.
*/
public Block(int y, int column, int size) {
this.color = RandomColor();
this.bounds = new Rectangle(20*column, y, size, size);
this.column = column;
}
/**
* Gets the color of this block.
* #return the color of this block.
*/
public Color getColor() {
return this.color;
}
/**
* Gets the pixel bounds for this block.
* #return The bounds.
*/
public Rectangle getBounds() {
return bounds;
}
/**
* Sets the bounds for this block.
* #param object
*/
public void setBounds(Rectangle object) {
this.bounds = object;
}
/**
* This is Elevation in units of blocks, like column
* #return
*/
public int getLevel(){
return (340 - this.getBounds().y)/20;
}
/**
* Gets the bottom of the block.
* #return
*/
public int getBottom(){
return this.bounds.y + this.bounds.height;
}
/**
* Gets the integer column index of the block (0-8)
* #return the column
*/
public int getColumn() {
return column;
}
/**
* Sets the integer column index of the block (0-8)
* #param column the column to set
*/
public void setColumn(int column) {
this.column = column;
}
/**
* Generates a random color.
* #return Color
*/
public static Color RandomColor() {
Color[] Array = {Color.blue, Color.pink, Color.gray};
Random random = new Random();
return Array[random.nextInt(Array.length)];
}
}
BlockBuster.java
package p1;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
/**
* This class handles the work of finding adjacent blocks.
* To delete blocks ONLY if the blocks are horizontally adjacent,
* then you can remove the "Up" and "Down" sections.
* #author Ted
*/
public class BlockBuster {
private List<Block> queue;
private List<Block> bustList;
/**
* Creates a new instance of the BlockBuster class.
*/
public BlockBuster() {
queue = new ArrayList<Block>();
}
/**
* This method busts the blocks of with matching colors
*/
public void bustBlocks() {
Block block = BlockManager.getInstance().getFallingBlock();
bustList = new ArrayList<Block>();
queue.add(block);
while (queue.size() > 0) {
Block current = queue.get(0);
addMatchingNeighbors(current);
queue.remove(0);
if(!bustList.contains(current)){
bustList.add(current);
}
}
if(bustList.size() > 2){
for(Block busted : bustList){
BlockManager.getInstance().RemoveBlock(busted);
List<Block> check = BlockManager.getInstance().getAllBlocks();
if(check.contains(busted)){
boolean stop = true;
}
}
}
}
/**
* This method adds neighbors that match in color to the queue
* as long as they don't already exist in the queue or bustlist.
* #param block The current block to consider.
*/
private void addMatchingNeighbors(Block block) {
// currently only check by 4 connectivity
int x = block.getColumn();
int y = block.getLevel();
Color color = block.getColor();
// left
Block leftBlock = getBlock(x - 1, y);
if(colorMatches(color, leftBlock)){
queue.add(leftBlock);
}
// Right
Block rightBlock = getBlock(x + 1, y);
if(colorMatches(color, rightBlock)){
queue.add(rightBlock);
}
// Up
Block upBlock = getBlock(x, y + 1);
if(colorMatches(color, upBlock)){
queue.add(upBlock);
}
Block downBlock = getBlock(x, y-1);
if(colorMatches(color, downBlock)){
queue.add(downBlock);
}
}
/**
* This tests whether the block's color matches the specified
* color, and whether or not the block is already in a list.
* #param color The color that should match for deletion.
* #param block The block to be removed.
* #return
*/
public boolean colorMatches(Color color, Block block){
if(block == null){
return false;
}
if(bustList.contains(block)){
return false;
}
if(queue.contains(block)){
return false;
}
return (block.getColor().equals(color));
}
/**
* This finds a block at the specified column and height.
* #param column The integer column index (0-8)
* #param height The integer level (in blocks)
* #return a Block
*/
public Block getBlock(int column, int level) {
Column[] columns = BlockManager.getInstance().getColumns();
if (column >= 0 && column < 10) {
if (columns[column].getBlocks().size() > level && level >= 0) {
Block result = columns[column].getBlocks().get(level);
if(bustList.contains(result)){
return null;
}
return result;
}
}
return null;
}
}
BlockManager.java
package p1;
import java.util.ArrayList;
import java.util.List;
/**
* This is a singleton to control the blocks and is visible
* from the other classes in the application.
* #author Ted
*/
public class BlockManager {
/**
* The single instance of a falling block.
*/
private Block fallingBlock;
/**
* All blocks on the page, including the falling one.
*/
private List<Block> allBlocks;
/**
* The array of columns at the bottom, containing blocks.
*/
private Column[] columns;
/**
* Creates an instance of a BlockManager class. As part
* of the singleton pattern, this is private so that
* you can only access the shared instance.
*/
private BlockManager() {
allBlocks = new ArrayList<Block>();
columns = new Column[9];
for(int i = 0; i < 9; i++)
{
columns[i] = new Column();
}
}
/**
* Gets the single shared instance of the singleton.
* #return BlockManager
*/
public static BlockManager getInstance() {
return BlockManagerHolder.INSTANCE;
}
private static class BlockManagerHolder {
private static final BlockManager INSTANCE = new BlockManager();
}
/**
* Adds a new falling block to the top of the control.
*/
public void AddBlock(){
fallingBlock = new Block(0, 3, 20);
allBlocks.add(fallingBlock);
}
/**
* Removes the specified block.
* #param block
*/
public void RemoveBlock(Block block){
allBlocks.remove(block);
List<Block> blocks = columns[block.getColumn()].getBlocks();
if(blocks.size() > block.getLevel()){
for(int i = block.getLevel(); i < blocks.size(); i++)
{
// Make blocks above this block drop.
blocks.get(i).getBounds().y += 20;
}
}
columns[block.getColumn()].getBlocks().remove(block);
}
/**
* Gets the falling block.
* #return the fallingBlock
*/
public Block getFallingBlock() {
return fallingBlock;
}
/**
* Sets the falling block.
* #param fallingBlock the fallingBlock to set
*/
public void setFallingBlock(Block fallingBlock) {
this.fallingBlock = fallingBlock;
}
/**
* Gets the list of all blocks visible on the form,
* including the falling block.
* #return the allBlocks
*/
public List<Block> getAllBlocks() {
return allBlocks;
}
/**
* Sets the list of all blocks visible on the form,
* including the falling block.
* #param allBlocks the allBlocks to set
*/
public void setAllBlocks(List<Block> allBlocks) {
this.allBlocks = allBlocks;
}
/**
* Gets the array of columns containing blocks at the bottom.
* #return the columns
*/
public Column[] getColumns() {
return columns;
}
/**
* Sets the array of columns containing blocks at the bottom.
* #param columns the columns to set
*/
public void setColumns(Column[] columns) {
this.columns = columns;
}
}
Column.java
package p1;
import java.util.ArrayList;
import java.util.List;
/**
* A Column is an object that contains a list of the blocks stored
* in a vertical stack at the bottom of the game. Each block object
* points to the same object instance in allBlocks.
* #author Ted
*/
public class Column {
private static int maxHeight = 340;
private List<Block> blocks;
/**
* Gets the maximum vertical pixel height of the form.
* #return the maxHeight
*/
public static int getMaxHeight() {
return maxHeight;
}
/**
* Sets the maximum vertical pixel height of the form.
* #param aMaxHeight the maxHeight to set
*/
public static void setMaxHeight(int aMaxHeight) {
maxHeight = aMaxHeight;
}
/**
* Creates a new instance of the column class.
*/
public Column(){
blocks = new ArrayList<Block>();
}
/**
* Gets the amount of space remaining in this column
* #return
*/
public int getDepthRemaining(){
return maxHeight - getBlockHeight();
}
/**
* Gets the total height of all the blocks in this column
* #return
*/
public int getBlockHeight(){
int height = 0;
for(Block block : blocks){
height += block.getBounds().height;
}
return height;
}
/**
* Gets the list of blocks associated with this column.
* The 0 index is at the bottom.
* #return the dropObjects
*/
public List<Block> getBlocks() {
return blocks;
}
/**
* Sets the list of blocks associated with this column.
* #param dropObjects the dropObjects to set
*/
public void setBlocks(List<Block> dropObjects) {
this.blocks = dropObjects;
}
}
EventRect.java
package p1;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.LinkedList;
import java.util.TimerTask;
import javax.swing.Timer;
import javax.swing.JPanel;
/**
* EventRect is the main form for the game.
*/
public class EventRect extends JPanel {
/**
* #return the fallingBlock
*/
public static Block getFallingBlock() {
return BlockManager.getInstance().getFallingBlock();
}
private BlockBuster blockBuster;
private LinkedList<TimerTask> tasklist = new LinkedList<TimerTask>();
Timer timet;
int i = 1;
/**
* Creates a new instance of the main form for the project.
*/
public EventRect() {
BlockManager.getInstance().AddBlock();
this.blockBuster = new BlockBuster();
addKeyListener(new InputKey());
setFocusable(true);
startSampling();
}
/**
* Paints all the blocks onto the screen, including the falling block.
* #param g The Graphics surface of the form.
*/
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Block dropObject : BlockManager.getInstance().getAllBlocks()) {
Rectangle bounds = dropObject.getBounds();
g2d.setColor(dropObject.getColor());
g2d.fillRect(bounds.x, bounds.y, bounds.width + 1, bounds.height + 1);
}
}
/**
* This handles the downward motion of the falling block.
*/
public void moveRectangle() {
Column[] columns = BlockManager.getInstance().getColumns();
if(getFallingBlock().getBounds().getY() >=
columns[getFallingBlock().getColumn()].getDepthRemaining()){
// We hit the top of the column, so add this block to the column
columns[getFallingBlock().getColumn()].getBlocks().add(getFallingBlock());
blockBuster.bustBlocks();
BlockManager.getInstance().AddBlock();
}
else{
BlockManager.getInstance().getFallingBlock().getBounds().y += 20;
}
}
/**
* This method was made final because it is called from the
* constructor. This starts game timers (from original post).
*/
final void startSampling() {
TimerTask task = new TimerTask() {
#Override
public void run() {
moveRectangle();
repaint();
}
};
java.util.Timer timer = new java.util.Timer();
timer.scheduleAtFixedRate(task, 0, 150);
tasklist.add(task);
}
/**
* This method removes the timer from the tasklist.
*/
void stopSampling() {
if (tasklist.isEmpty()) {
return;
}
tasklist.removeFirst().cancel();
}
}
InputKey.java
package p1;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
/**
* This class handles the movement of blocks. This
* class was modified to include collision detection
* with blocks in a neighboring column.
*/
public class InputKey extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
int keys = e.getKeyCode();
Block block = EventRect.getFallingBlock();
if (block == null) {
return;
}
Rectangle rect = block.getBounds();
if (keys == KeyEvent.VK_RIGHT) {
//Check for right bounds
if (rect.x < 160) {
// Check to see if we run into another block
int level = block.getLevel();
int column = block.getColumn();
if (BlockManager.getInstance().getColumns()[column + 1].getBlocks().size() > level) {
// we ran into a block
return;
}
rect.x += 20;
block.setColumn(rect.x / 20);
}
} else if (keys == KeyEvent.VK_LEFT) {
// Check for left bounds
if (rect.x > 0) {
// Check to see if we run into another block
int level = block.getLevel();
int column = block.getColumn();
if (BlockManager.getInstance().getColumns()[column + 1].getBlocks().size() > level) {
// we ran into a block
return;
}
rect.x -= 20;
block.setColumn(rect.x / 20);
}
}
}
}
Main.java
package p1;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
/**
* This class runs the program.
* #author Ted
*/
public class Main implements Runnable {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = frame.getContentPane();
container.setLayout(new GridLayout(0, 1));
container.add(new EventRect());
frame.setSize(197, 400);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main());
}
}

I had once implemented tetris like game and the way i detected collision and filled areas was by following datastructures
The whole space where you shapes are going to settle down is an grid represented with array, inititally 0 and filled with 1 on cells where a block falls.
Every block is also a square grid with shape defined by filling it with 1s where required.
After this model its trivial to detect collision as well as filled areas. If you want colors treated differently you could assign 1,2,3..so on for every color to the cell. Hope that helps.

Related

How to bounce text in a window without JFrame?

I am trying to bounce my text in a window, inserted from a text file without modifying the inputs from the text. I have the code to create and bounce the text, but some of it is lost when trying to bounce on the wall. The code consists of 3 classes.
This creates the text:
public class MyShape extends ClosedShape {
//The width and height of the text (major and minor axis)
private int width, height;
private String bouncingText;
private double textFont;
/**
* Creates an oval.
* #param x The display component's x position.
* #param y The display component's y position.
* #param vx The display component's x velocity.
* #param vy The display component's y velocity.
* #param width The width of the text (in pixels).
* #param height The height of the text (in pixels).
* #param colour The line colour or fill colour.
* #param isFilled True if the oval is filled with colour, false if opaque.
*/
public MyShape (int insertionTime, int x, int y, int vx, int vy, String bouncingText, int width, int height, Color colour, boolean isFilled, double textFont) {
super (insertionTime, x, y, vx, vy, colour, isFilled);
this.width = width;
this.height = height;
this.bouncingText = bouncingText;
this.textFont = textFont;
}
/**
* Method to convert a text to a string.
*/
public String toString () {
String result = "This is a text shape\n";
result += super.toString ();
result += "Its width is " + this.width + " and its height is " + this.height + "\n";
return result;
}
/**
* #param width Resets the width.
*/
public void setWidth (int width) {
this.width = width;
}
/**
* #param height Resets the height.
*/
public void setHeight (int height) {
this.height = height;
}
public void setBouncingText (String bouncingText) {
this.bouncingText = bouncingText;
}
public void setTextFont (double textFont) {
this.textFont = textFont;
}
/**
* #return The width of the text.
*/
public int getWidth() {
return width;
}
/**
* #return The height of the text.
*/
public int getHeight() {
return height;
}
public String getBouncingText() {
return bouncingText;
}
public double getTextFont() {
return textFont;
}
public void pulsing() {
if (textFont > 20) {
textFont = 10;
}else {
textFont = 50;
}
}
/**
* Draw the text.
* #param g The graphics object of the drawable component.
*/
public void draw (GraphicsContext g) {
Font bouncingTextFont = new Font ("TimesNew", textFont);
g.setFill (colour);
g.setStroke( colour );
if (isFilled) {
g.fillText(bouncingText, xPos, yPos );
g.setFont(bouncingTextFont);
}
else {
g.strokeText(bouncingText, xPos, yPos );
g.setFont(bouncingTextFont);
}
}
}
This is the bouncing code:
import java.util.ArrayList;
import javafx.animation.Animation;
import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
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.scene.shape.ArcType;
import javafx.stage.Stage;
import javafx.util.Duration;
public class BouncingShapesWindow {
private static final int ANIMATION_DELAY = 10;
private static final String FRAME_TITLE = "Shape Booooiiinggg Frame";
private GraphicsContext gc;
private Queue shapesToAdd;
private ArrayList<ClosedShape> activeShapes;
private int currentTime = 0;
private boolean flag=true;
private String filename;
public BouncingShapesWindow(GraphicsContext gc,String filename) {
this.gc=gc;
activeShapes=new ArrayList<ClosedShape>();
this.initShapes(filename);
this.insertShapes ();
drawShapes();
actionPerformed();
}
private void drawShapes () {
for (ClosedShape s : activeShapes)
{
s.draw(gc);
}
}
private void initShapes (String filename) {
shapesToAdd = ReadShapeFile.readFile(filename);
}
private void insertShapes() {
//no more shapes to add, we are done
if (shapesToAdd.isEmpty ()) {
return;
}
//add shapes if needed
ClosedShape current = (ClosedShape) shapesToAdd.peek ();
while (!shapesToAdd.isEmpty () && (current.getInsertionTime() <= currentTime*ANIMATION_DELAY)) {
activeShapes.add(current);
shapesToAdd.dequeue();
if (!shapesToAdd.isEmpty ()) {
current = (ClosedShape) shapesToAdd.peek();
}
}
}
public void actionPerformed()
{
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(5), ae -> onTime()));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
private void onTime() {
currentTime++;
double h =gc.getCanvas().getHeight();
double w = gc.getCanvas().getWidth();
gc.clearRect(0, 0, w, h);
moveShapes();
insertShapes ();
drawShapes();
}
public void moveShapes()
{
double dimsY = gc.getCanvas().getHeight() ;
double dimsX = gc.getCanvas().getWidth() ;
for (ClosedShape s : activeShapes)
{
s.move();
// Move us back in and bounce if we went outside the drawing area.
if (s.outOfBoundsX(dimsX))
{
s.putInBoundsX(dimsX);
s.bounceX();
}
if (s.outOfBoundsY(dimsY))
{
s.putInBoundsY(dimsY);
s.bounceY();
}
}
}
}
and this is the super class:
import javafx.scene.paint.Color;
import javafx.scene.canvas.GraphicsContext;
/**
* A ClosedShape is any shape that can be drawn without
* taking a pencil off a piece of paper.
* It's representation on computer has a line colour
* and a position on the drawable screen component.
* It can be filled in with colour or opaque.
* This class is a super class for all shapes.
*/
public class ClosedShape {
/**
* The x position of the Shape.
*/
protected int xPos;
/**
* The y position of the Shape.
*/
protected int yPos = 20;
/**
* The x position of the Shape.
*/
protected int xVec;
/**
* The y position of the Shape.
*/
protected int yVec;
/**
* The line colour of the shape, or the filled in
* colour if the Shape has fill.
*/
protected Color colour;
/**
* Determines if the Shape has a fill colour or not.
*/
protected boolean isFilled;
/**
* Encodes the insertion time into the scene
*/
private int insertionTime;
/**
* Creates a closed shape object.
* #param x The x position.
* #param y the y position.
* #param colour The line or fill colour.
* #param isFilled True if the shape is filled, false if not.
*/
protected ClosedShape (int insertionTime, int x, int y, int vx, int vy, Color colour, boolean isFilled) {
this.xPos = x;
this.yPos = y;
this.xVec = vx;
this.yVec = vy;
this.colour = colour;
this.isFilled = isFilled;
this.insertionTime = insertionTime;
}
/**
* The method returns a string suitable for printing.
* #return string to print out shape.
*/
public String toString () {
String result = "";
result += "Its position is " + xPos + " " + yPos + "\n";
result += "Its velocity is " + xVec + " " + yVec + "\n";
result += "Its colour is " + colour + "\n";
if (isFilled)
result += "It is filled" + "\n";
else
result += "It is not filled" + "\n";
result += "It should be inserted at " + insertionTime + "\n";
return result;
}
/**
* Resets the x position.
*/
public void setX (int x) {
this.xPos = x;
}
/**
* Resets the y position.
*/
public void setY (int y) {
this.yPos = y;
}
/**
* Resets the x vector
*/
public void setVecX (int x) {
this.xVec = x;
}//end setVecX
/**
* Resets the y position.
*/
public void setVecY (int y) {
this.yVec = y;
}//end setVecY
/**
* Resets the colour.
*/
public void setColour (Color colour) {
this.colour = colour;
}
/**
* Sets the shape to filled.
*/
public void setFilled () {
isFilled = true;
}
/**
* Sets the shape to unfilled.
*/
public void unsetFilled () {
isFilled = false;
}
/**
* Sets the insertion time.
*/
public void setInsertionTime (int time) {
insertionTime = time;
}
/**
* #return The x position value.
*/
public int getX() {
return xPos;
}
/**
* #return The y position value.
*/
public int getY() {
return yPos;
}
/**
* #return The colour.
*/
public Color getColour() {
return colour;
}
/**
* #return True if the shape is filled, false if not.
*/
public boolean isFilled() {
return isFilled;
}
/**
* #return the insertion time.
*/
public int getInsertionTime () {
return insertionTime;
}
/**
* Puts the shape back in bounds in X
*/
public void putInBoundsX (double winX) {
if (xPos < 0) xPos = 0;
if (xPos + this.getWidth() > winX) {
xPos = (int) (winX - Math.ceil (this.getWidth ()));
}
}//end inBoundsX;
/**
* Puts the shape back in bounds
*/
public void putInBoundsY (double winY) {
if (yPos < 0) yPos = 0;
if (yPos + this.getHeight() > winY) {
yPos = (int) (winY - Math.ceil (this.getHeight ()));
}
}//end inBoundsY;
/**
* Bounces the shape off a vertical wall
*/
public void bounceX () {
xVec = -xVec;
}
/**
* Bounces the shape off a horizontal wall
*/
public void bounceY () {
yVec = -yVec;
}
/**
* Returns true if the shapes have gone out of bounds in X
*/
public boolean outOfBoundsX (double winX) {
return (xPos + this.getWidth()> winX) || (xPos < 0);
}
/**
* Returns true if the shapes have gone out of bounds in Y
*/
public boolean outOfBoundsY (double winY) {
return (yPos + this.getHeight() > winY) || (yPos < 0);
}
/**
* Takes in a direction and a velocity and moves the shape
* in that direction on unit
*/
public void move () {
xPos += xVec;
yPos += yVec;
}
/**
* Draws the object to the current component.
* #param g The graphics object associated with the drawing component.
*/
public void draw (GraphicsContext g) {
System.out.println ("You forgot to override this method! (draw)");
System.out.println ("Don't modify this method.");
}
/**
* Get the width of the current component
*/
public int getWidth () {
System.out.println ("You forgot to override this method! (getWidth)");
System.out.println ("Don't modify this method.");
return 1;
}
/**
* Get the width of the current component
*/
public int getHeight () {
System.out.println ("You forgot to override a method! (getHeight)");
System.out.println ("Don't modify this method.");
return 1;
}
public int getSide () {
System.out.println ("You forgot to override this method! (getSide)");
System.out.println ("Don't modify this method.");
return 1;
}
}
I tried reversing it, but nothing works. Using other shapes like circle or rect is OK, but with text it bounces under the head of the window and if I change the font size it loses its boundaries. I tried searching for solutions and it seems that everything uses JFrame and JPanel. I hope the code I provided leads to a useful solution.

How can I redefine VerticalTableHeaderCellRenderer() for javax.swing.JTable(), to write text vertically

The goal to write the values of columns into JTable vertically. I read about DefaultTableCellRenderer, VerticalTableHeaderCellRenderer() but cannot implement it. Here is my code:
//=========================================================
private JScrollPane getTablePane() {
if (tablePane == null) {
tablePane = new JScrollPane();
tablePane.setRowHeaderView(getTableDictionary());
tablePane.setViewportView(getTableDictionary());
}
return tablePane;
}
//=============================================================
private JTable getTableDictionary(){
if (table == null) {
rowVector = new String[colCount];
for(int i=0; i<colCount;i++) {rowVector[i]="";}
data = new DefaultTableModel(rowVector, 0);
for (int i = 0; i < rowCount; i++) { data.addRow(rowVector); }
table = new JTable(data);
table.getTableHeader().setDefaultRenderer(new VerticalTableHeaderCellRenderer());
for(int i=1; i<colCount; i++) { table.getColumnModel().getColumn(i).setPreferredWidth(withCol);}
table.setSelectionForeground(Color.BLACK);
table.setSelectionBackground(Color.YELLOW);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.setAutoscrolls(true);
table.setColumnSelectionAllowed(false);
table.setRowSelectionAllowed(true);
/* DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer();
int bg = table.getTableHeader().getBackground().getRGB();
defaultTableCellRenderer.setBackground(Color.getHSBColor(125, 125, 125)); //задаем цвет столбца
table.getColumnModel().getColumn(0).setCellRenderer(defaultTableCellRenderer);
*/
return table;
}
Where is my mistake? Thank you
I read more resources and I found a decision, it works. I need to implement a render for that and call it when I am locating the table on the JScrollPane. It means that column header is processing apart from a JTable using datamodel. Also, I can define the height of the columns.
private JScrollPane getTablePane() {
if (tablePane == null) {
tablePane = new JScrollPane();
tablePane.setRowHeaderView(getTableDictionary());
tablePane.setViewportView(getTableDictionary());
tablePane.setColumnHeader(new JViewport() {
#Override public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.height = rowColumnHeigth; // Col header Height
return d;
}
});
}
return tablePane;
}
Also, I need for that additional classes:
/**
* #(#)DefaultTableHeaderCellRenderer.java 1.0 02/24/09
*/
import java.awt.Component;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
/**
* A default cell renderer for a JTableHeader.
* <P>
* DefaultTableHeaderCellRenderer attempts to provide identical behavior to the
* renderer which the Swing subsystem uses by default, the Sun proprietary
* class sun.swing.table.DefaultTableCellHeaderRenderer.
* <P>
* To apply any desired customization, DefaultTableHeaderCellRenderer may be
* suitably extended.
*
* #author Darryl
*/
public class DefaultTableHeaderCellRenderer extends DefaultTableCellRenderer {
/**
* Constructs a <code>DefaultTableHeaderCellRenderer</code>.
* <P>
* The horizontal alignment and text position are set as appropriate to a
* table header cell, and the opaque property is set to false.
*/
public DefaultTableHeaderCellRenderer() {
setHorizontalAlignment(CENTER);
setHorizontalTextPosition(LEFT);
setVerticalAlignment(BOTTOM);
setOpaque(false);
}
/**
* Returns the default table header cell renderer.
* <P>
* If the column is sorted, the approapriate icon is retrieved from the
* current Look and Feel, and a border appropriate to a table header cell
* is applied.
* <P>
* Subclasses may overide this method to provide custom content or
* formatting.
*
* #param table the <code>JTable</code>.
* #param value the value to assign to the header cell
* #param isSelected This parameter is ignored.
* #param hasFocus This parameter is ignored.
* #param row This parameter is ignored.
* #param column the column of the header cell to render
* #return the default table header cell renderer
*/
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
JTableHeader tableHeader = table.getTableHeader();
if (tableHeader != null) {
setForeground(tableHeader.getForeground());
}
setIcon(getIcon(table, column));
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
return this;
}
/**
* Overloaded to return an icon suitable to the primary sorted column, or null if
* the column is not the primary sort key.
*
* #param table the <code>JTable</code>.
* #param column the column index.
* #return the sort icon, or null if the column is unsorted.
*/
protected Icon getIcon(JTable table, int column) {
SortKey sortKey = getSortKey(table, column);
if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) {
switch (sortKey.getSortOrder()) {
case ASCENDING:
return UIManager.getIcon("Table.ascendingSortIcon");
case DESCENDING:
return UIManager.getIcon("Table.descendingSortIcon");
}
}
return null;
}
/**
* Returns the current sort key, or null if the column is unsorted.
*
* #param table the table
* #param column the column index
* #return the SortKey, or null if the column is unsorted
*/
protected SortKey getSortKey(JTable table, int column) {
RowSorter rowSorter = table.getRowSorter();
if (rowSorter == null) {
return null;
}
List sortedColumns = rowSorter.getSortKeys();
if (sortedColumns.size() > 0) {
return (SortKey) sortedColumns.get(0);
}
return null;
}
}
Also:
/**
* #(#)VerticalLabelUI.java 1.0 02/18/09
*/
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicLabelUI;
/**
* A UI delegate for JLabel that rotates the label 90є
* <P>
* Extends {#link BasicLabelUI}.
* <P>
* The only difference between the appearance of labels in the Basic and Metal
* L&Fs is the manner in which diabled text is painted. As VerticalLabelUI
* does not override the method paintDisabledText, this class can be adapted
* for Metal L&F by extending MetalLabelUI instead of BasicLabelUI.
* <P>
* No other changes are required.
*
* #author Darryl
*/
public class VerticalLabelUI extends BasicLabelUI {
private boolean clockwise = false;
// see comment in BasicLabelUI
Rectangle verticalViewR = new Rectangle();
Rectangle verticalIconR = new Rectangle();
Rectangle verticalTextR = new Rectangle();
protected static VerticalLabelUI verticalLabelUI =
new VerticalLabelUI();
private final static VerticalLabelUI SAFE_VERTICAL_LABEL_UI =
new VerticalLabelUI();
/**
* Constructs a <code>VerticalLabelUI</code> with the default anticlockwise
* rotation
*/
public VerticalLabelUI() {
}
/**
* Constructs a <code>VerticalLabelUI</code> with the desired rotation.
* <P>
* #param clockwise true to rotate clockwise, false for anticlockwise
*/
public VerticalLabelUI(boolean clockwise) {
this.clockwise = clockwise;
}
/**
* #see ComponentUI#createUI(javax.swing.JComponent)
*/
public static ComponentUI createUI(JComponent c) {
if (System.getSecurityManager() != null) {
return SAFE_VERTICAL_LABEL_UI;
} else {
return verticalLabelUI;
}
}
/**
* Overridden to always return -1, since a vertical label does not have a
* meaningful baseline.
*
* #see ComponentUI#getBaseline(JComponent, int, int)
*/
#Override
public int getBaseline(JComponent c, int width, int height) {
super.getBaseline(c, width, height);
return -1;
}
/**
* Overridden to always return Component.BaselineResizeBehavior.OTHER,
* since a vertical label does not have a meaningful baseline
*
* #see ComponentUI#getBaselineResizeBehavior(javax.swing.JComponent)
*/
#Override
public Component.BaselineResizeBehavior getBaselineResizeBehavior(
JComponent c) {
super.getBaselineResizeBehavior(c);
return Component.BaselineResizeBehavior.OTHER;
}
/**
* Transposes the view rectangles as appropriate for a vertical view
* before invoking the super method and copies them after they have been
* altered by {#link SwingUtilities#layoutCompoundLabel(FontMetrics, String,
* Icon, int, int, int, int, Rectangle, Rectangle, Rectangle, int)}
*/
#Override
protected String layoutCL(JLabel label, FontMetrics fontMetrics,
String text, Icon icon, Rectangle viewR, Rectangle iconR,
Rectangle textR) {
verticalViewR = transposeRectangle(viewR, verticalViewR);
verticalIconR = transposeRectangle(iconR, verticalIconR);
verticalTextR = transposeRectangle(textR, verticalTextR);
text = super.layoutCL(label, fontMetrics, text, icon,
verticalViewR, verticalIconR, verticalTextR);
viewR = copyRectangle(verticalViewR, viewR);
iconR = copyRectangle(verticalIconR, iconR);
textR = copyRectangle(verticalTextR, textR);
return text;
}
/**
* Transforms the Graphics for vertical rendering and invokes the
* super method.
*/
#Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
if (clockwise) {
g2.rotate(Math.PI / 2, c.getSize().width / 2, c.getSize().width / 2);
} else {
g2.rotate(-Math.PI / 2, c.getSize().height / 2, c.getSize().height / 2);
}
super.paint(g2, c);
}
/**
* Returns a Dimension appropriate for vertical rendering
*
* #see ComponentUI#getPreferredSize(javax.swing.JComponent)
*/
#Override
public Dimension getPreferredSize(JComponent c) {
return transposeDimension(super.getPreferredSize(c));
}
/**
* Returns a Dimension appropriate for vertical rendering
*
* #see ComponentUI#getMaximumSize(javax.swing.JComponent)
*/
#Override
public Dimension getMaximumSize(JComponent c) {
return transposeDimension(super.getMaximumSize(c));
}
/**
* Returns a Dimension appropriate for vertical rendering
*
* #see ComponentUI#getMinimumSize(javax.swing.JComponent)
*/
#Override
public Dimension getMinimumSize(JComponent c) {
return transposeDimension(super.getMinimumSize(c));
}
private Dimension transposeDimension(Dimension from) {
return new Dimension(from.height, from.width + 2);
}
private Rectangle transposeRectangle(Rectangle from, Rectangle to) {
if (to == null) {
to = new Rectangle();
}
to.x = from.y;
to.y = from.x;
to.width = from.height;
to.height = from.width;
return to;
}
private Rectangle copyRectangle(Rectangle from, Rectangle to) {
if (to == null) {
to = new Rectangle();
}
to.x = from.x;
to.y = from.y;
to.width = from.width;
to.height = from.height;
return to;
}
}
Also:
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.Icon;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.UIManager;
/**
* A renderer for a JTableHeader with text rotated 90В° counterclockwise.
* <P>
* Extends {#link DefaultTableHeaderCellRenderer}.
*
* #see VerticalLabelUI
* #author Darryl
*/
public class VerticalTableHeaderCellRenderer
extends DefaultTableHeaderCellRenderer {
/**
* Constructs a <code>VerticalTableHeaderCellRenderer</code>.
* <P>
* The horizontal and vertical alignments and text positions are set as
* appropriate to a vertical table header cell.
*/
public VerticalTableHeaderCellRenderer() {
setHorizontalAlignment(LEFT);
setHorizontalTextPosition(CENTER);
setVerticalAlignment(CENTER);
setVerticalTextPosition(TOP);
setUI(new VerticalLabelUI());
}
/**
* Overridden to return a rotated version of the sort icon.
*
* #param table the <code>JTable</code>.
* #param column the colummn index.
* #return the sort icon, or null if the column is unsorted.
*/
#Override
protected Icon getIcon(JTable table, int column) {
SortKey sortKey = getSortKey(table, column);
if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) {
SortOrder sortOrder = sortKey.getSortOrder();
switch (sortOrder) {
case ASCENDING:
return VerticalSortIcon.ASCENDING;
case DESCENDING:
return VerticalSortIcon.DESCENDING;
}
}
return null;
}
/**
* An icon implementation to paint the contained icon rotated 90В° clockwise.
* <P>
* This implementation assumes that the L&F provides ascending and
* descending sort icons of identical size.
*/
private enum VerticalSortIcon implements Icon {
ASCENDING(UIManager.getIcon("Table.ascendingSortIcon")),
DESCENDING(UIManager.getIcon("Table.descendingSortIcon"));
private final Icon icon;// = ;
private VerticalSortIcon(Icon icon) {
this.icon = icon;
}
/**
* Paints an icon suitable for the header of a sorted table column,
* rotated by 90В° clockwise. This rotation is applied to compensate
* the rotation already applied to the passed in Graphics reference
* by the VerticalLabelUI.
* <P>
* The icon is retrieved from the UIManager to obtain an icon
* appropriate to the L&F.
*
* #param c the component to which the icon is to be rendered
* #param g the graphics context
* #param x the X coordinate of the icon's top-left corner
* #param y the Y coordinate of the icon's top-left corner
*/
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
int maxSide = Math.max(getIconWidth(), getIconHeight()+(Integer)getIconHeight()/4);
Graphics2D g2 = (Graphics2D) g.create(x, y, maxSide, maxSide);
g2.rotate((Math.PI / 2));
g2.translate(0, -maxSide);
icon.paintIcon(c, g2, 0, 0);
g2.dispose();
}
/**
* Returns the width of the rotated icon.
*
* #return the <B>height</B> of the contained icon
*/
#Override
public int getIconWidth() {
return icon.getIconHeight();
}
/**
* Returns the height of the rotated icon.
*
* #return the <B>width</B> of the contained icon
*/
#Override
public int getIconHeight() {
return icon.getIconWidth();
}
}
}
It works fine for me. Thank you!
Also, it looks like:

JavaFX drag adjustable Label like Slider

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;
}
}

Drawing a Line with MouseAdapter Not Showing Java

Found this sample code which should produce a drawn line after clicking, but does not show anything or works. Assume all import statements are correct, code gives no errors and I have no idea why it would not work. The line color is red while the background is white so it should show clearly if it works. The mouse listener seems to be correct as well. Any reason why this code will not work?
public class PathPanel extends JPanel {
/**
* The panel width.
*/
public static final int WIDTH = 400;
/**
* The panel height.
*/
public static final int HEIGHT = 400;
/**
* The background color of the panel.
*/
public static final Color BACKGROUND_COLOR = Color.WHITE;
/**
* The color to paint with.
*/
public static final Color FOREGROUND_COLOR = Color.RED;
/**
* The line width.
*/
public static final int LINE_WIDTH = 8;
// Instance Fields
/**
*
*/
private static final long serialVersionUID = -3644129903653409515L;
/**
* The path being created.
*/
private final Path2D myPath;
// OR you could use Path2D.Double instead of GeneralPath
// Constructor
/**
* Constructs a new general path panel.
*/
public PathPanel() {
super();
myPath = new GeneralPath();
myPath.setWindingRule(GeneralPath.WIND_EVEN_ODD);
//myPath = new Path2D.Double();
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setBackground(BACKGROUND_COLOR);
addMouseListener(new MyMouseListener());
}
/**
* Paints the current path.
*
* #param theGraphics The graphics context to use for painting.
*/
#Override
public void paintComponent(final Graphics theGraphics) {
super.paintComponent(theGraphics);
final Graphics2D g2d = (Graphics2D) theGraphics;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(FOREGROUND_COLOR);
g2d.setStroke(new BasicStroke(LINE_WIDTH));
g2d.draw(myPath);
}
// Main Method
/**
* Creates and displays a GeneralPathPanel.
*
* #param theArgs Command line arguments (ignored).
*/
public static void main(final String... theArgs) {
final PathPanel panel = new PathPanel();
final JFrame frame = new JFrame("GeneralPathPanel Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// Inner Class
/**
* Listens for mouse clicks, to draw on our panel.
*/
private class MyMouseListener extends MouseAdapter {
/**
* Handles a click event.
*
* #param theEvent The event.
*/
#Override
public void mouseClicked(final MouseEvent theEvent) {
if (myPath.getCurrentPoint() == null) {
myPath.moveTo(theEvent.getX(), theEvent.getY());
} else if (theEvent.getClickCount() == 2) {
myPath.closePath();
} else {
myPath.lineTo(theEvent.getX(), theEvent.getY());
}
repaint();
}
}
}
The Sample Code you posted works fine for me. Have you tried to add a System.out.println() in the mouseClicked(final MouseEvent theEvent) Method to check if it is actually called? If it is not called, you could try to change it to mouseReleased.
The imports I used:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;

Graphics and SwingWorker

I'm trying to paint a dynamic graph that has a few thousand points. I'm using SwingWorker class that gets the Graphics object as an argument. I initialize the graphics object in the constructor of the SwingWorker class and then call the main graphing function doGraph() in the done() method i.e, after all the calculations are done. I'm having issues with painting, not sure what is going on. The code works if I call doGraph() from the constructor of the SwingWorker class but not when I place it in done().
Any help is greatly appreciated. I have simplified the code for ease of understanding. I`m just trying to paint a rectangle in the doGraph() for simplicity.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.RenderingHints.Key;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
#SuppressWarnings("serial")
public class UIGraph extends JPanel {
private double minX, maxX;
private double minY, maxY;
private SWPaint swPaint; //Swing Worker object
private Graphics2D g2D;
public Key key = RenderingHints.KEY_ANTIALIASING;
public Object obj = RenderingHints.VALUE_ANTIALIAS_ON;
private int labelPadding = 25;
private int padding = 25;
private int padConst = (2 * padding) - labelPadding;
private double xScale;
private double yScale;
/**
* Crate Panel
*/
private void createUI() {
setBackground(new Color(245, 255, 250));
setLayout(new BorderLayout(0, 0));
}
/**
* Constructor
*
*/
public UIGraph() {
createUI();
getMinMax();
}
/**
* Paint Component. Do all the calculations and graphing
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (swPaint != null) {
if (!swPaint.isDone()) { // The swing worker is busy with tasks. Set Visibility to false and return.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setVisible(false);
}
});
return;
}
}
swPaint = new SWPaint(g); //Create a swing worker class, pass graphics object as argument
swPaint.execute();
}
}
/**
* Get the Min and Max values of Data
*/
private void getMinMax() {
//obtain min and max values
}
/**
* Main method for graphing
*
* #param g2D
*/
private void doGraph() {
xScale = (double) (getWidth() - padConst) / (maxX - minX);
yScale = (double) (getHeight() - padConst) / (maxY - minY);
g2D.setColor(Color.WHITE);
g2D.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding, getHeight() - 2 * padding
- labelPadding);
g2D.setColor(Color.BLACK);
}
/**
* Swing Worker to handle Paint
*
*/
public class SWPaint extends SwingWorker<Void, Void> {
/**
* Constructor
*
* #param g
*/
public SWPaint(Graphics g) {
g2D = (Graphics2D) g;
g2D.setRenderingHint(key, obj);
//doGraph() //This works
}
/**
* Do graphing calculations here
*/
#Override
protected Void doInBackground() throws Exception {
// do all calculations here
return null;
}
/*
* The SW is done. Paint the graph. Set Visibility to true
*/
protected void done() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
doGraph(); //This doesn`t work here.
setVisible(true);
}
});
}
}
}

Categories