0

Background:

I'm tearing my hair here because I've used awt and Swing a couple of times, and I always run into a roadblock during my initial graphics setup and I never seem to have an "Of course, this thing again! I just have to something something"-moment. This time I can't seem to get even basic drawing to work!

Problem description:

I wish to draw a number of simple boxes in a JFrame (via a JPanel, if necessary). To this end I have a Sprite extends JComponent class which currently only draws simple geometric shapes but will eventually do things with BufferedImageor somesuch.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.JComponent;

 @SuppressWarnings("serial")
 public class Sprite extends JComponent {

    private Rectangle rctBounds;
    private Color clrFill;

    public Sprite(int x, int y, Color color) {
        this.rctBounds = new Rectangle(x, y, 100, 100);
        this.clrFill = color;
    }// Sprite(int,int,Color)

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(clrFill);
        g.fillRect(rctBounds.x, rctBounds.y, rctBounds.width, rctBounds.height);

    }// paintComponent(Graphics)

}// Sprite.class

The window for these to go in is obtained by a simple Win extends JFrame class á:

import javax.swing.JFrame;

public class Win extends JFrame{

    public Win(){
        setSize(800,600);
        setLocation(100,100);

        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

}

Finally, components are initialized and connected in the main method, in the class run, using JComponent's (I believe) add() method, called on an instance of Win:

import java.awt.Color;

public class run {

    public static void main(String[] args) {

        Win w = new Win();

        Sprite s1 = new Sprite(10,100, Color.BLUE);
        Sprite s2 = new Sprite(200,200, Color.RED);
        Sprite s3 = new Sprite(300,200, Color.CYAN);


        w.add(s1);
        w.add(s2);
        w.add(s3);  

    }//main()

}//run.class

Expectations vs. Reality:

Now, I expect a window with three colored boxes, as in this image (all boxes visible). Instead, only the final JComponent to be added shows up, as seen here (only cyan box painted). What's even more infuriating is that attempting to edit the JFrame's layout or adding a JPanel as an intermediary container for the Sprites results in a blank window.

Exasperated Plea:

I'm at a complete loss! Everything I find on here talks about improper use of paintComponent(Graphics) (using paint() instead, forgetting to @Override, not propagating the graphical context properly, etc) or to use a JPanel as an intermediary container. The most infuriating part of all this is that I've built two functioning programs using Swing before (school assignments) and, looking at that code, I cannot for the life of me figure out what I'm doing wrong here! Please help!

a_neobum
  • 1
  • 1
  • 2
    Are you familiar with using a layout manager? – Dawood ibn Kareem Jun 22 '17 at 01:46
  • https://stackoverflow.com/questions/8660751/java-swing-jframe-layout has the answer to this. Look at Robin's answer (Ibrahim's answer is fine but Robin's actually explains the problem). – Cameron Skinner Jun 22 '17 at 01:47
  • 1
    So, most Swing components have a concept of layout management, which makes decisions about where and what size components should be, in your case, that `JFrame` defaults to `BorderLayout`, but since your `Sprite` class doesn't define any sizing hints, it's default to `0x0`. I'm wondering if this is really the best approach, perhaps you should define your own "shape" class which just has a "paint" method and which can then painted by you own custom container class – MadProgrammer Jun 22 '17 at 01:48
  • @Dawood ibn Kareem Yes. Yes, I am. As I mentioned in my original post (albeit not explicitly enough): I have had no success with setting up different LayoutManagers - any attempt at using setLayoutManager() results in a blank window. – a_neobum Jun 22 '17 at 02:02
  • Also, check in the your code the coordinates. In you input x and y, but in new Rectangle only x: public Sprite(int x, int y, Color color) { this.rctBounds = new Rectangle(x, x, 100, 100); this.clrFill = color; }/ – Vasyl Lyashkevych Jun 22 '17 at 02:07
  • @Cameron Skinner The post you link to is one of the posts I refer to when saying "everything I find on here". I did not find a solution to my problem in that post. I went through several Stackoverflow posts before creating an account and making my own post. As previously mentioned, attempts at using a different LayoutManager, or adding the individual components to different sections of the default layout (one in NORTH, one in EAST, etc) results in a blank window. – a_neobum Jun 22 '17 at 02:09
  • @MadProgrammer I had the same thought, but no packing takes place and everything's more than roomy enough to fit my sprites. Additionally, if that was the problem, none of the components should have been drawn. – a_neobum Jun 22 '17 at 02:11
  • @Vasyl Lyashkevych Thanks, just noticed that. It does, however, have no bearing on the problem at hand whatsoever. – a_neobum Jun 22 '17 at 02:12
  • *"Additionally, if that was the problem, none of the components should have been drawn."* Wrong. In some layout managers & constraints (e.g. `BorderLayout` + `CENTER`) the component will be stretched to available width. In others (e.g. flow layout) the default size of the component will be honored. But you seem to be missing the best strategy suggested by @MadProgrammer - 'custom painting'. This problem really does not sound like it should be using components for the rectangles at all. – Andrew Thompson Jun 22 '17 at 02:18
  • @Andrew Thompson And yet using BorderLayout + CENTER (both explicitly setting the layout and using the default, both explicitly adding components to CENTER and using the default single-argument add) does not stretch the image and FlowLayout seems to do nothing. As for using components for the rectangles: the rectangles are arbitrary and will be replaced. Would you care to elaborate on this whole 'custom painting' bit? – a_neobum Jun 22 '17 at 02:27
  • 1
    @a_neobum Adding components to the window after it's been made visible may not paint them either – MadProgrammer Jun 22 '17 at 02:42
  • @a_neobum I took your code and ran it, nothing appeared, I resized the window and the last component was painted - which is what I'd expect. If you add components to a window which is already visible on the screen, you need to call `invalidate` and `repaint` to trigger a layout and paint pass – MadProgrammer Jun 22 '17 at 03:16
  • *"does not stretch the image"* ***What*** 'image'? I see no reference to images anywhere in that code. You also consistently seem to be completely ignoring the advice to custom paint. Why are you wasting your (and more importantly *my*) time? With the attention of me and @MadProgrammer, you have the (temporary) attention of two people who are ranked in the [top 10 providers of Swing answers](https://stackoverflow.com/tags/swing/topusers). Why waste that by pissing about with trying to flog a dead horse? – Andrew Thompson Jun 22 '17 at 03:44
  • @a_neobum *"As for using components for the rectangles: the rectangles are arbitrary and will be replaced. Would you care to elaborate on this whole 'custom painting' bit?"* - That worries me, because you pay no additional to the actual size of the component, you just paint what you regardless. Remember, components have their own space context, that is, `0x0` is the top left corner of the component within ITS context, it may have a position within the parent container's space, but that they have seperate meanings – MadProgrammer Jun 22 '17 at 03:48
  • @MadProgrammer Yes, I am aware of this. However: 1) all that happens is that a graphical context received by my Sprite (a JComponent) from its containing JComponent (in this case a JFrame) is told to set itself some active color, and fill a rectangle of some width and height at some xy-coordinate. _This all happens!_ 2) setPreferredSize should only matter when a JComponent's containing JComponent's formatting is intended to adjust automatically based on its content - this is not the case here. – a_neobum Jun 22 '17 at 05:24
  • @a_neobum " setPreferredSize should only matter when a JComponent's containing JComponent's formatting is intended to adjust automatically based on its content - this is not the case here"* - Which is an assumption which is wrong - If you use a layout manager (which you are), then you will need to support `getPreferredSize` at a minimum in order for your components to be made visible in any meaningful manner. This would suggest you first components needs a preferred size of `110x200` before you'd see anything painted – MadProgrammer Jun 22 '17 at 05:27
  • @a_neobum But, because you're using a `BorderLayout`, you'd probably only see the last component which was added - after you invalidated the container as I've already mentioned – MadProgrammer Jun 22 '17 at 05:30
  • @Andrew Thompson You're right: there is no image. What I meant was that the image which I see on the screen (a code-generated one) appears not have been subject to any transformations. Could I persuade you to throttle back your own pissynes and offer some constructive advice? I mean, I know being a programmer does things to one's ego, but... sheesh, if it upsets you so much, perhaps find something better to do with your time than wasting it on giving me attention? Life's too short to waste on helping bumbling idiots such as I. – a_neobum Jun 22 '17 at 05:33
  • @MadProgrammer _Now_ we're getting somewhere! So are you telling me that, because my Sprite got its graphical context from its containing JFrame, and because I've said nothing else, the dimensions of my Sprite end up taking up the whole frame (or at least everything from the JFrame's 0,0 to it's own max,max), thus effectively drawing blank space on top of my previously added components? – a_neobum Jun 22 '17 at 05:37
  • @MadProgrammer I should mention that the information about the BorderLayout's CENTER only being able to hold one component was very useful, but as _I_ mentioned earlier, this realization didn't help me as I've had no luck with attempting to add my Sprites to separate "quadrants" (as it were), nor with using other managers. – a_neobum Jun 22 '17 at 05:39
  • @a_neobum It's got nothing to do with the `Graphics` context (that will be clipped to the bounds of your component when it's painted), but, as has already been stated, yes, `BorderLayout` will, by default, give all the remaining space of the window to the component at the (default) `CENTRE` position, but since only one component can be displayed at any of the 5 available positions, you'll only see the last one added (after the container is invalidated) – MadProgrammer Jun 22 '17 at 05:39
  • @a_neobum *"I've had no luck with attempting to add my Sprites to separate "quadrants" (as it were), nor with using other managers"* - This comes down to your components having a default preferred size of `0x0`. Only the center component of `BorderLayout` will be unaffected by this, as `BorderLayout` will give to all the remaining space and since you've not used `pack` on the window, `BorderLayout` is not been asked to make decisions about the preferred size it needs to comfortably layout the components – MadProgrammer Jun 22 '17 at 05:42

0 Answers0