I'm a java novice, and by far my weakest point in java is using graphics. I'm trying to make a retirement calculator that allows a user to input several pieces of data into an application through JTextFields and then press a button to calculate an account balance at each year (i.e. create an array of the balances at the end of each year). However, I want to display this information using a bar graph representing these balances, so the bar graph must draw its rectangles using the numbers in the array. However, since paintComponent is called at the beginning of the program and the values of the array aren't initialized until the press of the JButton, I can't have the paintComponent method refer to those values, otherwise I get a nullPointerException upon running and things don't render properly. Here's my code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Calculator extends JPanel
{
static double start;
static double age;
static double retirement;
static double apr;
static double inflation;
static double daily;
static double life;
static double income;
static int yearsToRetirement = (int)(retirement - age);
static int yearsPostRetirement = (int)(life - retirement);
static double real = (1.0 + (apr / 100.0))/(1.0 + (inflation/100.0)) - 1.0;
static double[] balance;
static double[] yearEndBalance;
static double[] balance2;
public Calculator()
{
setLayout(new BorderLayout());
JPanel subpanel = new JPanel();
add(subpanel, BorderLayout.LINE_END);
subpanel.setBackground(Color.GRAY);
subpanel.setLayout(new GridLayout(9, 1));
// Daily savings
JPanel pnlDailySavings = new JPanel();
JLabel lblDailySavings = new JLabel("Daily savings amount:");
final JTextField txtDailySavings = new JTextField(10);
pnlDailySavings.add(lblDailySavings);
pnlDailySavings.add(txtDailySavings);
subpanel.add(pnlDailySavings);
// Age
JPanel pnlAge = new JPanel();
JLabel lblAge = new JLabel(" Current age:");
final JTextField txtAge = new JTextField(10);
pnlAge.add(lblAge);
pnlAge.add(txtAge);
subpanel.add(pnlAge);
// Starting amount of savings
JPanel pnlStart = new JPanel();
JLabel lblStart = new JLabel("Starting amount of savings:");
final JTextField txtStart = new JTextField(10);
pnlStart.add(lblStart);
pnlStart.add(txtStart);
subpanel.add(pnlStart);
// Age of retirement
JPanel pnlRetirement = new JPanel();
JLabel lblRetirement = new JLabel("Expected age of retirement:");
final JTextField txtRetirement = new JTextField(10);
pnlRetirement.add(lblRetirement);
pnlRetirement.add(txtRetirement);
subpanel.add(pnlRetirement);
// Annual retirement income
JPanel pnlIncome = new JPanel();
JLabel lblIncome = new JLabel("Annual retirement income:");
final JTextField txtIncome = new JTextField(10);
pnlIncome.add(lblIncome);
pnlIncome.add(txtIncome);
subpanel.add(pnlIncome);
// Life expectancy
JPanel pnlLife = new JPanel();
JLabel lblLife = new JLabel("Life expectancy:");
final JTextField txtLife = new JTextField(10);
pnlLife.add(lblLife);
pnlLife.add(txtLife);
subpanel.add(pnlLife);
// Estimated rate of return on savings
JPanel pnlReturn = new JPanel();
JLabel lblReturn = new JLabel("Estimated rate of return on savings:");
final JTextField txtReturn = new JTextField(10);
pnlReturn.add(lblReturn);
pnlReturn.add(txtReturn);
subpanel.add(pnlReturn);
// Estimated rate of inflation
JPanel pnlInflation = new JPanel();
JLabel lblInflation = new JLabel("Estimated rate of inflation:");
final JTextField txtInflation = new JTextField(10);
pnlInflation.add(lblInflation);
pnlInflation.add(txtInflation);
subpanel.add(pnlInflation);
JButton btnCalculate = new JButton("Calculate your retirement savings");
btnCalculate.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
daily = Double.parseDouble(txtDailySavings.getText());
age = Double.parseDouble(txtAge.getText());
start = Double.parseDouble(txtStart.getText());
retirement = Double.parseDouble(txtRetirement.getText());
income = Double.parseDouble(txtIncome.getText());
life = Double.parseDouble(txtLife.getText());
apr = Double.parseDouble(txtReturn.getText());
inflation = Double.parseDouble(txtInflation.getText());
System.out.printf("%f%n%f%n%f%n%f%n%f%n%f%n%f%n%f%n", daily, age, start, retirement, income, life, apr, inflation);
balance = new double[365 * yearsToRetirement];
yearEndBalance = new double[yearsToRetirement];
balance2 = new double[yearsPostRetirement];
double total = start;
for (int i = 0; i < balance.length; i++)
{
total = total * (1 + real/365.0) + daily;
balance[i] = total;
}
for (int i = 0; i < balance2.length; i++)
{
total = total * Math.pow((1 + real/365.0), 365) - income;
balance2[i] = total;
}
for (int i = 0; i < yearEndBalance.length; i++)
{
yearEndBalance[i] = balance[365 * i];
}
printArray(yearEndBalance);
printArray(balance2);
printArray(balance);
repaint();
}
});
subpanel.add(btnCalculate);
}
public static void printArray(double[] array)
{
for (int i = 0; i < array.length; i++)
{
System.out.println(array[i]);
}
}
public void paintComponent(Graphics g)
{
int width = this.getWidth();
int height = this.getHeight();
super.paintComponent(g);
g.setColor(Color.BLACK);
g.drawLine((int)(width - 0.9 * width), (int)(height - 0.1 * height), (int)(width - 0.1 * width), (int)(height - 0.1 * height));
for (int i = 0; i < balance2.length; i++)
{
g.fillRect(((int)(width - 0.9 * width) + 5 * i), ((int)(height - 0.1 * height) - 5 * i), 5, 5 * i);
}
}
}
This code is obviously a work in progress, but right now my biggest hurdle is facilitating the communication between the arrays created upon JButton and creation of a bar graph. Can anyone help?
Five things...
Only paint the graph if the balance2 is not null and is not empty (length > 0)
Consider using paintComponent over paint, see Performing Custom Painting for more details.
Also call super.paint (or super.paintComponent if you've overridden paintComponent) to preserve the paint chain. See Painting in AWT and Swing for more details (nb: You are calling super.paintComponent from within paint, this is bad idea and could affect updates to the GUI)
Paint your graph to a separate, dedicated component, otherwise you will be painting underneath the other components
Call repaint on the bar component when you want to repaint it's content
For example..
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int width = this.getWidth();
int height = this.getHeight();
if (balance2 != null && balance2.length > 0) {
g.setColor(Color.BLACK);
g.drawLine((int)(width - 0.9 * width), (int)(height - 0.1 * height), (int)(width - 0.1 * width), (int)(height - 0.1 * height));
for (int i = 0; i < balance2.length; i++)
{
g.fillRect(((int)(width - 0.9 * width) + 5 * i), ((int)(height - 0.1 * height) - 5 * i), 5, 5 * i);
}
}
}
Related
I'm relatively new in Java and I have a task to realize the rotation of a figure drawn from random points. The realization has to be written by means of "naked logic", so AffineTransform tools is not an option in this case. And I got stuck in a moment where I suppose to translate shape to origin in order to perform rotation. I racked my brains, surfed on the internet but couldn't succeed with the solution. There is already one topic with a similar issue, tho there it was about rectangles translation, so it hasn't worked for me. Maybe someone could help with this.
public class Frame extends JFrame {
private JButton polyBtn = new JButton("Draw/Redraw Poly");
private JButton rotationBtn = new JButton("Rotate");
private JSpinner polySpn = new JSpinner(new SpinnerNumberModel(3,3,10,1));
private JSpinner angleSpn = new JSpinner(new SpinnerNumberModel(0,0,360,1));
private JPanel panel = new JPanel();
private JPanel controlPanel = new JPanel();
private JLabel polyLbl = new JLabel("Choose number of corners");
private JLabel angleLbl = new JLabel("Choose angle of rotation");
private int cornerAmount , degree ;
private boolean figureIsDrawed = false;
Random r = new Random();
Polygon poly = new Polygon();
Polygon editedPoly = new Polygon();
public Frame() {
super("Rotate Object");
setBounds(100, 100, 500, 250);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = this.getContentPane();
container.setLayout(new BorderLayout());
setVisible(true);
setLocationRelativeTo(null);
setResizable(false);
polyBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cornerAmount = (int) polySpn.getValue();
drawPoly(cornerAmount);
}});
rotationBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
degree = (int) angleSpn.getValue();
rotateShape(degree);
}});
panel.setPreferredSize(new Dimension(350,250));
panel.setBackground(Color.ORANGE);
panel.setBorder(BorderFactory.createLineBorder(Color.black,3));
controlPanel.setBorder(BorderFactory.createLineBorder(Color.BLUE, 2));
controlPanel.setLayout(new GridLayout(6,0,0,0));
polyLbl.setFont(polyLbl.getFont().deriveFont(10.0f));
angleLbl.setFont(angleLbl.getFont().deriveFont(10.0f));
controlPanel.add(polyLbl);
controlPanel.add(polySpn);
controlPanel.add(polyBtn);
controlPanel.add(angleLbl);
controlPanel.add(angleSpn);
controlPanel.add(rotationBtn);
container.add(controlPanel,BorderLayout.CENTER);
container.add(panel,BorderLayout.EAST);
}
private void drawPoly(int corners) {
Graphics g = panel.getGraphics();
int panelWidth = panel.getWidth();
int panelHeight = panel.getHeight();
if(figureIsDrawed) {
poly.reset();
repaint();
figureIsDrawed = false;
}else {
poly.addPoint(panelWidth/2, panelHeight/2);
System.out.println(poly.xpoints[0]);
System.out.println(poly.ypoints[0]);
for(int i = 0 ; i < corners-1 ; i++) {
poly.addPoint(r.nextInt(panelWidth), r.nextInt(panelHeight));
}
g.drawPolygon(poly);
figureIsDrawed = true;
}
}
private void rotateShape(int degree) {
Graphics g = panel.getGraphics();
int[] xCord = poly.xpoints;
int[] yCord = poly.ypoints;
double rads = degree * (Math.PI/180);
double sin = Math.sin(rads);
double cos = Math.cos(rads);
double[][] transform = new double[3][3];
int panelW = this.getWidth();
int panelH = this.getHeight();
transform[0][0] = cos;
transform[0][1] = -sin;
transform[1][0] = sin;
transform[1][1] = cos;
double[] transformedX = new double[xCord.length];
double[] transformedY = new double[yCord.length];
int[] updatedX = new int[xCord.length];
int[] updatedY = new int[yCord.length];
for(int i = 0;i<updatedX.length;i++) {
updatedX[i] = xCord[i];
updatedY[i] = yCord[i];
transformedX[i] = Math.round(updatedX[i] * transform[0][0] + updatedY[i] * transform[0][1] + transform[0][2]);
transformedY[i] = Math.round( updatedX[i] * transform[1][0] + updatedY[i] * transform[1][1] + transform[1][2]);
updatedX[i] = (int)transformedX[i];
updatedY[i] = (int)transformedY[i];
}
g.setColor(Color.orange);
g.fillRect(0, 0, panelW, panelH);
g.setColor(Color.black);
g.drawPolygon(updatedX, updatedY, updatedX.length);
}
}
The main problem is in "RotateShape" method.I just can't figure out,how to make it work right.
Your rotation code is correct. But you are trying to transform more points than there are in the polygon. The internal xpoints and ypoints arrays are not necessarily filled completely. Replace all occurences of xCord.length and yCord.length with poly.npoints and you are good to go.
Basically, my program intakes a sales value and it is updated. The program is also supposed to update how much percent out off 100 that this product makes up of the total sales of all the products combined. For example if i input 100 sale for carrots then the % of all sale should be 100% since this is the only product which has sold and there is only a 100 sale. When you add maybe 100 sales to potatos, the % would change to 50% for each as there is 200 sales split between two. How would i code this to happen. My attempted code is attached below, any further questions please comment below.
The problem is in the calculatepercentage method and the statement in the updatesalesaction method which starts with percentage. The percentage is not updating when i add sales to a specific product
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
This is an initial starter version of a Java application that should be
extended in stages, eventually allowing the user to enter product sales figures
for a number of products, and display them in a table with %ages, a ranking and a pie chart.
This starter code just has storage for one product, and allows that product's sales figure
to be updated and displayed. The display is plain, with no font changes and
no border lines for the table. All the processing is limited to just this one product,
and must be adapted for a whole array of products.
SBJ March 2016
*/
public class ProductChart extends JFrame implements ActionListener
{
/**
* Frame constants
*/
private static final int FRAME_LOCATION_X = 350;
private static final int FRAME_LOCATION_Y = 250;
private static final int FRAME_WIDTH = 650;
private static final int FRAME_HEIGHT = 400;
/**
* The maximum permitted number of products
*/
private final int MAX_PRODUCTS = 10;
/**
* These arrays holds all the sales data:
* Element 0 is unused, so array sizes are MAX_PRODUCTS+1.
* The product number (from 1 to MAX_PRODUCTS) is the index in the arrays
* where the product's data is held.
* Sales figures are counted quantities, so int.
*/
private String[] productName; // The name of each product
private int[] productSales; // The number of sales of each product
private float[] percentage; // The proportion of total sales for each product
private int totalSales; // Always the current total sales
/**
* Display area layout constants
*/
private final int DISPLAY_WIDTH = 600;
private final int DISPLAY_HEIGHT = 300;
private final int PRODUCT_X = 30; // Start of product number column
private final int NAME_X = 75; // Start of product name column
private final int SALES_X = 225; // Start of sales column
private final int PERCENTAGE_X = 300; //Start of percentage column
private final int TABLE_LINES_Y = 12; //The number of horizontal lines required to draw the table
private final int TABLE_LINES_X = 5; //The number of vertical ""
/**
* The main launcher method:
* Configure the applications's window, initialize the sales data structures,
* and make the applications visible.
* #param args Unused
*/
public static void main(String[] args)
{
ProductChart frame = new ProductChart();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(FRAME_LOCATION_X, FRAME_LOCATION_Y);
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.initializeSalesData();
frame.createGUI();
frame.setVisible(true);
frame.setTitle("Product Chart 2312931");
}
/**
* The GUI components
*/
private JTextField productSalesEntryField; // For entry of new product sales figures
private JButton updateSalesButton; // To request update of a sales figure
private JPanel displayArea; // Graphics area for drawing the sales table
private JTextField productRefEntryField; // For entry of product refence number when updating sales figures
/**
* Helper method to build the GUI
*/
private void createGUI()
{
// Standard window set up
Container window = getContentPane();
window.setLayout(new FlowLayout());
window.setBackground(Color.lightGray);
// Product reference entry label and text field
JLabel productRefEntryLabel = new JLabel("Product Ref #:");
productRefEntryField = new JTextField(2);
window.add(productRefEntryLabel);
window.add(productRefEntryField);
// Product sales entry label and text field
JLabel productSalesEntryLabel = new JLabel("Product sales:");
productSalesEntryField = new JTextField(5);
window.add(productSalesEntryLabel);
window.add(productSalesEntryField);
// Button to add new sales figure
updateSalesButton = new JButton("Update sales");
updateSalesButton.addActionListener(this);
window.add(updateSalesButton);
// The drawing area for displaying all data
displayArea = new JPanel()
{
// paintComponent is called automatically when a screen refresh is needed
public void paintComponent(Graphics g)
{
// g is a cleared panel area
super.paintComponent(g); // Paint the panel's background
paintScreen(g); // Then the required graphics
}
};
displayArea.setPreferredSize(new Dimension(DISPLAY_WIDTH, DISPLAY_HEIGHT));
displayArea.setBackground(Color.white);
window.add(displayArea);
}
/**
* Initializes product arrays:
* A set of product names,
* With sales initially 0.
*
* Note: In the arrays, the first element is unused, so Bread is at index 1
*/
private void initializeSalesData()
{
productName = new String[MAX_PRODUCTS+1];
productName[1] = "Bread"; // Note: First element is unused, so Bread is at index 1
productName[2] = "Milk";
productName[3] = "Eggs";
productName[4] = "Cheese";
productName[5] = "Cream";
productName[6] = "Butter";
productName[7] = "Jam";
productName[8] = "Chocolate Spread";
productName[9] = "Corn Flakes";
productName[10] = "Sugar";
productSales = new int[MAX_PRODUCTS+1];
for(int i=0; i< MAX_PRODUCTS; i++)
{
productSales[i] = 0;
}
percentage = new float[MAX_PRODUCTS+1];
for(int i=0; i< MAX_PRODUCTS; i++)
{
percentage[i] = 0;
}
}
/**
* Event handler for button clicks.
*
* One action so far:
* o Update the sales figure for a product
*/
public void actionPerformed(ActionEvent event)
{
if (event.getSource() == updateSalesButton)
updateSalesAction();
// And refresh the display
repaint();
}
/**
* Action updating a sales figure: the new sales figure is fetched
* from a text fields, parsed (converted to an int), and action is taken.
*/
private void updateSalesAction()
{
// Fetch the new sales details
int productRef = Integer.parseInt(productRefEntryField.getText());
int newSales = Integer.parseInt(productSalesEntryField.getText());
if (productRef>0&&productRef<=MAX_PRODUCTS)
{
// Update the sales tables
productSales[productRef] = newSales;
totalSales = totalSales + newSales;
percentage[productRef] = calculatePercentage(productSales[productRef-1], totalSales);
}
}
/**
* Redraw all the sales data on the given graphics area
*/
public void paintScreen(Graphics g)
{
// Draw a table of the sales data, with columns for the product number,
// product name, and sales
//Heading
g.setFont(new Font("default", Font.BOLD, 16));
g.drawString("Product sales data:", 20, 20);
// Table column headers
g.setFont(new Font("default", Font.BOLD, 12));
g.drawString("No", PRODUCT_X, 60);
g.drawString("Name", NAME_X, 60);
g.drawString("Sales", SALES_X, 60);
g.drawString("Percentage", PERCENTAGE_X, 60);
// The table of sales data
g.setFont(new Font("default", Font.PLAIN, 12));
int y = 80;// The y coordinate for each line
int yIncrement = 20;
for(int i = 0; i<productName.length-1; i++)
{
g.drawString(""+Integer.toString(i+1), PRODUCT_X, y+i*yIncrement); // First column: product number
}
for(int i = 0; i<productName.length-1; i++)
{
g.drawString(productName[i+1], NAME_X, y+i*yIncrement); // Second column: product name
}
for(int i = 0; i<productSales.length-1; i++)
{
g.drawString(Integer.toString(productSales[i+1]), SALES_X, y+i*yIncrement); // Third column: sales figure
}
for(int i = 0; i<percentage.length-1; i++)
{
g.drawString(Float.toString(percentage[i+1]), PERCENTAGE_X, y+i*yIncrement);
}
for(int i = 0; i<TABLE_LINES_Y ; i++)
{
//g.drawLine(
}
}
public float calculatePercentage(int sales, int totalSales)
{
float percentage = sales/totalSales;
return percentage;
}
}
First, your percentage calculation is suffering from integer division, therefore it always gets rounded off to the nearest whole number below it. Use this to calculate your percentage (also, multiply by 100 so that it looks more like a percentage):
public float calculatePercentage(int sales, int totalSales)
{
return 100.0f * sales / totalSales;
}
Second, you need to loop through all of the products with each update so that all of the percentages will be accurate & so that the totalSales count will be accurate.
private void updateSalesAction()
{
// Fetch the new sales details
int productRef = Integer.parseInt(productRefEntryField.getText());
int newSales = Integer.parseInt(productSalesEntryField.getText());
if (productRef>0&&productRef<=MAX_PRODUCTS)
{
// Update the sales tables
productSales[productRef] = newSales;
totalSales = 0;
for (int i = 1; i <= MAX_PRODUCTS; i++)
totalSales += productSales[i];
for (int i = 1; i <= MAX_PRODUCTS; i++)
percentage[i] = calculatePercentage(productSales[i], totalSales);
}
}
I tested it, and these updates fix the program.
I've been working on a dots look-a-like, but I'm having trouble painting the dots on the board. The array seems to work but it doesn't want to paint it.
Sorry, some of my variables are in Dutch but that shouldn't really pose too many confusion.
public class Bord extends JFrame{
Slang slang = new Slang();
Tile[][] tile = new Tile[6][6];
private JPanel menuPanel;
private JPanel gridPanel;
private JLabel levelTitel;
private JLabel levelNummer;
private JLabel scoreTitel;
private JLabel scoreNummer;
private JLabel targetTitel;
private JLabel targetNummer;
private JLabel timeTitel;
private JLabel timeNummer;
private JLabel pauzeKnop;
public Bord() {
super("Dots");
//setLocationRelativeTo(this);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
maakAttributen();
maakListeners();
maakLayout();
pack();
setSize(650, 750);
setVisible(true);
getContentPane();
repaint();
//TODO automatic size
}
public void maakAttributen() {
levelTitel = new JLabel("level");
levelNummer = new JLabel("1");
scoreTitel = new JLabel("score");
scoreNummer = new JLabel("2");
targetTitel = new JLabel("target");
targetNummer = new JLabel("3");
timeTitel = new JLabel("time");
timeNummer = new JLabel("4");
//TODO image in knop zetten
pauzeKnop = new JLabel("PAUZE");
}
public void maakListeners() {
}
public void maakLayout() {
JPanel menuPanel = new JPanel(new GridLayout(0, 5, 5, 5));
JPanel gridPanel = new JPanel(new GridLayout(0, 7, 5, 5));
add(menuPanel, BorderLayout.PAGE_START);
add(gridPanel, BorderLayout.CENTER);
//menu attributen aan menu toevoegen
menuPanel.add(levelTitel);
menuPanel.add(scoreTitel);
menuPanel.add(targetTitel);
menuPanel.add(timeTitel);
menuPanel.add(pauzeKnop);
menuPanel.add(levelNummer);
menuPanel.add(scoreNummer);
menuPanel.add(targetNummer);
menuPanel.add(timeNummer);
//grid met dots toevoegen
for (int x = 0; x < 6; x++) {
for (int y = 0; y < 6; y++) {
RandomKleur kleur = new RandomKleur();
tile[x][y] = new Tile(kleur.getKleur());
gridPanel.add(new myDots());
}
}
}
private class myDots extends JPanel {
#Override
protected void paintComponent(Graphics g) {
int h = getHeight();
int w = getWidth();
super.paintComponent(g);
for (int x = 0; x < 6; x++) {
for (int y =0; y < 6; y++) {
g.setColor(tile[x][y].getKleur());
g.fillOval(h / 2, w / 2, 33, 33);
}
}
}
}
}
I've tried debugging it and it gives some kind of null pointer exception sometimes.
You basic painting logic is wrong. You are adding 36 Dot panels to the frame, but then in the painting logic you are repainting all 36 dots on top of one another so only the last dot painted will display. The paintComponent() method should only be painting a single dot.
You need to change your MyDots class to accept a Tile as a parameter and then save the Tile object as an instance variable of the class. Then the code would be something like:
for (int x = 0; x < 6; x++) {
for (int y = 0; y < 6; y++) {
RandomKleur kleur = new RandomKleur();
tile[x][y] = new Tile(kleur.getKleur());
//gridPanel.add(new myDots());
gridPanel.add(new myDots(tile[x][y]));
}
}
I don't know if you even need the tile array, because now the MyDots class has the tile information.
Then Your painting logic should be something like:
//for (int x = 0; x < 6; x++) {
//for (int y =0; y < 6; y++) {
//g.setColor(tile[x][y].getKleur());
g.setColor(tile.getKleur()); // where "tile" is the instance variable
g.fillOval(h / 2, w / 2, 33, 33);
//}
//}
Who knows why you get the NPE, because the exception is not related to the code you posted.
By the way class name should start with an upper case character. It should be "MyDot".
Edit:
Do I have to create a new method in MyDots?
You need to create a constructor for your class:
public class MyDots
{
private Tile tile;
public MyDots(Tile tile)
{
this.tile = tile;
}
#Override
protectect paintComponent(Graphics g)
...
}
I created a simple "elevator" program (it's still in the beginning stages) that goes up 1 floor when I click UP and vice versa.
I messed up pretty badly when I drew all my components into JFrame, and as expected, it flickers every time I click the button (repaints). I know the solution to be draw in the JPanel and put the said panel in the JFrame, but I have a problem translating my JFrame components into JPanel. I've tried extending JPanel, creating a JFrame object and then overriding the paintComponent() method and doing my drawing there, but when I compile it does not draw it at all. It only creates the frame.
Can anyone help me or give me tips on how to proceed "transferring" my programming from JFrame based to JPanel based? Thank you in advance!
My code is below:
import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.Timer;
import java.math.*;
public class MyCanvas extends JFrame {
private int up = 0;
private int down = 0;
private int movefloorup = 0;
private int buildingType;//type of building (1 = Residential, 2 = Commercial)
private int totnumoffloors; //for the total number of floors
private int numofelevators; //for the number of elevators to be generated
private int floorlimit = 0; //to determine up until where the elevator will be moving
private int currenttime; //determine the time of the day the elevator is operating (1 = Morning, 2 = Lunch, 3 = Afternooon)
//For elevator resetting to bottom
private int rectX = 190;
private int switchmarker = 0;
//Lines and stuff
private int horizborder = 0;
private int bordercount = 0;
private class UpAction implements ActionListener //move the elevator up
{
public void actionPerformed(ActionEvent e)
{
if(movefloorup<780){
repaint();
up++;
movefloorup = movefloorup + 130;
//repaint();
}
else
{
switchmarker = 1;
movefloorup = 0;
repaint();
}
}
}
private class DownAction implements ActionListener //move the elevator down
{
public void actionPerformed(ActionEvent e)
{
if(movefloorup>0){
repaint();
down++;
movefloorup = movefloorup - 130;
//repaint();
}
else
{
switchmarker = 0;
movefloorup = 780;
repaint();
}
}
}
public MyCanvas(int buildingType, int totnumoffloors, int numofelevators, int currenttime){
this.buildingType = buildingType;
this.totnumoffloors = totnumoffloors;
this.numofelevators = numofelevators;
this.currenttime = currenttime;
String title;
if(this.buildingType == 1)
{
title = "Residential Building";
}
else
{
title = "Commercial Building";
}
setLayout(null);
horizborder = 500*((int)Math.ceil((double)totnumoffloors/7)); //calculating how wide the window should be
bordercount = ((int)Math.ceil((double)totnumoffloors/7)); //counts how many borders there will be
//NOTES
//A floor is 130 units in the Y-Direction
//Drawing the bulding layout
if(totnumoffloors>7)
{
setSize(horizborder, 1000);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(title);
setLayout(new BorderLayout());
getContentPane().setBackground(Color.WHITE);
}
else{
setSize(500, 1000); //suitable for 7 floors
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(title);
setLayout(new BorderLayout());
getContentPane().setBackground(Color.WHITE);
}
JButton upButton = new JButton("UP");
upButton.addActionListener(new UpAction());
add(upButton, BorderLayout.NORTH);
JButton downButton = new JButton("DOWN");
//downButton.setBounds(0, 0, 220, 30);
//downButton.setLocation(100, 100);
downButton.addActionListener(new DownAction());
add(downButton, BorderLayout.SOUTH);
}
public void paint(Graphics graphics){ //this is where you draw shit
super.paint(graphics);
//Floors
graphics.setColor(Color.RED);
int numoffloorsY = 830;
int numoffloorsX = 830;
int floorbeginning = 0;
int floorcounter = 1;
int floorflag = 0;
int rightedge = 500;
if(this.totnumoffloors>7) //drawing the floors
{
//Default number of floors -> 7
for(int i = 0;i<totnumoffloors;i++)
{
graphics.setColor(Color.RED);
graphics.drawLine(floorbeginning,numoffloorsX,rightedge,numoffloorsY); //FLOORS
graphics.setColor(Color.DARK_GRAY);
graphics.setFont(new Font("TimesRoman", Font.PLAIN, 15));
graphics.drawString(" "+floorcounter, floorbeginning+10, numoffloorsY+20); //SAVE THIS FOR DRAWING FLOORS
numoffloorsY = numoffloorsY - 130;
numoffloorsX = numoffloorsX - 130;
floorcounter++;
floorflag++;
if(floorflag==7)
{
floorbeginning = floorbeginning + 500;
rightedge = rightedge+500;
numoffloorsY = 830;
numoffloorsX = 830;
floorflag = 0;
}
}
//Every other floor past 7 will be added here.
/*for(int i = 0;i<totnumoffloors-7;i++)
{
//System.out.println("LOLOOLO");
graphics.setColor(Color.RED);
graphics.drawLine(floorbeginning,numoffloorsX,horizborder,numoffloorsY);
graphics.setColor(Color.DARK_GRAY);
graphics.setFont(new Font("TimesRoman", Font.PLAIN, 15));
graphics.drawString(" "+floorcounter, floorbeginning, numoffloorsY+20);
//graphics.setColor(Color.DARK_GRAY);
//graphics.drawLine(500,0,500,1000);
floorcounter++;
numoffloorsY = numoffloorsY - 130;
numoffloorsX = numoffloorsX - 130;
}*/
//DIVIDING LINE -> to determine the first 7 floors from the ones higher up.
for(int i=0;i<bordercount;i++)
{
graphics.setColor(Color.DARK_GRAY);
graphics.drawLine(500*i,0,500*i,1000);
}
}
else{
for(int i = 0;i<this.totnumoffloors;i++)
{
graphics.setColor(Color.RED);
graphics.drawLine(0,numoffloorsX,500,numoffloorsY);
graphics.setColor(Color.DARK_GRAY);
graphics.setFont(new Font("TimesRoman", Font.PLAIN, 15));
graphics.drawString(" "+floorcounter, floorbeginning+10, numoffloorsY+20); //SAVE THIS FOR DRAWING FLOOR
numoffloorsY = numoffloorsY - 130;
numoffloorsX = numoffloorsX - 130;
floorcounter++;
}
}
//Drawing the elevators
if(up>0 && movefloorup<1000){
graphics.setColor(Color.GRAY);
if(switchmarker==1)
{
System.out.println("ELSA");
rectX = 690;
//rectX = rectX + 190;
}
else
{
rectX = 190;
}
System.out.println(rectX);
graphics.fillRect(rectX, 850-movefloorup, 100, 100); //this needs to match the stats of the rectangle to fill it properly
graphics.drawRect(rectX, 850-movefloorup, 100, 100);
//Line for the door
graphics.setColor(Color.BLACK);
graphics.drawLine(rectX+50, 850-movefloorup, rectX+50, 950-movefloorup); //match the y-coordinate for the rectangle, add 100 for the y-coordinate of the other end
System.out.println(movefloorup);
System.out.println(switchmarker);
//drawLine(x1, y1, x2, y2); --From (x1,y1) to (x2,y2)
}
else if(down>0 && movefloorup>0)
{
graphics.setColor(Color.GRAY);
if(switchmarker==1) //This determines when the elevator should move to the next column of higher floors.
{
System.out.println("ELSA");
rectX = 500;
}
System.out.println(rectX);
graphics.fillRect(rectX, 850-movefloorup, 100, 100); //this needs to match the stats of the rectangle to fill it properly
//graphics.drawRect(190, 850 + movefloorup, 100, 100); //FIRST FLOOR
graphics.drawRect(rectX, 850-movefloorup, 100, 100); //SECOND FLOOR (135 units difference in Y-axis between floors)
//x-coordinate, y-coordinate, width, height
//Line for the door
graphics.setColor(Color.BLACK);
graphics.drawLine(rectX+50, 850-movefloorup, rectX+50, 950-movefloorup); //match the y-coordinate for the rectangle, add 100 for the y-coordinate of the other end
System.out.println(movefloorup);
System.out.println(switchmarker);
}
else
{
graphics.setColor(Color.GRAY);
graphics.fillRect(190, 850, 100, 100); //this needs to match the stats of the rectangle to fill it properly
graphics.drawRect(190, 850, 100, 100); //FIRST FLOOR
graphics.drawRect(190, 850, 100, 100); //SECOND FLOOR (135 units difference in Y-axis between floors)
//x-coordinate, y-coordinate, width, height
//Line for the door
graphics.setColor(Color.BLACK);
graphics.drawLine(240, 850, 240, 950); //match the y-coordinate for the rectangle, add 100 for the y-coordinate of the other end
//System.out.println("In else!");
}
}
}
The main class just gets input from the user, such as the number of floors, time of day, etc.
This is going to be a little messy.
Start by creating a custom component that extends from JPanel (I'll call it ElevatorPane).
Take the contents of the current paint method and place them within this components paintComponent method. This will involve moving the instance variables that the paintComponent method will need including, totnumoffloors, bordercount, up, down, movefloorup, switchmarker, rectX
This is where it gets a little messy...
You need to take the contents of your ActionListeners and translate these into methods within the ElevatorPane, this way you expose the functionality without exposing the details...
Create a constructor within ElevatorPane that takes the number of floors.
Override the getPrefferedSize method of ElevatorPane and return the size that the component needs to be to satisfy your needs...
Create an instance field of ElevatorPane in MyCanvas, instantiate it and add it to the frame.
Clean, build, run...
I have an assignment in which I have to allow a user to plot a graph using a quadratic equation.
I managed to draw the skeleton of the graph, and now I am trying to display the "control panel" for the user to input the values.
I have 4 files:
graph.java
panel.java
panelB.java
panelC.java
My problem is when I run the code it is displaying only the panel.java even in the container where it should display the other two panels.
panel.java
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.JPanel;
public class panel extends JPanel {
public panel(){
this.setBackground(Color.yellow);
}
}
Can anyone please advise what changes I should do to fix this problem?
I have done some changes to the graph.java file:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import javax.swing.*;
public class GraphApplet extends JApplet{
public GraphApplet(){
raph p = new Graph();//graph
p.setPreferredSize(new Dimension(760,500));
conn.add(p,BorderLayout.CENTER);
}
And now all that is being displayed is the graph.
Regarding the other code, I also made some changes to the class names:
gnjk;.java
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.JPanel;
public class Graph extends JPanel {
public Graph(){
this.setBackground(Color.yellow);
}
public void paintComponent(Graphics p) {
super.paintComponent(p);
Graphics2D graph = (Graphics2D)p;
this.setBackground(Color.yellow);//set background color.
int x,y,y1,x1,a,b,p1x,p1y,p2x,p2y;
int xstart = 7;
int ystart = 1;
int xfinish = 3;
.......
bhfvhn.java
import javax.swing.*;
import java.awt.*;
import javax.swing.JPanel;
public class ControlsA extends JPanel{
public void init (Box g) {
a = Box.createVerticalBox();
a.add(new JLabel("Please enter the values below:"));
a.add(new JLabel("h"));
}
}
jknmk.java
import javax.swing.*;
import java.awt.Component;
import java.awt.Dimension;
public class ControlsB extends JPanel{
public void init (Box b) {
b = Box.createHorizontalBox();
b.add(new JLabel("a"));
JTextField f1 = new JTextField("0.0");
f1.setMaximumSize(new Dimension(100,30));
b.add(f1);
}
}
Here is an updated to my project:
jkl.java
import java.awt.BorderLayout;
import java.awt.Container;
public class GraphApplet extends JApplet{
public GraphApplet() {
public void init(){
SwingUtilities.invokeLater(new Runnable() {
public void run(){
Container conn = getContentPane();
conn.setLayout(new BorderLayout());
Graph z = new Graph();
conn.add(p,BorderLayout.CENTER);
fasfae a = new ControlsA(box1);
conn.add(a,BorderLayout.LINE_START);
adsfawef b = new ControlsB(box2);
conn.add(b,BorderLayout.PAGE_END);
}
});
}
}
/*Container conn = getContentPane();
conn.setLayout(new BorderLayout());
Graph p = new Graph();//graph
p.setPreferredSize(new Dimension(460,560));
conn.add(p,BorderLayout.CENTER);
Box a = new Box(BoxLayout.Y_AXIS);
a.setPreferredSize(new Dimension(50,50));
conn.add(a,BorderLayout.EAST);
Box b = new Box(BoxLayout.X_AXIS);
b.setPreferredSize(new Dimension(201,50));
conn.add(b,BorderLayout.SOUTH);*/
//this code is commented not to loose it
vtk.java
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.JPanel;
class Graph extends JPanel {
public Graph(){
this.setBackground(Color.yellow);
}
#Override
public Dimension getPreferredSize(){return (new Dimension(460,560)); }
public void paint(Graphics z) {
Graphics graph = (Graphics2D)z;
this.setBackground(Color.yellow).
int x,y,y1,x1,a,b,p1x,p1y,p2x,p2y;
//line co-ordinates
//the numbers represent the number of boxes on the graph
int xstart = 7;
int ystart = 1;
int x = 3;
int y = 9;
//other variables
int i = 0;
int i2 = 0;
int m = 0;
int n = 0;
int m2 = 0;
int n2 = 0;
int f2 = 0;
int g2 = 1;
//ranges
int f = 5;
int g = -5;
//change -ve num to +ve
int g3 = Math.abs(g);
int a1 = g3 + f;
int b1 = a1;
a = (Height);
f = (Width);
}
}
}
// 6 variables the user has to input
}
#Override
public Dimension getPreferredSize() {return (new Dimension(200,100));}
}
nllkl.java
#Override
public Dimension getPreferredSize(){return (new Dimension(201,50));}
}
Still no improvement. I cannot understand what is going on.
The main problems in that code are:
The applet is adding 3 instances of panel as opposed to one each of panel, panelB & panelC.
Neither panelB & panelC ever adds the Box to the panel, so it will not appear.
You intimate in the code that panelB should be vertically aligned, which means it would better fit in the LINE_START (WEST) of the BorderLayout, as opposed to the NORTH.
public void paint(Graphics p) {.. is wrong for panel. Since panel is a Swing JPanel it should be public void paintComponent(Graphics p) {..
Once those things are attended to, this is how it might appear.
Other problems.
The only code that needs to extend a class is for the applet itself and panel. In fact, even the panel could be changed to show a BufferedImage (the graph) inside a JLabel
The panelB and panelC are entirely redundant, just add a Box directly to the layout area of the parent component needed.
The nomenclature is wrong.
Java class names should be EachWordUpperCase
Use meaningful class names - the applet might be GraphApplet, the graphing area Graph, ..I am not sure what to call the last 2 panels, since they carry identical components. If there were but one, I might call it Controls as a class (which is overkill for this), or controls if it were an instance of a plain JPanel or Box.
None of the calls to set maximum or preferred size are recommended in this situation. The only case that can be made for it is in the preferred size of the graph itself, but since this appears in the CENTER of applet, a size will be suggested by the applet width/height specified in HTML, minus the natural size of the other components (the CENTER component will get 'the rest of the space').
Update
..how am I going to change the code in my applet so that the applet adds one of each panel(?)
Change:
panel a = new panel();//vertical
To:
panelB a = new panelB(new Box(BoxLayout.Y_AXIS));//vertical
Is the simplest way. Note that it changes if you decide to add the Box directly.
Box a = new Box(BoxLayout.Y_AXIS);//vertical
Please try to run this code now, tell me is this closer to what you wanted ? if so do let me know, so that I can delete my answer. Do watch the changes I had done :
in your PanelC class, where instead of using init() method, I
made the constructor.
Inside your Graph class< i had removed all setPreferredSizes(...)
calls, and instead I had overridden getPreferredSize() for each
Class extending JPanel. For this visit each Class Panel, PanelB and
PanelC.
Lastly I had changed the Dimension values supplied to the JPanel,
which is to go to the LEFT/LINE_START side, to something that can
be seen
BEST CHANGE is the use of EDT - Event Dispatch Thread, seems like you
really not aware of Concurrency in Swing
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class Graph extends JApplet{
public void init(){
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
Container conn = getContentPane();
conn.setLayout(new BorderLayout());
Panel p = new Panel();//graph
conn.add(p,BorderLayout.CENTER);
Box box1 = new Box(BoxLayout.Y_AXIS);
PanelB a = new PanelB(box1);//vertical
conn.add(a,BorderLayout.LINE_START);
Box box2 = new Box(BoxLayout.X_AXIS);
PanelC b = new PanelC(box2);//horizontal
conn.add(b,BorderLayout.PAGE_END);
}
});
}
}
class Panel extends JPanel {
public Panel(){
this.setBackground(Color.yellow);
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(460,560));
}
public void paintComponent(Graphics p) {
super.paintComponent(p);
Graphics2D graph = (Graphics2D)p;
Dimension appletSize = this.getSize();
int appletHeight = (int)(appletSize.height);
int appletWidth = appletSize.width;
this.setBackground(Color.yellow);//set background color.
int x,y,y1,x1,a,b,p1x,p1y,p2x,p2y;
//line co-ordinates
//the numbers represent the number of boxes on the graph
int xstart = 7;
int ystart = 1;
int xfinish = 3;
int yfinish = 9;
//other variables
int i = 0;
int i2 = 0;
int m = 0;
int n = 0;
int m2 = 0;
int n2 = 0;
int f2 = 0;
int g2 = 1;
//ranges
int f = 5;
int g = -5;
//change -ve num to +ve
int g3 = Math.abs(g);
int a1 = g3 + f;
int b1 = a1;
y1 = (appletHeight);
x1 = (appletWidth);
y = (appletHeight / 2);
x = (appletWidth / 2);
a = (appletWidth / a1);
b = (appletHeight / b1);
int d = (appletWidth / a1);
int e = (appletHeight / b1);
/**
to determine the
ammount of pixles there
is in each box of the
graph, both y-axis and
x-axis
*/
int xbox = x1 / 10;
int ybox = y1 / 10;
//line variables
//the xstart, ystart, etc represent the number of boxes
//top point of the line on the graph
p1x = xbox * xstart;//start x
p1y = ybox * ystart;//start y
//lowwer point of the line on the graph
p2x = xbox * xfinish;//finish x
p2y = ybox * yfinish;//finish y
//draw y-axis numbers
//(+ve)
while(f != 0){
String s = String.valueOf(f);
p.drawString(s,(x + 5),m + 13);
m = m + b;
f = f - 1;
}
//(-ve)
m2 = y;
while(f2 != g-1){
String u = String.valueOf(f2);
p.drawString(u,(x + 5),m2 - 3);
m2 = m2 + b;
f2 = f2 - 1;
}
//draw x-axis numbers.
//(-ve)
while(g != 0){
String t = String.valueOf(g);
p.drawString(t,n,y - 5);
n = n + a;
g = g + 1;
}
//(+ve)
n2 = x + a;
while(g2 != g3+1){
String vw = String.valueOf(g2);
p.drawString(vw,n2 -10,y - 5);
n2 = n2 + a;
g2 = g2 + 1;
}
BasicStroke aLine2 = new BasicStroke(1.0F,
BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
graph.setStroke(aLine2);
//notch on numbers and grid lines
//left to right, top to bottom notches
int v2 = -5;
int v5 = 0;
while(i <= a1-1){
p.setColor(Color.lightGray);//lightgray line
p.drawLine(a,0,a,y1);//vertical lightgray
p.drawLine(0,b,x1,b);//horizontal lightgray
a = a + d;
b = b + e;
i = i + 1;
}
//notches
while(i2 <= a1){
p.setColor(Color.blue);//notch color
p.drawString("x",v2+2,y+3);//xaxis
p.drawString("x",x-4,v5+4);//yaxis
v5 = v5 + e;
v2 = v2 + d;
i2 = i2 + 1;
}
//draws the border of the graph
p.setColor(Color.black);
Rectangle2D.Float rect = new Rectangle2D.Float(0,0,x1,y1);
BasicStroke aLine = new BasicStroke(2.5F,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
graph.setStroke(aLine);
graph.draw(rect);
//draw cross
BasicStroke aLine3 = new BasicStroke(2.5F,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
graph.setStroke(aLine3);
p.drawLine(x,0,x,y1); //vertical line
p.drawLine(0,y,x1,y); //horizontal line
//display the value of graph width and graph height
String aw = String.valueOf(x1);
p.drawString("Graph Width = ", 50,90);
p.drawString(aw,150,90);
p.drawString("Graph Height = ", 50,110);
String ah = String.valueOf(y1);
p.drawString(ah,156,110);
//draw line on graph
BasicStroke aLine4 = new BasicStroke(1.5F,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
graph.setStroke(aLine4);
p.setColor(Color.red);
if(p1x <= x1 && p2x <= x1 && p1y <= y1 && p2y <= y1){
p.drawLine(p1x,p1y,p2x,p2y);
Color c = new Color(0,0,0);
p.setColor(c);
p.drawString("X", p1x-4,p1y+4);
p.drawString("X", p2x-4,p2y+4);
}
else{
p.setColor(Color.black);
p.drawRect(48,34,223,35);
p.setColor(Color.white);
p.fillRect(49,35,222,34);
p.setColor(Color.red);
p.drawString("Wrong co-ordinates!!!", 50,50);
p.drawString("Values exceede applet dimensions.", 50,65);
}
}
}
class PanelB extends JPanel{
public PanelB (Box a) {
a = Box.createVerticalBox();
a.add(new JLabel("Please enter the values below:"));
a.add(new JLabel("a"));
JTextField g1 = new JTextField("0.0");
g1.setMaximumSize(new Dimension(100,30));
a.add(g1);
a.add(new JLabel("b"));
JTextField g2 = new JTextField("0.0");
g2.setMaximumSize(new Dimension(100,30));
a.add(g2);
a.add(new JLabel("c"));
JTextField g3 = new JTextField("0.0");
g3.setMaximumSize(new Dimension(100,30));
a.add(g3);
a.add(new JLabel("d"));
JTextField g4 = new JTextField("0.0");
g4.setMaximumSize(new Dimension(100,30));
a.add(g4);
a.add(new JButton("Plot"));
a.add(new JButton("Refine"));
add(a);
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(200,100));
}
}
class PanelC extends JPanel{
public PanelC (Box b) {
b = Box.createHorizontalBox();
b.add(new JLabel("a"));
JTextField f1 = new JTextField("0.0");
f1.setMaximumSize(new Dimension(100,30));
b.add(f1);
b.add(new JLabel("b"));
JTextField f2 = new JTextField("0.0");
f2.setMaximumSize(new Dimension(100,30));
b.add(f2);
b.add(new JLabel("c"));
JTextField f3 = new JTextField("0.0");
f3.setMaximumSize(new Dimension(100,30));
b.add(f3);
b.add(new JLabel("d"));
JTextField f4 = new JTextField("0.0");
f4.setMaximumSize(new Dimension(100,30));
b.add(f4);
b.add(new JButton("Plot"));
b.add(new JButton("Refine"));
add(b);
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(201,50));
}
}
Here is the HTML file I used :
<html>
<p> This file launches the 'Graph' applet: Graph.class! </p>
<applet code= "Graph.class" height = 550 width = 1000>
No Java?!
</applet>
</html>
Here is the output I am getting :