Limit JButton repaint to state changes? - java

After what I learned from my previous question, I would like to use a texture to paint the text on an extended JButton while it's being pressed. The first step was setting up the button's ChangeListener and stateChanged method; I have these working and can set the foreground color in the method, so that the text will be one color while the button's pressed and another while not.
Building on this, I replaced the setForeground call with the drawString code I used for my toggleButtons. It works, but immediately after the text is drawn it's overwritten by the button being automatically repainted. I tried throwing the code in a "while (model.isPressed())" loop, but that had some pretty terrible results (system hang). How would I go about manually repainting my button, so that it's only redrawn during the stateChanged method?

It seems to me that you are going the wrong way to change the look of your button. I think it would be easier for you to create a class that would handle the look and feel of your button, instead of manually handling the drawing parameters of your button inside the button's code. Blocking the repaint() calls isn't really the way to go I believe in your case.
I would personally create my own ButtonUI implementation that would handle all the paint rules (foreground color based on button state for instance), then I would call the setUI on the button, specifying an instance of this new ButtonUI as a parameter. If you don't want to handle all the drawing stuff, you can always use your new class as a proxy to the button's already existing UI handler (through JButton's getUI() method), and make changes only where you need them (I haven't tested it myself, but I'm pretty sure it would work just fine).
Of course, this represents more coding for you, but it would localize your look and feel handling in a single class, and it would fit in Swing's way of working. There are a few resources on the web to get you started (here, here and here).

Related

How to generate SWT paint event at startup

I have a SWT application which displays and sorts image files by renaming the file. Scruffy I know, but the sorted-by-name files are the input for a later step that I cannot change.
So far I have the UI almost working but the initial image at startup does not generate a paint event, so the Canvas is blank. If I minimally resize the window, it immediately appears correctly. I am trying (perhaps incorrectly) to generate the event explicitly using the notifyListeners() method of Canvas which needs to be passed a PaintEvent and I am stuck on how to instantiate one.
What is the correct way to force this initial event to be generated?
You don't have to explicitly fire a PaintEvent. Instead use one, or combination, of the below options:
layout() marks forces the repositioning of all composite-children.
This will become visible on next repaint, which will be done
somewhere in the future, when composite's screen-area will be redrawn
redraw() marks the widget an invalidated. On next
redraw-system-action this area will be repainted.
update() forces all outstanding redraw() requests to be completed
NOW.

Design for a component with 2 listeners and 2 "setters" (Swing application)

I'm making a map editor using java swing for my tile based java game. the swing application has two major components, the "upper" component is the game map preview, and the "lower" component is modifyable properties of the map, like its height and width.
Currently the user types in to a jtextfield for the map width, then I use a change listener to set that value to the GameMap object. The GameMap object when changed fires a notification event to GameMapListeners, the primary listener it has is the preview display of the map inside the swing application.
This lets the user change the map width and instnatly see the results in a preview pane.
Now I want to go to the other way. I want the user to be able to click and drag the edges of the map in the preview pane, but then the results need to then be sent to the properties panel so it shows the updated width value.
This is where the problem is, if I update the jtextfield it'll fire a change event, which would update the GameMap and update the preview display, and then that would fire an event that changes the jtextfield again (so on and on until the program crashes due to stack overflow)
Are there any kind of design patterns i could use instead, or is there some common way to solve this issue?
In this type of case, you have at least two choices...
You Could
Remove the listener to the other component when you want to trigger a change, adding it back after you've raised the event...
You Could
Change the state of a flag to indicate that you should ignore any changes that might come in, resetting after you're raised the event...
Which one you choose will depend on how much code you want to add and how readily available the reference to the listeners in question are (ie, if you don't have a reference to the listener you want to remove, it's kind of hard to implement)
If I update the jtextfield it'll fire a change event, which would update the GameMap and update the preview display, and then that would fire an event that changes the jtextfield again (so on and on until the program crashes due to stack overflow).
When you have a situation like this, you can temporarily remove event listeners, fire the change event, and add the event listeners back. Yes, this is as much of a pain as it sounds, but it's a good way to prevent the stack overflow.
You can see a detailed explanation as well as a working example of managing event listeners in my Sudoku Solver Swing GUI article.
You can use action events for a JTextField. Action events don't trigger when you change the component programmatically.

Should i redraw the GUI after change?

I have built a GUI displaying a matrix. It looks much like in excel where you have labels on the first row and first column. The cells contains either 1 or 0.
There's a JComboBox below the matrix. I can select an item from the JComboBox and click a button "add". This adds an extra row to the matrix with the JComboBox item as its name. My question is how i should handle expanding this.
Is it a good idea to have a method that redraws the whole window? Or should i try and just redraw the part that's been changed?
I thought of having a method like updateWindow() that could be used both for initiating the window and updating it if i make changes.
Is it a good idea to have a method that redraws the whole window? Or should i try and just redraw the part that's been changed?
It depends on what's in your window.
If you're drawing on a JPanel, by overriding the paintComponent method, redraw the entire JPanel. It's not worth the effort to try and redraw a part of a JPanel.
If you have a window made up of many JPanels, you can redraw just the JPanel with the changes.
I thought of having a method like updateWindow() that could be used both for initiating the window and updating it if i make changes.
It's generally a good idea to create methods to perform specific GUI tasks. I'd have separate initializeWindow and updateWindow methods for my own sanity. I usually separate the initialization of my GUI from the update of my GUI.
Read this excellent article, Sudoku Solver Swing GUI, to get an idea of how to put together a Swing GUI.
When you add components to a container, you will be invalidating that container's layout. This will automatically trigger a repaint, so the question becomes moot.
The real question becomes why?
Instead of messing about with labels and fields, you should just simply use a JTable. Check out How to use Tables.
This is optimized for performance, so if you're really concerned, this should provide a better solution, so long as you are firing the correct events to the required changes.
Unless you're finding yourself performance bound, I see no real reason not to redraw the entire window; sure there will be a performance hit but it should be negligible and your source will be simpler and easier to maintain. However, if you're finding yourself pressed for performance, I would suggest looking into implementing a "dirty rectangles" method of redrawing (i.e. your second approach).

Manage multiple Jlabel's events

I have a left Panel with multiples Jlabels which i use them as buttons to change a Main Panel's content which is layouted with a CardLayout.
I cant work perfectly with these events:
mouseEntered : to make highlight effect to the jlabel
mouseExited : to take off the highlight effect.
mouseClicked : to change the content of the main Panel and start some threads
The problem here that can't found an event or a method tell me that another Jlabel has been clicked so i can stop my threads started in the mouseClicked event,
OR
an event or method tell me that a JPanel in the CardLayout has been displayed or hidden.
Your problem is not finding an appropriate event. I think you are doing this using a visual GUI builder and expect to solve everything out-of-the-box. It's not going to work that way, you will need to write some real code. For example, write a method that you will call from the mouse click listener of each of the three JLabels. Thus you will have arranged for this method to be called for each JLabel click. Then in the method do the appropriate handling. This is just a rough outline, you haven't provided much detail to give any further advice.
It sounds like you need FocusEvents and FocusListeners. These are supported by all JComponents like JPanel, JLabel, and JButton, such as by calling addFocusListener();
Basically a FocusListener can tell you when a JComponent gains focus (such as by clicking on the JComponent) and when it looses focus (such as by clicking on a different JComponent).
Refer to http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/event/FocusListener.html for further information

Who is calling paintComponent?

For some reason, my paintComponent(Graphics g) method is being called infinitely. I can't seem to tell who is calling it, even if I dump a StackTrace in the call (it's an event that's dispatched and handled).
Are there any easy ways to find out who's triggering the event?
Update: I've found the cause and I now understand the reason. Whoever answers it correctly will get the answer to the question.
Here is the code that's causing the issue:
#Override
public void paintComponent(Graphics g)
{
myJButton.setIcon(ResourceLoader.getImageIconWithLocale(MY_BUTTON_IMAGE));
super.paintComponent(g);
}
FYI: It's a really tricky one!! It's not obvious by looking at the code. I made an assumption that was wrong.
I don't know which component this is, but setting an icon on a button from within a paint routine is a bad idea. It will definitely cause the button to be repainted. If the button is a child of your component then setting the button invalidate the component too causing an infinite loop.
Set the icon somewhere else such as where the dialog / window is set up initially.
The setIcon(ImageIcon) will revalidate and repaint itself ONLY if the ImageIcon is another instance.
When working with Locales, most people are use to the ResourceBundle, which returns Strings, which in turn are immutable. Therefore setting the text over and over doesn't matter.
However, in this case, the ResourceLoader (custom class) returned a new instance of an ImageIcon. Sure it was the same Image, but it was another instance. And if you decompile the code, you'll see that setIcon (at least for JButtons), it will repaint and revalidate if newIcon != oldIcon.
The solution was to use a HashMap in the ResourceLoader, this way it saves from loading the images more than once since most of the images are used very frequently (might as well reuse the instances if you can). Turns out that overall this quick adjustment also saved a decent amount of overall memory consumption as an added bonus.
Are you calling repaint() anywhere? Also, when a window becomes visible (uncovered or deminimized) or is resized, the "system" automatically calls the paintComponent() method for all areas of the screen that have to be redrawn.
The problem is that you are setting the icon in the paintComponent() method. You should never set a property in this method.
Swing components are smart enough to repaint themselves whenever a property changes. In this case you have the problem of the component repainting itself because the Icon changes, but you are also rereading the Icon every time the component repaints itself which is also not very efficient.

Categories

Resources