need help accessing variable inside inner class for loop - java

I am having problem accessing the variable int i inside the inner class
Code:
public class OnColumnSelectPanel {
JFrame jFrame;
JPanel mainJPanel;
//JPanel jPanel1;
//JTextField jTextField;
//JButton jButton;
//JComboBox jComboBox [];
MigLayout layout;
MigLayout frameLayout;
//column names
ColumnNames cn = new ColumnNames();
List<String> listedColumnNames = cn.getColumnNames();
String columnNames[] = listedColumnNames.toArray(new String[0]);
public OnColumnSelectPanel(int n) {
//jPanel1 = new JPanel();
jFrame = new JFrame("Create Structure of Columns");
// mainJPanel = new JPanel();
layout = new MigLayout("flowy", "[center]rel[grow]", "[]10[]");
frameLayout = new MigLayout("flowx", "[center]rel[grow]", "[]10[]");
//mainJPanel.setLayout(new BoxLayout(mainJPanel, BoxLayout.X_AXIS));
//MigLayout mainJPanelLayout = new MigLayout("flowy", "[]rel[grow]", "[]5[]");
// declare & initialize array
JPanel jPanel[] = new JPanel[n];
JComboBox jComboBox[] = new JComboBox[n];
JButton jButton[] = new JButton[n];
final int num = 0;
for (int i = 0; i < n; i++) {
//assign array
jComboBox[i] = new JComboBox(columnNames);
jButton[i] = new JButton("add Sub Heading");
jPanel[i] = new JPanel();
System.out.println("Loop number: " + i);
jButton[i].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
for (int j = 0; j < n; j++) {
if (j <= n) {
jComboBox[j] = new JComboBox(columnNames);
jPanel[j].add(jComboBox[j]);
jFrame.revalidate();
} else {
JOptionPane.showMessageDialog(null, "You have exceeded your limit");
}
}
}
});
//set layout
jPanel[i].setLayout(layout);
//jPanel[i].setPreferredSize(new Dimension(50, 400));
//mainJPanel.setLayout(mainJPanelLayout);
jFrame.setLayout(frameLayout);
System.out.println(" height " + jPanel[i].getHeight());
jPanel[i].add(jComboBox[i], new CC().newline());
//jPanel.add(jTextField);
jPanel[i].add(jButton[i]);
//mainJPanel.add(jPanel[i], "spany");
jPanel[i].repaint();
jFrame.add(jPanel[i]);
jFrame.pack();
jPanel[i].revalidate();
jFrame.revalidate();
//mainJPanel.revalidate();
//jFrame.revalidate();
}
//mainJPanel.setSize(600, 400);
//jFrame.add(jPanel1);
jFrame.setVisible(true);
jFrame.setSize(600, 400);
jFrame.setAlwaysOnTop(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Output Frame
As you can see the output image. The problem here is that when i click on the add subheading button, combobox is added to every jpanel. this is because i can;t pass the value i to the inner class.
It would be interesting to know possible solutions for this.
FYI I am using Mig Layout.
Thanks in advance.

Only effectively final variables can be accessed from anonymous classes.
i is modified by the loop, so it is not effectively final.
A workaround would look like this:
for (int i = 0; i < n; i++) {
...
final int effectivelyFinal = i;
jButton[i].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
...
// use effectivelyFinal instead of i
});
}
But as other suggested, it would be way better to extract the anonymous class into a real class and pass all required parameters using the constructor. This can look like this:
class MyListener implements ActionListener {
private final int index;
// add more fields for other required parameters
public MyListener(int index) {
this.index = index;
}
#Override
public void actionPerformed(ActionEvent e) {
// use index
}
}
Usage:
jButton[i].addActionListener(new MyListener(i));

Related

Java repaint() not working when called from a different class

My repaint works when called in the same class but not from another class. I haven't been able to find this issue elsewhere. I have put my code below. Thank you! The code is for making a calculator in a JFrame with 2 JPanels, one showing the user's input and one with all the buttons. I want to call repaint so the drawString() method changes as the user enters their input.
public class Calculator
{
public static void main(String[] args)
{
Calculator c = new Calculator();
}
public Calculator()
{
JFrame frame = new JFrame("Calculator");
frame.setSize(800, 800);
frame.setResizable(false);
Buttons b = new Buttons();
Display d = new Display();
frame.setLayout(new GridLayout(2, 1));
frame.add(d);
frame.add(b);
frame.setVisible(true);
}
public class Buttons extends JPanel implements ActionListener
{
private int z;
public JButton[] buttons;
public Display d;`enter code here`
public String[] values;
public String clickedButton;
public Buttons()
{
setBackground(Color.BLACK);
setLayout(new GridLayout(5, 4));
values = new String[100];
for(int i = 0; i < values.length; i++)
{
values[i] = new String("");
}
addButtons();
}
public void addButtons()
{
Font courier = new Font("Courier", Font.BOLD, 20);
buttons = new JButton[20];
for(int i = 0; i < buttons.length; i++)
{
buttons[i] = new JButton(Integer.toString(i));
buttons[i].setBackground(Color.BLUE);
buttons[i].setForeground(Color.WHITE);
buttons[i].setFont(courier);
buttons[i].setFocusable(false);
buttons[i].addActionListener(this);
buttons[i].setBorder(BorderFactory.createLineBorder(new Color(0, 100, 175, 255)));
add(buttons[i]);
}
buttons[10].setVisible(false);
buttons[10].setEnabled(false);
buttons[11].setVisible(false);
buttons[11].setEnabled(false);
buttons[12].setText("C");
buttons[13].setText("+");
buttons[14].setText("-");
buttons[15].setText("*");
buttons[16].setText("/");
buttons[17].setText("+/-");
buttons[18].setText("^");
buttons[19].setText("=");
}
public void actionPerformed(ActionEvent e)
{
String action = e.getActionCommand();
d = new Display();
for(int i = 0; i < 10; i++)
{
if(action.equals(Integer.toString(i)))
{
values[d.i]+=Integer.toString(i);
System.out.println("should be repainting");
d.repaint();
}
}
}
}
public class Display extends JPanel
{
public Buttons b;
public Font courier;
public int i;
public Display()
{
i = 0;
b = new Buttons();
setBackground(Color.BLACK);
courier = new Font("Courier", Font.BOLD, 50);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.setFont(courier);
g.drawString(b.values[i], 50, 50);
repaint();
}
}
}
You seem to be creating a new Display every time and telling that to repaint instead of the real one. Move your Display d variable into the Calculator class as a field, and don't declare new ones.
You create the original Display object as a local variable so it can't be accessed from elsewhere, so make this part use the class field instead:
Display d = new Display();
Also, this line in actionPerformed is creating a new instance and should be removed:
d = new Display();
Don't call repaint from within a paint method. This will cause the API to schedule anther paint request, which will eventually consume all your CPU cycles
You actually have two instances of Display, one which is on the screen, one which is not
While there are a few ways to fix this, one of the simpler would be to simply pass the instance of Display created to in Calculators constructor to Buttons, for example...
public class Calculator {
public static void main(String[] args) {
Calculator c = new Calculator();
}
public Calculator() {
JFrame frame = new JFrame("Calculator");
frame.setSize(800, 800);
frame.setResizable(false);
Display d = new Display();
Buttons b = new Buttons(d);
frame.setLayout(new GridLayout(2, 1));
frame.add(d);
frame.add(b);
frame.setVisible(true);
}
public class Buttons extends JPanel implements ActionListener {
private int z;
private JButton[] buttons;
private String[] values;
private String clickedButton;
private Display d;
public Buttons(Display d) {
this.d = d;
setBackground(Color.BLACK);
setLayout(new GridLayout(5, 4));
values = new String[100];
for (int i = 0; i < values.length; i++) {
values[i] = new String("");
}
addButtons();
}
Then buttons can use that instance to display what ever it needs to display
private int index = 0;
public void actionPerformed(ActionEvent e) {
String action = e.getActionCommand();
for (int i = 0; i < 10; i++) {
if (action.equals(Integer.toString(i))) {
values[index] += Integer.toString(i);
d.setValue(values[index]);
index++;
}
}
}
}
public class Display extends JPanel {
public Font courier;
private String value;
public Display() {
setBackground(Color.BLACK);
courier = new Font("Courier", Font.BOLD, 50);
}
public void setValue(String value) {
this.value = value;
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.setFont(courier);
if (value != null) {
g.drawString(value, 50, 50);
}
}
}
}

Paint Method in an Applet (Sort Algorithms in Java)

I have two classes: One applet for the GUI and one class to sort numbers.
Sort Class:
public class Sortierung {
public int[] Zahlen = {5,2,3,1,4};
int z;
public static int zufallszahl (int z){
int dauer = ((int)(Math.random()*z))+1;
return dauer;
}
public void ArrayErstellen(int l){
int[] myIntArray = new int[l];
for(int f=0;f<myIntArray.length;f++){
myIntArray[f]=zufallszahl(10);
}
Zahlen=myIntArray;
}
public int[] BubbleSort() {
for(int p=0;p<Zahlen.length-1;p++){
for(int i=0;i<Zahlen.length-1;i++){
if(Zahlen[i]<Zahlen[i+1]){
continue; //beendet den derzeitigen Schleifenablauf
}
else{
z=Zahlen[i];
Zahlen[i]=Zahlen[i+1];
Zahlen[i+1]=z;
}
}
}
return Zahlen;
}
public int[] InsertionSort() {
int f = 0; //Variable 1
int j = 0; //Variable 2
for(int i = 1;i < Zahlen.length;i++){
j = i;
while((j>0)&&(Zahlen[j-1]>Zahlen[j])){
f = Zahlen[j-1];
Zahlen[j-1] = Zahlen[j];
Zahlen[j] = f;
j--;
}
}
return Zahlen;
}
public int[] SelectionSort(){
for (int i = 0; i < Zahlen.length - 1; i++){
int s = i; //Neue Variable für die Schleife
for (int j = i + 1; j < Zahlen.length; j++)
if (Zahlen[j] < Zahlen[s]){
s = j;
}
int smallerNumber = Zahlen[s];
Zahlen[s] =Zahlen[i];
Zahlen[i] = smallerNumber;
}
return Zahlen;
}
public void ArrayWiedergeben(int[] eimer){
for(int u=0;u<eimer.length;u++){
System.out.println(eimer[u]);
}
} }
Gui (Applet):
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Gui extends JApplet {
JTextField tf;
JButton b;
JButton k;
JLabel s;
JLabel u;
int anzahl;
int test = 0;
JPanel p = new JPanel();
JPanel h = new JPanel();
JPanel f = new JPanel();
Sortierung neu = new Sortierung();
Choice Sortierungsmethode = new Choice();
ActionListener startOhr = new ActionListener() {
public void actionPerformed(ActionEvent e) {
anzahl = Integer.parseInt(tf.getText());
if (anzahl > 100) {
tf.setText("Wert zu hoch.");
return;
}
neu.ArrayErstellen(anzahl);
tf.setText("Array[" + anzahl + "] erstellt.");
}
};
ActionListener sortOhr = new ActionListener() {
public void actionPerformed(ActionEvent e) {
switch (Sortierungsmethode.getSelectedIndex()) {
case 0:
int[] eimer = neu.BubbleSort();
neu.ArrayWiedergeben(eimer);
test = 1;
repaint();
break;
case 1:
int[] eimer2 = neu.InsertionSort();
neu.ArrayWiedergeben(eimer2);
break;
case 2:
int[] eimer3 = neu.SelectionSort();
neu.ArrayWiedergeben(eimer3);
break;
default:
break;
}
}
};
public void init() {
tf = new JTextField(8);
b = new JButton("Erstellen");
k = new JButton("Bestätigen");
s = new JLabel("Array Länge");
u = new JLabel("Sortmethode");
Sortierungsmethode.add("Bubblesort");
Sortierungsmethode.add("Insertionsort");
Sortierungsmethode.add("Selectionsort");
setLayout(new BorderLayout());
p.add(s);
p.add(tf);
p.add(b);
h.add(u);
h.add(Sortierungsmethode);
h.add(k);
this.add(p, BorderLayout.NORTH);
this.add(h, BorderLayout.CENTER);
this.add(f, BorderLayout.SOUTH);
b.addActionListener(startOhr);
k.addActionListener(sortOhr);
}
}
My intention is to draw the sorted array in a coordinate system with a paint
method. The problem is, that when I try to create a paint method, the whole screen is white and I cant see anything
You need to address several details:
You should create a class which overrides JPanel to do your custom drawing. You can create an instance of the class in your Gui class.
You should override paintComponet() rather than paint().
The first line in paintComponent(Graphics g) should be super.paintComponent(g) in order to ensure that the super class can paint itself.
You need to be careful that your sorting algorithm doesn't take over the main thread. This will cause your app to freeze and nothing will paint until the sorting has finished.
You need to create a Thread object and set it to call run method 30 times per second. In this method, call repaint() method of main JPanel.

Action Listeners

I have rows and columns of jbuttons, and when each button is clicked they should turn red, and when clicked a second time they should return to their original color. So far, the code I have written is as follows:
public class MainPanel extends JPanel
{
private JButton[][] btn1 = new JButton[3][5];
public MainPanel()
{
JPanel MainPanel= new JPanel();
MainPanel.setPreferredSize(new Dimension(700,700));
JPanel p1 = new JPanel();
{
p1.setLayout(new GridLayout(3,5,10,10));
p1.setBackground(Color.WHITE);
for(int i = 0; i < 3; i++)
for(int j = 0; j < 5; j++)
{
btn1[i][j] = new JButton();
btn1[i][j].setBackground(Color.YELLOW);
p1.add(btn1[i][j]);
btn1[i][j].addActionListener
(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
btn1[i][j].setBackground(Color.RED);
}
}
);
}
}
}
}
The line btn1[i][j].setBackground(Color.RED); is stating 'local variables referenced from an inner class must be final or effectively final'. Does anyone know how to fix this?
Add listeners in loop:
for(int i = 0; i < 3; i++)
for(int j = 0; j < 5; j++) {
final JButton btn = btn1[i][j];
btn1[i][j].addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
btn.setBackground(Color.RED);
}
}
);
}
You added listener only to the first button.

How to remove NullPointerExceptions due to JButton array

I'm terribly unfamiliar with swing, but here I'm trying to make a "clacker" game where each time two dice are rolled, either you click the two (or 1 if they're the same number) buttons representing the two numbers or just one button representing the sum of the two numbers. If a button is clicked, visible is set to false. I have a JButton array to hold the 12 buttons, but every time a reference is made to the array buttons, it throws a NullPointerException. I've tried putting the code making the JButton array within the constructor and actionPerformed, but it still throws the same exception.
Here is the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class Clacker implements ActionListener
{
JPanel panel;
JFrame frame;
JLabel dieFace1, dieFace2, numTurns;
JButton[] buttons;
JButton rollDie, reset;
String turn="Turns: ";
int turns, numCovered;
public Clacker()
{
frame = new JFrame("clacker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel=new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(50,30,50,30));
panel.setBackground(Color.gray);
panel.setLayout(new GridLayout(5,10,1,1));
dieFace1=new JLabel(new ImageIcon("die3.jpg"));
dieFace1.setAlignmentX(JLabel.RIGHT_ALIGNMENT);
dieFace1.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
panel.add(dieFace1);
dieFace2=new JLabel(new ImageIcon("die3.jpg"));
dieFace2.setAlignmentX(JLabel.RIGHT_ALIGNMENT);
dieFace2.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
panel.add(dieFace2);
rollDie=new JButton("Roll Die");
rollDie.setAlignmentX(JButton.RIGHT_ALIGNMENT);
rollDie.addActionListener(this);
panel.add(rollDie);
numTurns=new JLabel(turn+turns);
numTurns.setAlignmentX(JLabel.LEFT_ALIGNMENT);
numTurns.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
panel.add(numTurns);
setArray();
reset=new JButton("Reset");
reset.setAlignmentX(JButton.LEFT_ALIGNMENT);
reset.addActionListener(this);
panel.add(reset);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
public void setArray()
{
JButton[] buttons = new JButton[12];
for (int i=0; i<12; i++)
{
buttons[i]= new JButton(i+1+"");
buttons[i].setActionCommand(i+"");
buttons[i].addActionListener(this);
panel.add(buttons[i]);
buttons[i].setEnabled(false);
System.out.println(buttons[i]);
}
}
public void actionPerformed(ActionEvent event)
{
if (event.getActionCommand().equals("Roll Die"))
{
Random asdf = new Random();
int newRoll= asdf.nextInt(6)+1;
dieFace1.setIcon(new ImageIcon("die"+newRoll+".jpg"));
Random asdf2 = new Random();
int newRoll2= asdf2.nextInt(6)+1;
dieFace2.setIcon(new ImageIcon("die"+newRoll2+".jpg"));
int sum=newRoll+newRoll2;
turns++;
numTurns.setText(turn+turns);
for (int i=0; i<7; i++) ////here
{
if (i==newRoll)
buttons[i].setEnabled(true);
if (i==newRoll2)
buttons[i].setEnabled(true);
}
for (int i=7; i<13; i++)
{
if (sum==i)
buttons[i].setEnabled(true);
}
String eventName = event.getActionCommand();
for (int b=1; b<13; b++)
{
if (eventName.equals(buttons[b].getActionCommand())) ////here
{
if (Integer.parseInt(eventName)>6)
{
buttons[b].setText("");
numCovered++;
buttons[b].setVisible(false);
for (int i=0; i<7; i++)
buttons[i].setEnabled(false);
}
}
}
for (int i=0; i<buttons.length; i++) ////here
buttons[i].setEnabled(false);
if (numCovered==12)
{
rollDie.setEnabled(false);
numTurns.setText("Win! "+turns+" turns");
}
}
else if (event.getActionCommand().equals("Reset"))
{
turns=0;
numTurns.setText(turn+turns);
rollDie.setEnabled(true);
numCovered=0;
for (int i=0; i<buttons.length; i++) ////here
{
buttons[i].setVisible(true);
buttons[i].setEnabled(false);
}
}
}
private static void runGUI()
{ JFrame.setDefaultLookAndFeelDecorated(true); Clacker game = new Clacker();}
public static void main(String[] args)
{ javax.swing.SwingUtilities.invokeLater(new Runnable() {public void run() { runGUI();}});}
}
In setArray you're declaring a local variable instead of referencing the class member variable. Just change
JButton[] buttons = new JButton[12];
to
this.buttons = new JButton[12];

java update Jpanel component

I am using a Custome jPanel in my Gui Builder JFram Class A, the problem i am facing is to update the components (Lable) in my JPanel when I click button in JFrame.here is the button in Gui Builder JFrame ClassA: it changes the color of Jpl and also remove all the labels but not update the new labels.
private void btnShowActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Random randomGenerator = new Random();
for (int idx = 1; idx <= 10; ++idx) {
q = randomGenerator.nextInt(100);
}
jpl1.removeAll();
new Jpl().printMe(ClassA.q);
jpl1.revalidate();
jpl1.setBackground(Color.BLUE);
jpl1.repaint();
}
here is Jpl class that is used as a custome component in GuiBuilder JFrame Class A.
public class Jpl extends JPanel {
public Jpl() {
printMe(ClassA.q);
}
public void printMe(int q) {
for (int i = 0; i <q; i++) {
System.out.println(i+"rinting lable");
String htmlLabel = "<html><font color=\"#A01070\">" + i + " New Lable </font></html>";
JLabel lbl = new JLabel(htmlLabel);
setLayout(new GridLayout(0, 1));
add(lbl, Jpl.RIGHT_ALIGNMENT);
lbl.setForeground(Color.BLUE);
Border border = BorderFactory.createLineBorder(Color.lightGray);
lbl.setBorder(border);
lbl.add(new JSeparator(SwingConstants.HORIZONTAL));
lbl.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
JOptionPane.showMessageDialog(null, "You Slected");
System.out.println(label.getText() + "NO AKKA is Selected");
}
});
}
}
You are calling printMe() on a new instance of Jpl, try that:
private void btnShowActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Random randomGenerator = new Random();
for (int idx = 1; idx <= 10; ++idx) {
q = randomGenerator.nextInt(100);
}
jpl1.removeAll();
jpl1.printMe(ClassA.q); // HERE - REMOVED new and using jpl1 instance
jpl1.setBackground(Color.BLUE);
jpl1.revalidate();
jpl1.repaint();
}
In don't understand why you loop 10 times for your random number. Only the last result will be kept, maybe a you wanted to use q += randomGenerator.nextInt(100);. Also, ClassA.q should be replaced by q if it's the same variable.

Categories