I am using a JTabbedPane in my application. I have added two tabs which are instances of a custom class "ContentPanel". This extends JPanel and sets the background, border etc etc. Basically it means I dont have to set the properties of each JPanel I want to apply this colour scheme to. I notice that not only does their border appear but another border (which, I think, is blue - at least on my screen) appears around this border, connected to the tab "selectors" themselves (i.e. the buttons you click on to get the appropriate view). I would like to change this border as it just looks odd against a gold / brown colour scheme. Does anyone have any idea how to do this? I have tried JTabbedPane.setBorder(Border b) but that doesnt work. That simply sets a border around the entire thing, including the tab selectors.. not what I want.
Any help with this would be greatly appreciated.
These colors are defined in the Look and Feel. If you look at the code for BasicTabbedPaneUI, you will notice that installDefaults() sets a bunch of protected Color instance variables. The keys they are defined against in the L&F are also available here.
protected void installDefaults() {
LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
"TabbedPane.foreground", "TabbedPane.font");
highlight = UIManager.getColor("TabbedPane.light");
lightHighlight = UIManager.getColor("TabbedPane.highlight");
shadow = UIManager.getColor("TabbedPane.shadow");
darkShadow = UIManager.getColor("TabbedPane.darkShadow");
//...
// a lot more stuff
//...
}
If you do not want to go as far as define your own L&F, you have the ability to set a custom UI delegate on your tabbed pane:
myTabbedPane.setUI(new BasicTabbedPaneUI() {
#Override
protected void installDefaults() {
super.installDefaults();
highlight = Color.pink;
lightHighlight = Color.green;
shadow = Color.red;
darkShadow = Color.cyan;
focus = Color.yellow;
}
});
you may of course want to change those color settings. As set, you will see which vars are used where.
None affecting L&F and JVM run-time system-wide settings code solution.
Create your own tabbed-pane class and nested tabbed-pane-UI class to deal with the issue for a "specific" class of tabbed-pane. The code below is original: (The last answer was 2010, but this may be useful too.)
public class DisplayTabbedPane extends JTabbedPane implements
MouseListener, ChangeListener {
public DisplayTabbedPane() {
setTabPlacement(SwingConstants.BOTTOM);
// UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0));
// works but is a JVM system wide change rather than a specific change
NoInsetTabbedPaneUI ui = new NoInsetTabbedPaneUI();
// this will build the L&F settings for various tabbed UI components.
setUI( ui );
// override the content border insets to remove the tabbed-pane
// blue border around the pane
ui.overrideContentBorderInsetsOfUI();
}
/**
* Class to modify the UI layout of tabbed-pane which we wish to override
* in some way. This modification only applies to objects of this class.
* Doing UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0));
* would affect all tabbed-panes in the JVM run-time.
*
* This is free to use, no copyright but is "AS IS".
*/
class NoInsetTabbedPaneUI extends MetalTabbedPaneUI {
/**
* Create tabbed-pane-UI object to allow fine control of the
* L&F of this specific object.
*/
NoInsetTabbedPaneUI(){
super();
}
/**
* Override the content border insets of the UI which represent
* the L&F of the border around the pane. In this case only care
* about having a bottom inset.
*/
public void overrideContentBorderInsetsOfUI(){
this.contentBorderInsets.top = 0;
this.contentBorderInsets.left = 0;
this.contentBorderInsets.right = 0;
this.contentBorderInsets.bottom = 2;
}
}
........
}
Change Look And Feel with "UIManager"
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Enabled].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Enabled+MouseOver].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Enabled+Pressed].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Focused+MouseOver+Selected].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Focused+Pressed+Selected].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Focused+Selected].backgroundPainter", new BackgroundPainter(Color.GRAY));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[MouseOver+Selected].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Pressed+Selected].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Selected].backgroundPainter", new BackgroundPainter(Color.white));
BackgroundPainter class
public class BackgroundPainter implements Painter<JComponent> {
private Color color = null;
BackgroundPainter(Color c) {
color = c;
}
#Override
public void paint(Graphics2D g, JComponent object, int width, int height) {
if (color != null) {
g.setColor(color);
g.fillRect(0, 0, width - 1, height - 1);
}
}
}
Related
I'm new here but I did some research before posting. My goal is to create a simple tower defense game using a couple of interesting ideas and moreover to train my development skills using javax.swing and java.awt. As far as I know, developers are mostly lazy guys and they do everything to make their life more simple.
There is map with a grid and for map loading my game uses a boolean matrix and a loading method to locate terrain on panels. I thought it will be quite simple solution. Because the matrix is 12 x 12, I would like to create it with some other application rather than entering a line of 144 numbers.
Here comes an idea to first create a map editor application and later do maps for levels in it. When I have such a tool I could make that map visually and then save its boolean matrix to a file, which later can be read by loading method and recreated in game. Next step is to make graphics and also panels that would react properly on user's actions. On the left there is a panel with buttons - after user clicks one of them, the field currentColor changes.
This field is used by method that implements actionListener and makes color change of the panel that is declared in its constructor. I wanted to change color of certain panel when its clicked. I use colors because its easier for now to make it working, later I want to replace color with a texture - obviously, I know I have to use a paintComponent method, but I assume that will work for it too, right? Also would be nice if the panel border changes color when I move my cursor over it and changes it back to normal when mouse is somewhere else.
The point here is that I'm having some trouble to make panels interactive. First problem is that panels are created in for loop and that makes it difficult to refer to a certain panel while mouse is over it. Another one comes with that I would like to change appearance of that panel after I click on it.
As far as I know, MouseListeners should do the work, but how to actually write it to have an effect on screen? I found some post about that, but for me it doesn't work. Here's the link: highlighting panels in java
My code:
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class Editor extends JFrame
{
private JButton towers = new JButton(new ImageIcon("pu.gif"));
private JButton road = new JButton(new ImageIcon("pu.gif"));
private JButton start = new JButton(new ImageIcon("pu.gif"));
private JButton finish = new JButton(new ImageIcon("pu.gif"));
private String mapTitle = "testmap";
private Color currentColor;
private int width = Toolkit.getDefaultToolkit().getScreenSize().width;
private int height = Toolkit.getDefaultToolkit().getScreenSize().height;
private String currentMapType = "Standard";
private static final int currentHeight = 12;
private static final int currentWidth = 12;
private JPanel[][] currentMapPanel;
private int[][] currentMapField;
//Toolbar - a panel with buttons
private JPanel panel = new JPanel(new GridLayout(10,3));
//Container for map - a panel with map
private Dimension containerSize = new Dimension(height, height);
static JPanel container = new JPanel(new GridLayout(currentHeight, currentWidth), true);
//Separator
private JSplitPane separator = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel, container);
public Editor()
{
initComponents();
}
public void initComponents()
{
this.setTitle(mapTitle + ".map" + " - " + "Game Map Editor");
this.setSize(800, 600);
int frameWidth = this.getSize().width;
int frameHeight = this.getSize().height;
this.setLocation((width - frameWidth) / 2, (height - frameHeight) / 2);
this.setIconImage(Toolkit.getDefaultToolkit().getImage("pu.gif"));
towers.addActionListener(e -> {
currentColor = Color.CYAN;
System.out.println(currentColor);
});
road.addActionListener(e -> {
currentColor = Color.GRAY;
System.out.println(currentColor);
});
start.addActionListener(e -> {
currentColor = Color.LIGHT_GRAY;
System.out.println(currentColor);
});
finish.addActionListener(e -> {
currentColor = Color.BLACK;
System.out.println(currentColor);
});
new Map(currentMapType, currentWidth, currentHeight, false);
panel.add(towers);
panel.add(road);
panel.add(start);
panel.add(finish);
this.getContentPane().add(separator);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
* Class that allows to load the graphic map and to view it in JFrame
*/
public class Map
{
public Map(String mapType, int rows, int columns, boolean load)
{
if (!load)
{
currentMapPanel = mapPanel(rows, columns);
currentMapField = new MapGenerator().mapFieldEmpty(rows, columns);
mapLoader(currentMapField, currentMapPanel);
}
else
{
currentMapPanel = mapPanel(rows, columns);
currentMapField = new MapGenerator().mapFieldGenerator(rows, columns);
mapLoader(currentMapField, currentMapPanel);
}
}
private JPanel[][] mapPanel(int rows, int columns)
{
JPanel[][] mapPanel = new JPanel[rows][columns];
for (int i = 0; i < rows - 1; i++)
{
for (int j = 0; j < columns - 1; j++)
{
mapPanel[i][j] = new JPanel(true);
mapPanel[i][j].setPreferredSize(new Dimension(height/12, height/12));
mapPanel[i][j].setBorder(new LineBorder(Color.BLACK));
mapPanel[i][j].addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
super.mouseEntered(e);
JPanel parent = (JPanel) e.getSource();
new colorListener(parent, Color.LIGHT_GRAY);
parent.revalidate();
}
#Override
public void mouseExited(MouseEvent e)
{
super.mouseExited(e);
JPanel parent = (JPanel) e.getSource();
new colorListener(parent, Color.GREEN);
parent.revalidate();
}
#Override
public void mouseClicked(MouseEvent e)
{
super.mouseClicked(e);
JPanel parent = (JPanel) e.getSource();
new colorListener(parent, currentColor);
parent.revalidate();
}
});
}
}
return mapPanel;
}
private void mapLoader(int[][] mapField, JPanel[][] mapPanel)
{
for (int i = 0; i < mapField.length - 1; i++)
{
for (int j = 0; j < mapField.length - 1; j++)
{
if (mapField[i][j] == 0)
{
mapPanel[i][j].setBackground(Color.GREEN);
container.add(mapPanel[i][j]);
}
else if (mapField[i][j] == 1)
{
mapPanel[i][j].setBackground(Color.GRAY);
container.add(mapPanel[i][j]);
}
else if (mapField[i][j] == 2)
{
mapPanel[i][j].setBackground(Color.LIGHT_GRAY);
container.add(mapPanel[i][j]);
}
else if (mapField[i][j] == 3)
{
mapPanel[i][j].setBackground(Color.BLACK);
container.add(mapPanel[i][j]);
}
else
{
System.out.println("An error occurred...");
}
}
}
}
private JPanel mapContainer(int rows, int columns)
{
container = new JPanel();
container.setLayout(createLayout(rows, columns));
container.setPreferredSize(containerSize);
container.setBounds(height/4, height/4, containerSize.width, containerSize.height);
return container;
}
private GridLayout createLayout(int rows, int columns){
GridLayout layout = new GridLayout(rows, columns);
return layout;
}
}
private class colorListener implements ActionListener
{
public colorListener(JPanel p, Color c)
{
this.panel = p;
this.color = c;
}
#Override
public void actionPerformed(ActionEvent e) {
panel.setBackground(color);
}
JPanel panel;
Color color;
}
public static void main(String[] args) {
new Editor().setVisible(true);
}
}
The question is broad and the answer complicated.
Essentially, you want to do some research into concepts such as "separation of responsibilities" and "decoupling code".
The idea is that you break down you functionality requirements so that your objects are doing a single, specialised job. You also "decouple" the code so that changing the implementation of one part won't adversely affect other parts of the program. This is commonly achieved through the use of interfaces.
You will also want to investigate the concept of "model-view-controller", where by the "data" or "state" is modelled in one or more classes, but is wholly independent of the UI. The UI is then free to "render" the model in what ever way it feels is appropriate.
In this way, the "view" (interacting with the controller) can change the state (or react to the change in state) of the model, making it easier to mange (no seriously, it does)
Code Review ...
This...
static JPanel container = new JPanel(new GridLayout(currentHeight, currentWidth), true);
is dangerous and a bad idea. It voids the concept of encapsulation and allows any one to create new instance of container at any time, without notification, which will disconnect it from what the program was previously using. In fact, you actually do this.
static is not your friend. Used correctly, it's useful, but used in this way, it's just a bad idea and should be avoid.
You should instead favour "dependency injection", where the "elements" that any one object relies on are passed to it.
I would avoid things like...
this.setSize(800, 600);
int frameWidth = this.getSize().width;
int frameHeight = this.getSize().height;
this.setLocation((width - frameWidth) / 2, (height - frameHeight) / 2);
Windows are complicated components, which also contain window decorations which wrap about the content. This means that the available space to the content is window size - window decorations. Instead. You should rely on the layout manager API to provide appropriate sizing hints and pack the frame.
On most modern OSs you have "other" system elements, which, again, reduces the amount of available space on the screen (docks, task bars, other funky stuff). Instead, you can use setLocationRelativeTo(null) to centre the window more reliably on the screen.
Instead of setIconImage, you should be using Window#setIconImages(List), which allows you to pass a number of images which can be used by the API to represent the application in different places that require different resolution images.
Not sure what ...
new Map(currentMapType, currentWidth, currentHeight, false);
but it's not really helping.
If you find yourself just creating an instance of class without actually maintaining a reference to it, then it's probably a good sign of a bad design.
Your Map class raises a bunch of questions which aren't easily answered. It kind of makes me worried that the Map class is modifying the state of the parent class and screams "dependency injection" instead.
This...
mapPanel[i][j].setPreferredSize(new Dimension(height / 12, height / 12));
is best avoided. You should prefer overriding getPreferredSize and it should simply return a "desired" size, which could then be used by things like GridLayout to layout the component more effectively.
This then leads into the "separation of responsibility". This section suggestions you should have a "tile" class, which would be self managed and responsible for a single element from the model.
There are a number of things wrong with your mouse event handling...
#Override
public void mouseEntered(MouseEvent e) {
super.mouseEntered(e);
JPanel parent = (JPanel) e.getSource();
new colorListener(parent, Color.LIGHT_GRAY);
parent.revalidate();
}
You shouldn't be calling super.mouseXxx(e) on of the jobs of those methods is to call the delegate MouseListeners, so, mess right there.
You can more easily use e.getComponent() to get a reference to the component which generated the event, but if panel was a self contained unit of work (ie Tile) and the MouseListener an anonymous or inner class, you'd be able to forego the cast altogether.
new colorListener(parent, Color.LIGHT_GRAY); scares me as it's setting up a bunch of strongly references objects which can't be easily dereferenced, nor am I clear on there intent.
parent.revalidate(); isn't doing what you seem to think it's doing.
revalidate generates a new layout pass, what you seem to want is repaint.
These...
container.setPreferredSize(containerSize);
container.setBounds(height / 4, height / 4, containerSize.width, containerSize.height);
are just bad ideas. Let the content of the container, along with the layout manager deal with.
So, the short answer is, you have a lot of research left to do, things like:
OO design patterns
OO good practices, including "separation of responsibilities", "code decoupling" and in a more general sense, "dependency injection"
Model-View-Controller, coding to interface instead of implementation
just to name a few
I've created a super class (ImagePanel) which extends JPanel and paints an image as the background. In my ImagePanel subclass I'm using GroupLayout (via the NetBeans GUI Designer) to overlay the panel with JTextFields which are aligned with the underlying image.
This approach works as intended on a single platform; however, when I run the application on a different platform, the JTextFields are resized/moved based on the Look and Feel. If I set the layout manager to null the JTextFields remain in the appropriate position, but I lose the resizing of the JTextFields. Ideally, I would like to keep the position of the JTextFields, but have them sized according to the L&F? How can I approach this differently?
/**
* Extends JPanel adding the ability to paint a background image.
*/
public class ImagePanel extends JPanel implements Serializable
{
public static final String PROP_IMAGEFILE = "imageFile";
//~--- fields -------------------------------------------------------------
private ImageIcon imageIcon;
private String imageFile;
/**
* Constructs a new ImagePanel.
*/
public ImagePanel()
{
// required by Beans specification.
}
/**
* Get the path to the image file used to paint the background.
*
* #return the path.
*/
public String getImageFile()
{
return imageFile;
}
/**
* Set the path to the image file used to paint the background.
*
* #param imageFile the image file path.
*/
public void setImageFile(String imageFile)
{
String oldImageFile = this.imageFile;
this.imageFile = imageFile;
imageIcon = new ImageIcon(getClass().getResource(imageFile));
firePropertyChange(PROP_IMAGEFILE, oldImageFile, imageFile);
}
/**
* Overridden to draw image background image.
*/
#Override
public void paintComponent(Graphics g)
{
/* Draw image on the panel */
super.paintComponent(g);
if (imageIcon != null)
{
/* create image icon to get image */
Image image = imageIcon.getImage();
if (image != null)
{
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
}
}
}
On Windows:
On Linux:
I am not sure how well compound layouts will work in this case and it might very well be one of the 1% of cases where you do need a null layout (though that should be avoided whenever humanly possible). as mentioned, miglayout might work with with a little playing around, but what you might have to do is hard code ratio values rather than position values. Calculate the percentage of the image that represents the location and size of each component and, after the image has been drawn, and use those ratio values to programatically lay out your components.
A new layoutmanager could be written to accomplish the same thing (and would probably be preferred to the null layout method above. The add(); method could take 5 variables (component, ratio value for location x, ratio value for location y, size value for locaton x, size value for locaton y);. I am not well versed as to how to write a layout manager, but it is an option.
I've got a project based around the Model-View-Controller paradigm, and I've been having a lot of trouble with getting it to work properly.
The program has 4 panels, which are supposed to allow me to modify an oval drawn on the screen in various ways. These seem to work fine, and after considerable trouble I was able to get them to display in the JFrame which holds the whole shebang. I've managed to get them to display by breaking away from the provided instructions, but when I do that, I can't seem to get the oval to update. However, if I follow the directions to the letter, I only ever see an empty frame.
The project had pretty specific directions, which I followed up to a point, but some of the documentation was unclear. I think what I'm missing must be something simple, since nothing is jumping out at me as not making sense. I have to admit though that my Java experience is limited and my experience with GUI design/paradigms is even more so.
Anyway, I've been searching the web and this site extensively trying to figure out what's wrong, but this is a somewhat specific example and honestly I just don't know enough about this to generalize any of the answers I've found online and figure out what's missing. I've been poring over this code for far too long now so I'm really hoping someone can help me out.
public class Model {
private Controller controller;
private View view;
private MvcFrame mvcFrame;
private int radius = 44;
private Color color = Color.BLUE;
private boolean solid = true;
//bunch of mutators and accessors for the above variables
public Model() {
controller = new Controller(this);
view = new View(this);
mvcFrame = new MvcFrame(this);
}
}
Here's the model class. This seems to be fairly simple. I think my understanding of what's going on here is solid, and nothing seems to be wrong. Included mostly for context.
public class Controller extends JPanel{
private Model model;
public Controller(Model model) {
this.model = model;
setBorder(BorderFactory.createLineBorder(Color.GREEN));
setLayout(new GridLayout(4,1));
add(new RadiusPanel(model));
add(new ColorPanel(model));
add(new SolidPanel(model));
add(new TitlePanel(model));
}
}
This is the Controller class. As far as I can tell, the setBorder, setLayout, and series of adds do nothing here. I had them commented out, but this is the way that the instructions told me to do things, so either there's a mistake there or something about my setup is wrong. However, when I did it this way, I would get an empty window (JFrame) but none of the panels would show up in it. What I did to fix this is put those add functions in the mvcFrame class:
public class MvcFrame extends JFrame {
private Model model;
public MvcFrame(Model model){
this.model = model;
//setLayout(new GridLayout(4,1));
//add(new RadiusPanel(model));
//add(new ColorPanel(model));
//add(new SolidPanel(model));
//add(new TitlePanel(model));
//add(new View(model));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setSize(800,600);
setVisible(true);
}
}
So here's where things kind of started getting weird. The first block of commented out code is the same as what's in the Controller class. The reason I have it commented out is because that was just a lucky guess - it's not supposed to be like that according to the instructions. However, this did work for getting the panels to show up - but at that point I was still tearing my hair out trying to get the oval to display.
The other commented line ( add(new View(model)); ) was a different attempt at making things work. In this case, I put those add functions in the View class (see commented out code below). This actually worked to display both the oval and the panels, but that method wouldn't allow me to update the oval. Also, though I just had the oval displaying, I can't seem to figure out what exactly made that happen, and I can't seem to make it come back.
public class View extends JPanel{
private Model model;
public View(Model model) {
this.model = model;
//setLayout(new GridLayout(4,1));
//add(new RadiusPanel(model));
//add(new ColorPanel(model));
//add(new SolidPanel(model));
//add(new TitlePanel(model));
repaint();
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
//center of view panel, in pixels:
int xCenter = getWidth()/2;
int yCenter = getHeight()/2;
int radius = model.getRadius();
int xStart = xCenter - radius;
int yStart = yCenter - radius;
int xWidth = 2 * radius;
int yHeight = 2 * radius;
g.setColor(model.getColor());
g.clearRect(0, 0, getWidth(), getHeight());
if (model.isSolid()){
g.fillOval(xStart, yStart, xWidth, yHeight);
} else {
g.drawOval(xStart, yStart, xWidth, yHeight);
}
}
}
Kinda same idea as before - the commented out code is stuff I added to try to get things working, but is not based on the provided directions. In the case where that stuff was uncommented, I had the add(new View(model)); line from the mvcFrame line uncommented as well.
The various panel classes (SolidPanel, ColorPanel, etc) simply extend a class called ControlPanel which extends JPanel. These all seem to work as expected, not having much issue with them. There is also a driver which launches the GUI. This also seems to work as expected.
The main problem I'm having is that I can't get the oval to show up, and the one time I could make it show up, none of the options for changing it seemed to work. I feel like I'm close but I'm just at a loss for other things to try out at this point.
Anyone who can help will have my sincerest gratitude.
Here's a very hasty rewrite of the thing, "just to get it working".
main.java
public class main {
public static void main(String[] args) {
// The JFrame could be created here, since it lasts the life
// of the program.
//...then, later, the model.
Model mdl = new Model();
// ...and then move on to applying the view and control to the frame.
}
}
Controller.java
// Nothing interesting here, added for consistency.
public class Controller {
private final Model model;
public Controller(Model model) {
// The frame is shown automatically in the model here.
this.model = model;
// The frame's setVisible is a control issue, should be called
// from in here, not automatically in the model.
}
}
MvcFrame.java
import javax.swing.JFrame;
public class MvcFrame extends JFrame {
private final Model model;
public MvcFrame(Model model){
this.model = model;
// Anytime you add anything to a JFrame, use the content pane.
this.getContentPane().add(model.getView());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// This line centers the frame on your screen.
setLocationRelativeTo(null);
setSize(800,600);
// The frame won't paint until it's visible.
// This means if you check dimensions, everything will be 0 x 0
// until this line is called.
setVisible(true);
}
}
Model.java
import java.awt.Color;
public class Model {
private final Controller controller; // Not used yet
private final View view;
private final MvcFrame mvcFrame; // Not used yet
// Mutators and accessors needed for these guys (set/get)
private final int radius = 44;
private final Color color = Color.BLUE;
private final boolean solid = true;
public Model() {
controller = new Controller(this);
view = new View(this);
mvcFrame = new MvcFrame(this);
}
public View getView() {
return view;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public boolean isSolid() {
return solid;
}
}
View.java
import java.awt.Graphics;
import javax.swing.JPanel;
public class View extends JPanel{
private final Model model;
public View(Model model) {
this.model = model;
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
//center of view panel, in pixels:
int xCenter = getWidth()/2;
int yCenter = getHeight()/2;
int radius = model.getRadius();
int xStart = xCenter - radius;
int yStart = yCenter - radius;
int xWidth = 2 * radius;
int yHeight = 2 * radius;
g.setColor(model.getColor());
g.clearRect(0, 0, getWidth(), getHeight());
if (model.isSolid()){
g.fillOval(xStart, yStart, xWidth, yHeight);
} else {
g.drawOval(xStart, yStart, xWidth, yHeight);
}
}
}
Your question is a bit rambling and long-winded, and I'm not sure why you have commented out critical parts of the code, but the first question is:
//add(new View(model));
Are you adding the view to the frame? The oval could be drawing just fine, but the view isn't added.
A problem is most likely in this code:
public class MvcFrame extends JFrame {
...
public MvcFrame(Model model){
...
//setLayout(new GridLayout(4,1));
//add(new RadiusPanel(model));
...
}
}
When you call add directly, it references the superclass (JFrame). Unfortunately JFrames are sneaky since they have a contentPane which holds the layout. Further sneakiness: that content pane is a null layout and can only be changed by putting in your own panel.
So, you should probably make something like this. Even if you don't follow exactly, the methods should help you a lot:
...
JPanel pnl = new JPanel(new GridLayout(4, 1));
this.setContentPane(pnl);
pnl.add(new RadiusPanel(model));
...
If you don't want to set the content pane explicitly, you can use this.getContentPane().add(foo).
The null layout issue may also be affecting your oval drawing, since when you add the JPanel, its size is not specified so defaults to (0,0).
Also, not sure why your controller extends JPanel. Your view should be available to the controller, and should be the only thing with any swing components in it.
Although I had a different problem, the line in view:
g.clearRect(0, 0, getWidth(), getHeight());
Solved everything for me on my project because it was not erasing the previous Oval, so I could only see changes if I made it bigger. Thank you.
i am adding widgets to a horizontal panel, and i want them to be all once next to the other on the left corner.
even though i have set the spacing=0 and alignment= left the widgets still have space between them. they are spread evenly in the panel.
please see the code here for the widget C'tor and the function that adds a new tab (toggle button)
tabsPanel is a horizontalPanel, that you can see is aligned to left/right according to the locale
any advise would be appreciated
thanks....
public TabsWidgetManager(int width, int height, int tabs_shift_direction){
DecoratorPanel decorContent = new DecoratorPanel();
DecoratorPanel decorTitle = new DecoratorPanel();
widgetPanel.setSize(Integer.toString(width), Integer.toString(height));
tabsPanel.setSize(Integer.toString(UIConst.USER_CONTENT_WIDTH), Integer.toString(UIConst.TW_DEFAULT_TAB_HEIGHT));
tabsPanel.setSpacing(0);
if (tabs_shift_direction==1)
tabsPanel.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT);
else
tabsPanel.setHorizontalAlignment(HorizontalPanel.ALIGN_RIGHT);
decorTitle.add(tabsPanel);
contentPanel.setSize(Integer.toString(UIConst.USER_CONTENT_WIDTH), Integer.toString(UIConst.USER_CONTENT_MINUS_TABS_HEIGHT));
decorContent.add(contentPanel);
widgetPanel.add(decorTitle, 0, 0);
widgetPanel.add(decorContent, 0, UIConst.TW_DEFAULT_TAB_HEIGHT+15);
initWidget(widgetPanel);
}
public void addTab(String title, Widget widget){
widget.setVisible(false);
ToggleButton tab = new ToggleButton(title);
tabsList.add(tab);
tab.setSize(Integer.toString(UIConst.TW_TAB_DEFAULT_WIDTH), Integer.toString(UIConst.TW_TAB_DEFAULT_HEIGHT));
tab.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
handleTabClick((ToggleButton)event.getSource());
}
});
//adding to the map
tabToWidget.put(tab, widget);
// adding to the tabs bar
tabsPanel.add(tab);
//adding to the content
contentPanel.add(widget);
}
I assume contentPanel is your HorizontalPanel. HorizontalPanel will generate an html table. You're setting the width of the panel. If the width is wider than the sum of all widths of your sub widgets the table will spread the widgets evenly. Remove the setWidth() call.
I would like to remove the scrollbar arrow buttons from a scrollbar in a JScrollPane. How would I do this?
class NoArrowScrollBarUI extends BasicScrollBarUI {
protected JButton createZeroButton() {
JButton button = new JButton("zero button");
Dimension zeroDim = new Dimension(0,0);
button.setPreferredSize(zeroDim);
button.setMinimumSize(zeroDim);
button.setMaximumSize(zeroDim);
return button;
}
#Override
protected JButton createDecreaseButton(int orientation) {
return createZeroButton();
}
#Override
protected JButton createIncreaseButton(int orientation) {
return createZeroButton();
}
#Override
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
//own painting if needed
}
#Override
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
//own painting if needed
}
}
Removing buttons let space for then. I found make buttons zero as the simplest way.
If you are using the basic version of JScrollBar, then it is probably rendering using the BasicScrollBarUI. I would suggest that you extend BasicScrollBarUI to create a custom UI class (like MyBasicScrollBarUI) . The buttons are protected variables in the superclass. So you need to override the installComponents() methods in the subclass and make sure that you do not add the buttons. See the below code snippet and hide the lines as suggested there.
protected void installComponents(){
switch (scrollbar.getOrientation()) {
case JScrollBar.VERTICAL:
incrButton = createIncreaseButton(SOUTH);
decrButton = createDecreaseButton(NORTH);
break;
case JScrollBar.HORIZONTAL:
if (scrollbar.getComponentOrientation().isLeftToRight()) {
incrButton = createIncreaseButton(EAST);
decrButton = createDecreaseButton(WEST);
} else {
incrButton = createIncreaseButton(WEST);
decrButton = createDecreaseButton(EAST);
}
break;
}
scrollbar.add(incrButton); // Comment out this line to hide arrow
scrollbar.add(decrButton); // Comment out this line to hide arrow
// Force the children's enabled state to be updated.
scrollbar.setEnabled(scrollbar.isEnabled());
}
Then, in your code after you initialize a JScrollBar, you can call setUI() and pass in an instance of MyBasicScrollBarUI class.
Note: I havent tried this myself, but from the code it looks like it could work.
It is not the most elegant way... but works for me
JScrollBar jsb = getHorizontalScrollBar();
for(Component c : jsb.getComponents()) {
jsb.remove(c);
}
This is the way i went.
Set the scrollbar policy of the scrollbar you want to hide as never
Mimic this behavior with a MouseWheelListener
This method:
Is fast to implement with very few lines of code.
Retains the benefits of the L&F.
Will remove both the buttons and the bar.
Below is a sample code for removing the verticall scroll bar.
JScrollPane myScrollPane = new JScrollPane();
//remove the scroll bar you don't want
myScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
JTextPane myJTextArea = new JTextPane();
//myScrollPane.setViewportView(myJTextArea);
myScrollPane.addMouseWheelListener(new MouseWheelListener() {
//this will mimick the behavior of scrolling
public void mouseWheelMoved(MouseWheelEvent e) {
JScrollBar scrollBar = myScrollPane.getVerticalScrollBar();
//capturing previous value
int previousValue = scrollBar.getValue();
int addAmount;
//decide where the wheel scrolled
//depending on how fast you want to scroll
//you can chane the addAmount to something greater or lesser
if(e.getWheelRotation()>0) {
addAmount = 2;
}else {
addAmount = -2;
}
//set the new value
scrollBar.setValue(previousValue + addAmount);
}
});