Rendering from a List<> array - java

I am trying to render to a JPanel from a list array. I've created my own 2D renderer, but when I try and add to the list using the RenderAdd function, it either doesn't add, or the list array doesn't allow the list to be read...
He's the code which starts it.
JFrame frame = new JFrame();
frame.setSize(900, 500);
frame.setVisible(true);
Render render = new Render(new RenderDimension(frame.getX(),frame.getY(),frame.getWidth(),frame.getHeight()), frame);
BufferedImage zombie = new ImageLoader().readImage("zombie");
BufferedImage player = new ImageLoader().readImage("player");
render.RenderAdd(new RenderImage(new RenderDimension(100, 100, player.getWidth(), player.getHeight()), player));
render.RenderAdd(new RenderImage(new RenderDimension(0, 0, zombie.getWidth(), zombie.getHeight()), zombie));
render.start();
render.RenderAdd(new RenderImage(new RenderDimension(200, 100, player.getWidth(), player.getHeight()), player));
'render' is the main Render class in the rendering part. Then, RenderAdd adds a RenderImage which has RenderDimension which is the x and y of the object, and the image width and height. Then also takes a BufferedImage as a parameter.
Although, every time I try running the program, it comes with a blank screen
Now, in the Render class, there is another class which extends a thread. This is the class which takes the frame as a parameter, deletes the contents and starts painting to the getContentPane(). This next code is inside the paintComponent() function in the renderthread class.
Unfortunately, nothing paints, but is does process because I've tried with System.out.print("h") which repeatedly prints itself.
for (RenderImage r : render.getList()){
int x = r.getSize().getX();
int y = r.getSize().getY();
int wi = r.getSize().getWidth();
int hi = r.getSize().getHeight();
if (x + wi >= -1 && x + wi <= d.getWidth()){
if (y + hi >= -1 && y + hi <= d.getHeight()){
g.drawImage(r.getImage(), x, y, null);
}
}
}
frame.getContentPane().add(p);
frame.getContentPane().validate();
frame.getContentPane().repaint();
I think the problem is that the list won't add, so here's that part.
List<RenderImage> render = new ArrayList<RenderImage>();
public List<RenderImage> getList(){
return render;
}
public void RenderAdd(RenderImage renders){
render.add(renders);
}

It's difficult to know with the example code you've given us.
Scenario #1, overriding JComponent#paintComponent
If you are doing this inside your paintComponent method
for (RenderImage r : render.getList()){
int x = r.getSize().getX();
int y = r.getSize().getY();
int wi = r.getSize().getWidth();
int hi = r.getSize().getHeight();
if (x + wi >= -1 && x + wi <= d.getWidth()){
if (y + hi >= -1 && y + hi <= d.getHeight()){
g.drawImage(r.getImage(), x, y, null);
}
}
}
frame.getContentPane().add(p);
frame.getContentPane().validate();
frame.getContentPane().repaint();
Then DON`T.
Calling any method that updates the UI in any way from within a paint method will only result in disaster. This is simply triggering another repaint request to be added to the Event Dispatching Thread, which will call you paintComponent method and you can say good by to your CPU and program responsiveness.
Also, make sure, when updating the UI from a different Thread other then the EDT, make sure you sync the request back to the EDT using SwingUtilities#invokeLater or SwingUtilities.invokeAndWait
Also, make sure you are calling super.paintComponent
Scenario #2, using JComponent#getGraphics
The question that comes to mind is, where does g come from in your example.
If you're using JComponent#getGraphics, then don't. This is simple a snapshot of the graphics between repaints, as soon as the next repaint occurs, it will be erased.
Create a custom component from something like JPanel and override it's paintComponent method and update the component with your RenderImage loop (just leave out the code that changes the UI)
Also, make sure that all repaint requests are made from within the context of EDT

Related

How do I properly paint to a frame/panel?

I've tried many variations of everything at the bottom of this class and so far nothing works. Occasionally an edit will cause the print statement to work, but the window just always opens at a size that isn't even the one I set it up and stays blank. I don't know what's wrong with it. I'm trying to print 1024 rectangles on my window with a pause inbetween each print. The values are right, they're just not getting painted for some reason. Changing the method to paintComponent doesn't seem to do much either. The code is long, so here's a pastebin: http://pastebin.com/ridipz3X. The important stuff is at the end though:
JFrame frm = new TestEnvironment();
frm.setSize(1152, 1152);
frm.setVisible(true);
JPanel panel = new JPanel();
frm.add(panel);
t = 0;
i = 0;
while (t < x - 1) {
panel.repaint();
j++;
t++;
Thread.sleep(10000);
}
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(getForeground());
try {
for (int h = 0; h < 1152; h++) {
g.drawRect(h, 0, (int) (((ampArray[h][j]) / maxFreq) * 1152),
1);
g.fillRect(h, 0, (int) (((ampArray[h][j]) / maxFreq) * 1152),
1);
System.out.println(ampArray[h][j]);
}
} finally {
g.dispose();
}
}
}
Thanks
Painting is typically done from within the paintComponent method of a component that extends from JComponent, typically a JPanel, depending on your needs.
You should refrain from overriding paint of top level containers like JFrame for a number of reasons, including, they are not double buffered, you will end up painting under the window decorations, Swing windows contain a number of layered components which make up the viewable content of a window, meaning that you will either paint over or under this content, which just gets messy.
You should never dispose of Graphics context that you did not create yourself.
Swing is a single threaded environment, that is, anything that blocks the thread (such as Thread.sleep, will prevent it from process new repaint requests and events, making it look like you program has stopped.
Swing is also not thread safe. This means that you are required to ensure that all updates and interactions with the UI are done from within the context of the Event Dispatching Thread.
Animation is typically achieved through the use of a javax.swing.Timer or SwingWorker depending on the complexity of the animation. You can use a Thread, but it complicates the issues as you will be required to ensure that all updates to the UI (directly or otherwise) are done from within the context of the EDT manually.
Take a look at:
Performing Custom Painting
Painting in AWT and Swing
Concurrency in Swing

Static method is slow

I'm programming a simple game in java.
I've made a collision test with 30 FPS, where I had to get the size of the window.
Because I haven't had access to the GUI instance, I thought I'd make a shared instance, because this is pretty standard in Objective-C, where I come from.
class GUI extends JFrame {
private static GUI _sharedInstance;
public static GUI sharedInstance() {
if (_sharedInstance == null) {
_sharedInstance = new GUI();
}
return _sharedInstance;
}
}
But for some reason, it was really slow.
Then I replaced the shared instance with public static final instances for the size, and it works fast now, even with 60 FPS or higher.
Can anyone explain me why this happens?
EDIT
So instead of calling GUI.sharedInstance().getWidth(), I'm just calling GUI.windowSize.width.
I have used the public static final Dimension windowSize instead.
EDIT
Here's the collision detection.
So, instead of calling int width = GUI.kWindowWidth;,
I was calling int width = GUI.sharedInstance().getWidth(); before.
// Appears on other side
if (kAppearsOnOtherSide) {
int width = GUI.kWindowWidth;
int height = GUI.kWindowHeight;
// Slow
// int width = GUI.sharedInstance().getWidth();
// int width = GUI.sharedInstance().getHeight();
Point p = this.getSnakeHead().getLocation();
int headX = p.x;
int headY = p.y;
if (headX >= width) {
this.getSnakeHead().setLocation(new Point(headX - width, headY));
} else if (headX < 0) {
this.getSnakeHead().setLocation(new Point(headX + width, headY));
} else if (headY >= height) {
this.getSnakeHead().setLocation(new Point(headX, headY - height));
} else if (headY < 0) {
this.getSnakeHead().setLocation(new Point(headX, headY + height));
}
}
I know that this might not be the real answer to the question, but there could be a problem with a race condition.
I assume that you are trying to use the lazy/singleton pattern, because the construction of the GUI class is quite expensive and you need only one instance.
Now, what happens? If a thread runs into the if statement body, it creates a new GUI instance. Just before the next assignment, it gets paused by the scheduler.
Now another thread pops in, sees that _sharedInstance is still null and creates another GUI instance (and so on...).
Lets assume that there are only two threads.
The first thread runs to the end and the second one is continued, now you have two instances of the GUI class. Nobody says that the unassigned GUI instance gets destroyed or garbage collected.
That could explain tha 50 % performance loss compared to using finally and assigning the GUI instance directly to _sharedInstance.
See e.g. the Java examples at http://en.wikipedia.org/wiki/Singleton_pattern
Double checked locking, inner classes or an enum are the ways you can use.

JButton and its Location methods acting very weird

my problem is, that when I create a JButton, whithin its constructor, I set its location to some relative coordinates, say x = 5, and y = 6, using the following code:
this.setLocation(new Point(x, y));
but after I am trying to get its location using the getLocation() method, it always returns 0,0. Please note that this happens for every JButton I am trying to place on a grid layout powered JFrame, and during the debugging process, I have also noted that their location is being properly instantiated.
Can someone explain to me why this happens, and if I can fix it somehow?
EDIT:
The constructor (The brick class that I made, extends JButton):
public Brick(int posx, int posy) {
this.setLocation(new Point(posx, posy));
this.setVisible(true);
}
I make about 100+ of them in 2 for loops:
for (int row = 0; row < 15; row++) {
for (int column = 0; column < 15; column++) {
Brick brickie = new Brick(row, column);
}
}
But afterwards, if I wanna pick a brick and check its location like this:
Point brickLocation = brickie.getLocation();
both brickLocation.x == 0 and brickLocation.y == 0
You are trying to change button location when it is automatically assigned by layout manager (your GridLayout). That is why you are always getting the same value back - layout just overwrites it.
To be able to change any component bounds/location (including buttons) manually - you have to set "null" as the container's layout. After that just change the location/size/bounds as you like and it will affect the components positions.
Also you don't need to use "setVisible(true)" - by default that flag is true for all components (even those that aren't displayed yet).
JComponent(s) can returns its coordinates, getBounds or getLocation only
if container is already visible on the screen
after pack()

How to repaint out of focus dialog without gaining its focus?

I made some menu and it is to update conmmon variables (for text on grid) then the out-of-focus dialog must repaint the grid. Here is the screenshot:
The main control panel is always at top position and 'Data Display' panel is always sitting behind it. When press a button on front panel, Data Display must update its grid. Currently, the common variable 0.4 on the grid is updated by adding listener and works fine. But the grid itself is not repainting anymore. How can I repaint the out-of-focus dialog in real time?
Here is the code of the front panel:
public class MainDisplayForm extends javax.swing.JFrame {
Storage st = new Storage();
DisplayForm dF = new DisplayForm();
....
public MainDisplayForm() {
initComponents();
Btn_IncreaseGain.addActionListener(new ButtonListener_IncreaseGain());
}
....
} //MainDisplayForm ends here.
class ButtonListener_IncreaseGain implements ActionListener {
DisplayForm dF = new DisplayForm();
Storage st = new Storage();
ButtonListener_IncreaseGain()
{
}
public void actionPerformed(ActionEvent e) {
st.iGain = 20;
dF.revalidate();
dF.repaint();
System.out.println("Testing");
}
}//Listener ends here.
Here is code of Data Display:
public void paint(Graphics g)
{
g2 = (Graphics2D) g;
paintComponents(g2);
//added numbers are for adjustment.
int x = this.jPanel1.getX()+8;
int y = this.jPanel1.getY()+30;
int width = this.jPanel1.getWidth()+19;
int height = this.jPanel1.getHeight()+40;
//labelling voltages
label0.setText(st.zero);
label1.setText(st.v1);
label2.setText(st.v2);
label3.setText(st.v3);
label4.setText(st.v4);
label5.setText(st.v3);
label6.setText(st.v4);
g2.setColor(Color.darkGray);
for(int i=x; i<width; i=i+80)
{
g2.drawLine(i, y, i, height);
}
int j = 0;
for(int i=y; i<height; i=i+80)
{
j++;
//st.iGain
g2.setColor(Color.orange);
if(j==1)
{
double k1 = st.iGain * 0.4;
st.v1 = Double.toString(k1);
g2.drawString(st.v1, x+5, y+10);
}
if(j==2)
{
double k2 = st.iGain * 0.3;
st.v2 = Double.toString(k2);
g2.drawString(st.v2, x+5, y+90);
}
g2.setColor(Color.DARK_GRAY);
g2.drawLine(x, i, width, i);
....
} //grid info is not completed yet.
Thanks,
Focus isn't the issue and has nothing to do with your current problem. The solution is to change the properties of the data grid by updating fields it contains via setter methods and calling repaint on the JComponent (perhaps a JPanel, or some other component that derives ultimately from JComponent) held by the data grid. The paintComponent method of this component should use its class fields to update what it draws.
You almost never paint in the paint method of a JComponent and certainly you don't want to draw directly into a top-level window. You also probably don't want to set text of JLabels, JTextFields, or any other JTextComponent. from within paint/paintComponent.
I can't see why your code is not working and can only guess that the likely cause of your problem is in code not shown.
Edit 1:
Just guessing, but you may have a problem of references. I notice that your listener class creates new DisplayForm and Storage objects:
DisplayForm dF = new DisplayForm();
Storage st = new Storage();
There's a good possibility that these objects are not the ones being displayed, especially if you create these objects elsewhere and display them. Again I'm just guessing since I don't see the rest of your code, but perhaps you should to pass references for these objects into the DisplayForm via constructor or setter method parameters.
Edit 2:
e.g.,
public void setDisplayForm(DisplayForm dF) {
this.dF = dF;
}
// same for Storage
And in the main program:
public MainDisplayForm() {
initComponents();
ButtonListener_IncreaseGain btnListenerIncreaseGain = new ButtonListener_IncreaseGain();
btnListenerIncreaseGain.setDisplayForm(....);
btnListenerIncreaseGain.setStorage(....);
Btn_IncreaseGain.addActionListener(btnListenerIncreaseGain);
}

How does JButton implement its icons?

I'm trying to understand how Swings JButton/AbstractButton implements the painting of its icons (defaultIcon, disabledIcon, pressedIcon, etc.).
I have found the fields/getters/setters for said icons in AbstractButton, but apparently the assorted paint methods are inherited directly from JComponent. This begs the question how the icons are ever painted!? Obviously they are, but I could not find the code that does it.
Paiting of components in Swing is made by the Look-n-Feel. So if you want to find how the icon of a button is painted simply look the method paintIcon of the class BasicButtonUI. But each Look-n-Feel you use can provide different painting.
The icon in JButton is painted by the UI Class. And the UI class is defined by the Look and Feel. It is an implementation of javax.swing.plaf.ButtonUI.
JComponent.paint method get the ui property from the instance and invoke its update method (and is where the icon is painted). The UI of the instance is acquired by the UIManager.
You can se the paint method on Open JDK BasicButonUI
I hope I understand your question: are you trying to understand how does an icon get painted on an JButton? Take a look at the paint method from JComponent (its super super class). I'll add comments to make it easier to explain:
public void paint(Graphics g) {
boolean shouldClearPaintFlags = false;
//Check the size of the component: don't draw anything with a negative width or height!
if ((getWidth() <= 0) || (getHeight() <= 0)) {
return;
}
//Create new Graphics objects (why? to create the images)
Graphics componentGraphics = getComponentGraphics(g);
Graphics co = componentGraphics.create();
try {
RepaintManager repaintManager = RepaintManager.currentManager(this);
//Initialize a RepaintManager (JavaDoc says: "This class manages repaint requests, allowing the number of repaints to be minimized")
//Create a rectangle at the size of a graphics object
Rectangle clipRect = co.getClipBounds();
int clipX;
int clipY;
int clipW;
int clipH;
//If the rectangle is null, then give it default values
if (clipRect == null) {
clipX = clipY = 0;
clipW = getWidth();
clipH = getHeight();
} else { //otherwise, use its coordinates
clipX = clipRect.x;
clipY = clipRect.y;
clipW = clipRect.width;
clipH = clipRect.height;
}
//Ajust the clip widths and heights
if(clipW > getWidth()) {
clipW = getWidth();
}
if(clipH > getHeight()) {
clipH = getHeight();
}
//If your Component is placed on a JComponent (or extended class), then make adjustments
if(getParent() != null && !(getParent() instanceof JComponent)) {
adjustPaintFlags();
shouldClearPaintFlags = true;
}
//Check if the component is printing (private flag IS_PRINTING)
int bw,bh;
boolean printing = getFlag(IS_PRINTING);
//If the component is not printing its contents, and if the repain manager is double buffered, AND if not ancestor (subclass) is buffering AND if the components is buffered....
//JavaDoc says for RepaintManager.isDoubleBufferingEnabled(): "Returns true if this RepaintManager is double buffered. The default value for this property may vary from platform to platform."
if(!printing && repaintManager.isDoubleBufferingEnabled() &&
!getFlag(ANCESTOR_USING_BUFFER) && isDoubleBuffered()) {
//... then start painting the Graphics
repaintManager.beginPaint();
try {
repaintManager.paint(this, this, co, clipX, clipY, clipW, clipH);
} finally {
repaintManager.endPaint();
}
//if there is an exception, a try/finally is required to avoid the RepaintManager being "left in a state in which the screen is not updated" (JavaDoc)
}
else {
// Will ocassionaly happen in 1.2, especially when printing.
if (clipRect == null) {
co.setClip(clipX, clipY, clipW, clipH);
}
//Checks if the rectangle at the specified coordinates if obscured
if (!rectangleIsObscured(clipX,clipY,clipW,clipH)) {
//Then paint the graphics (or print if printing is true)
if (!printing) {
paintComponent(co);
paintBorder(co);
} else {
printComponent(co);
printBorder(co);
}
}
//Also paint the children (eg: a JPanel has a JLabel and a JButton as children if you add them to the panel) (or print if printing is true)
if (!printing) {
paintChildren(co);
} else {
printChildren(co);
}
}
} finally {
//Clean up!!
co.dispose();
if(shouldClearPaintFlags) {
setFlag(ANCESTOR_USING_BUFFER,false);
setFlag(IS_PAINTING_TILE,false);
setFlag(IS_PRINTING,false);
setFlag(IS_PRINTING_ALL,false);
}
}
}
In other words, there is a lot of code involved in painting Graphics objects, but the steps are pretty simple once you've analyzed the code:
Create new Graphics objects from the one passed in parameter;
Initialize a RepaintManager for repaint requests;
Check if the graphics can be painted, and do so if possible;
Finalize by repainting the children;
You're done!
If you want to know more on how the painting process is applied, then according to Oracle's Java documentation:
In Swing, painting begins with the paint method, which then invokes paintComponent, paintBorder, and paintChildren. The system will invoke this automatically when a component is first painted, is resized, or becomes exposed after being hidden by another window.
Programatic repaints are accomplished by invoking a component's repaint method; do not invoke its paintComponent directly. Invoking repaint causes the painting subsystem to take the necessary steps to ensure that your paintComponent method is invoked at an appropriate time.
You can invoke repaint multiple times from within the same event handler, but Swing will take that information and repaint the component in just one operation.
For components with a UI Delegate, you should pass the Graphics paramater with the line super.paintComponent(g) as the first line of code in your paintComponent override. If you do not, then your component will be responsible for manually painting its background. You can experiment with this by commenting out that line and recompiling to see that the background is no longer painted.
I took the code from here.
I hope I was able to help you,
Cheers

Categories

Resources