I have a JPanel with an overriden paintComponent. I want to make certain elements that I manually draw on this panel focusable so that people using assistive technologies could use my application using keyboard.
If you could give me a few pointers that would be awesome.
You can do the following:
Convert your elements into JComponents.
Set the LayoutManager of your panel to null. You will then add all your components/elements into this panel and you can move them freelly around with the method Component.setBounds(...).
Add a MouseListener in your panel which will transfer the focus to the selected component, for each mouse press.
You can determine which component was pressed by calling the method Component.getComponentAt(Point) inside the MouseListener of your panel.
Simple example:
Make a component with the standard behaviour of showing the user if it has focus or not. In my example-code below this class is FocusableComponent extends JComponent, which draws a blue rectangle around the component if it has focus (this is done inside the method FocusableComponent.paintComponent(Graphics)).
Then, for each distinct "element" you draw, subclass FocusableComponent and override its paintComponent(Graphics) method to paint the element. Make sure you call "super.paintComponent(Graphics)" inside there for the blue rectangle to be drawn (if it has focus).
Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FocusablePaintComps {
private static abstract class FocusableComponent extends JComponent {
#Override protected void paintComponent(final Graphics g) {
super.paintComponent(g);
if (hasFocus()) {
final Color prevColor = g.getColor();
g.setColor(Color.BLUE);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
g.setColor(prevColor);
}
}
}
private static class FocusableComponent1 extends FocusableComponent {
#Override protected void paintComponent(final Graphics g) {
super.paintComponent(g);
g.fillOval(0, 0, getWidth() - 1, getHeight() - 1);
}
}
private static class FocusableComponent2 extends FocusableComponent {
#Override protected void paintComponent(final Graphics g) {
super.paintComponent(g);
final int w = getWidth(), h = getHeight();
g.fillRect(20, 20, w - 40, h - 40);
g.fillArc(10, 10, w - 1, h - 1, 60, 150);
}
}
private static class YourPanel extends JPanel {
private Component previousFocusedComponent = null;
private YourPanel() {
super(null); //Null LayoutManager. This is important to be able to
//move added components around freelly (with the method setBounds(...)).
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent evt) {
final Component src = getComponentAt(evt.getPoint());
if (src instanceof FocusableComponent) {
final FocusableComponent fc = (FocusableComponent) src;
fc.requestFocusInWindow(); //Transfer focus to the pressed component.
if (previousFocusedComponent != null)
previousFocusedComponent.repaint(); //Repaint the last (without focus now).
setComponentZOrder(fc, 0); //Update: To make fc paint over all others as
//the user http://stackoverflow.com/users/131872/camickr commented.
fc.repaint(); //Repaint the new (with focus now).
previousFocusedComponent = fc;
}
else { //If clicked on empty space, or a non-FocusableComponent:
requestFocusInWindow(); //Tranfer focus to somewhere else (e.g. the panel itself).
if (previousFocusedComponent != null) {
previousFocusedComponent.repaint(); //Repaint the last (without focus now).
previousFocusedComponent = null;
}
}
}
});
setPreferredSize(new Dimension(250, 250));
add(new FocusableComponent1(), Color.RED, new Rectangle(10, 10, 200, 20));
add(new FocusableComponent1(), Color.GREEN, new Rectangle(40, 150, 50, 70));
add(new FocusableComponent2(), Color.GRAY, new Rectangle(60, 125, 90, 100));
add(new FocusableComponent2(), Color.MAGENTA, new Rectangle(150, 60, 80, 150));
}
private void add(final FocusableComponent fc, final Color fgColor, final Rectangle bounds) {
fc.setForeground(fgColor);
add(fc);
fc.setBounds(bounds);
}
}
public static void main(final String[] args) {
final JFrame frame = new JFrame("Focused Paint Comps");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new YourPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Screenshot:
Some notes:
The sequence in which the focus is transfered in relation to the sequence in which the rapaint()s are called inside mousePressed(...) determines which component will have the blue rectanlge around it, and which not.
Method Component.getElementAt(Point) does not "see through" transparent/non-opaque pixels.
Update:
Note: This update is an optional expansion (but maybe a more java-contract-consistent - let me put it) of the above solution. You may read only one of two implementations (either the following "update" one with RandomLayout, either the above "pre-update" one with null LayoutManager).
Follows an update of the above code, which uses a custom LayoutManager to lay-out the components in the container, as suggested by user "Andrew Thompson" in comments.
The only difference from the above code is that instead of setting to null the LayoutManager upon construction of the YourPanel, a new instance of the custom LayoutManager is used and instead of setting the bounds for each component, you only need to set its size.
I have named the custom LayoutManager to RandomLayout and it places all the components of the container in random positions, taking into account the size of the components and the Insets of the container (this is demonstrated by the added Border in the YourPanel).
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class FocusablePaintComps {
private static abstract class FocusableComponent extends JComponent {
#Override protected void paintComponent(final Graphics g) {
super.paintComponent(g);
if (hasFocus()) {
final Color prevColor = g.getColor();
g.setColor(Color.BLUE);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
g.setColor(prevColor);
}
}
}
private static class FocusableComponent1 extends FocusableComponent {
#Override protected void paintComponent(final Graphics g) {
super.paintComponent(g);
g.fillOval(0, 0, getWidth() - 1, getHeight() - 1);
}
}
private static class FocusableComponent2 extends FocusableComponent {
#Override protected void paintComponent(final Graphics g) {
super.paintComponent(g);
final int w = getWidth(), h = getHeight();
g.fillRect(20, 20, w - 40, h - 40);
g.fillArc(10, 10, w - 1, h - 1, 60, 150);
}
}
private static class YourPanel extends JPanel {
private Component previousFocusedComponent = null;
private YourPanel() {
super(new RandomLayout()); //RandomLayout: custom LayoutManager which lays
//out the components in random positions (takes Insets into account).
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent evt) {
final Component src = getComponentAt(evt.getPoint());
if (src instanceof FocusableComponent) {
final FocusableComponent fc = (FocusableComponent) src;
fc.requestFocusInWindow(); //Transfer focus to the pressed component.
if (previousFocusedComponent != null)
previousFocusedComponent.repaint(); //Repaint the last (without focus now).
setComponentZOrder(fc, 0); //Update: To make fc paint over all others as
//the user http://stackoverflow.com/users/131872/camickr commented.
fc.repaint(); //Repaint the new (with focus now).
previousFocusedComponent = fc;
}
else { //If clicked on empty space, or a non-FocusableComponent:
requestFocusInWindow(); //Tranfer focus to somewhere else (e.g. the panel itself).
if (previousFocusedComponent != null) {
previousFocusedComponent.repaint(); //Repaint the last (without focus now).
previousFocusedComponent = null;
}
}
}
});
setBorder(new LineBorder(Color.LIGHT_GRAY, 20));
setPreferredSize(new Dimension(300, 250));
add(new FocusableComponent1(), Color.RED, new Dimension(200, 20));
add(new FocusableComponent1(), Color.GREEN, new Dimension(50, 70));
add(new FocusableComponent2(), Color.GRAY, new Dimension(90, 100));
add(new FocusableComponent2(), Color.MAGENTA, new Dimension(80, 150));
}
private void add(final FocusableComponent fc, final Color fgColor, final Dimension size) {
add(fc);
fc.setForeground(fgColor);
fc.setSize(size);
}
}
public static void main(final String[] args) {
final JFrame frame = new JFrame("Focused Paint Comps");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new YourPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Update's "RandomLayout":
And the custom LayoutManager itself with JavaDoc (may be big, but reusable hopefully):
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.util.Random;
/**
* A {#link java.awt.LayoutManager} which lays out randomly all the {#link java.awt.Component}s
* of its parent, taking into consideration the parent's {#link java.awt.Insets}.
* <p>
* Use {#link #setRandomizeOnce(boolean)} method to determine if the lastly laid-out parent will
* be only laid-out randomly once and not for each {#link #layoutContainer(java.awt.Container)}
* subsequent call for the same parent, or the opposite.
* </p>
*/
public class RandomLayout implements LayoutManager {
/**
* The {#link java.awt.Container} which was lastly laid-out.
*/
private Container lastParent;
/**
* The {#link java.awt.Insets} of {#code lastParent} the last time it was laid-out.
*/
private Insets lastInsets;
/**
* If {#code true} then this {#link java.awt.LayoutManager} keeps track of the
* {#link java.awt.Container}s laid-out to make sure that {#code lastParent} is
* only laid-out once. If the another {#link java.awt.Container} is laid-out, other
* than {#code lastParent}, then its components are laid-out randomly and the
* {#link java.awt.Container} becomes the {#code lastParent}.
*/
private boolean randomizeOnce;
/**
* Normal constructor of {#code RandomLayout} with explicit value for {#code randomizeOnce}.
*
* #param randomizeOnce {#code true} if the lastly laid-out parent will be only laid-out
* randomly once and not for each {#link #layoutContainer(java.awt.Container)} subsequent call
* for the same parent, otherwise {#code false} and each call to
* {#link #layoutContainer(java.awt.Container)} will lay out randomly the {#link java.awt.Container}.
*/
public RandomLayout(final boolean randomizeOnce) {
this.randomizeOnce = randomizeOnce;
}
/**
* Default constructor of {#code RandomLayout} with {#code randomizeOnce} set to {#code true}.
*/
public RandomLayout() {
this(true);
}
/**
* If {#code true} then this {#link java.awt.LayoutManager} keeps track of the
* {#link java.awt.Container}s laid-out to make sure that {#code lastParent} is
* only laid-out once. If the another {#link java.awt.Container} is laid-out, other
* than {#code lastParent}, then its components are laid-out randomly and the
* {#link java.awt.Container} becomes the {#code lastParent}.
*
* #param randomizeOnce {#code true} if the lastly laid-out parent will be only laid-out
* randomly once and not for each {#link #layoutContainer(java.awt.Container)} subsequent call
* for the same parent, otherwise {#code false}.
*/
public void setRandomizeOnce(final boolean randomizeOnce) {
this.randomizeOnce = randomizeOnce;
}
/**
* Tells if the lastly laid-out parent will be only laid-out randomly once and not for each
* {#link #layoutContainer(java.awt.Container)} subsequent call for the same parent, or the
* opposite.
*
* #return {#code true} if the lastly laid-out parent will be only laid-out randomly once and
* not for each {#link #layoutContainer(java.awt.Container)} subsequent call for the same
* parent, otherwise {#code false}.
*/
public boolean isRandomizeOnce() {
return randomizeOnce;
}
/**
* #return The {#link java.awt.Container} which was lastly laid-out.
*/
protected Container getLastParent() {
return lastParent;
}
/**
* #return The {#link java.awt.Insets} of {#code lastParent} the last time it was laid-out.
* #see #getLastParent()
*/
protected Insets getLastInsets() {
return lastInsets;
}
/**
* Adds the specified component with the specified name to the layout.
* #param name The name of the component.
* #param comp The {#link java.awt.Component} to be added.
*/
public void addLayoutComponent(final String name,
final Component comp) {
}
/**
* Removes the specified component from the layout.
* #param comp The {#link java.awt.Component} to be removed.
*/
public void removeLayoutComponent(final Component comp) {
}
/**
* {#inheritDoc}
* #return The preferred size dimensions for the specified {#link java.awt.Container}.
*/
#Override
public Dimension preferredLayoutSize(final Container parent) {
final Dimension prefDim = minimumLayoutSize(parent);
prefDim.width += 2; //+2 to spare.
prefDim.height += 2; //+2 to spare.
return prefDim;
}
/**
* {#inheritDoc}
* #return The minimum size dimensions for the specified {#link java.awt.Container}.
*/
#Override
public Dimension minimumLayoutSize(final Container parent) {
final Dimension minDim = new Dimension();
final int childCnt = parent.getComponentCount();
for (int i=0; i<childCnt; ++i)
applyBigger(minDim, getPreferredSize(parent, parent.getComponent(i)));
final Insets parInsets = parent.getInsets();
minDim.width += (parInsets.left + parInsets.right);
minDim.height += (parInsets.top + parInsets.bottom);
return minDim;
}
/**
* {#inheritDoc}. If the another {#link java.awt.Container} is laid-out, other
* than {#code lastParent}, then its components are laid-out randomly and the
* {#link java.awt.Container} becomes the {#code lastParent}.
*/
#Override
public void layoutContainer(final Container parent) {
if (parent == null)
throw new IllegalArgumentException("Cannot lay out null.");
if (isRandomizeOnce() && lastParent == parent) { //At least take care of insets (if they have changed).
final Insets parentInsets = parent.getInsets();
if (!lastInsets.equals(parentInsets)) {
final int offx = parentInsets.left - lastInsets.left,
offy = parentInsets.top - lastInsets.top;
final int childCnt = parent.getComponentCount();
for (int i=0; i<childCnt; ++i) {
final Component child = parent.getComponent(i);
final Point childLoca = child.getLocation();
childLoca.x += offx;
childLoca.y += offy;
child.setLocation(childLoca);
}
lastInsets = parentInsets;
}
}
else
layoutContainerRandomly(parent);
}
/**
* Explicitly lays out randomly the specified container.
* <p>
* This is equivalent of calling:
* <pre>
* boolean isRand1 = randomLayout.isRandomizeOnce();
* randomLayout.setRandomizeOnce(false);
* randomLayout.layoutContainer(parent);
* randomLayout.setRandomizeOnce(isRand1);
* </pre>
* {#code parent} becomes {#code lastParent}.
* </p>
* #param parent The container to be laid out.
*/
public void layoutContainerRandomly(final Container parent) { //Place each child at a random location for the "new" parent (lastParent != parent).
if (parent == null)
throw new IllegalArgumentException("Cannot lay out null.");
reset();
final Dimension parentSize = parent.getSize();
final Insets parentInsets = parent.getInsets();
final Dimension childSize = new Dimension();
final Point childLoca = new Point();
final Random rand = new Random();
final int childCnt = parent.getComponentCount();
for (int i=0; i<childCnt; ++i) {
final Component child = parent.getComponent(i);
child.getSize(childSize);
childLoca.x = parentInsets.left + 1;
childLoca.y = parentInsets.top + 1;
final int xBound = parentSize.width - parentInsets.left - parentInsets.right - childSize.width,
yBound = parentSize.height - parentInsets.top - parentInsets.bottom - childSize.height;
if (xBound > 0)
childLoca.x += rand.nextInt(xBound);
if (yBound > 0)
childLoca.y += rand.nextInt(yBound);
child.setLocation(childLoca);
}
lastParent = parent;
lastInsets = parentInsets;
}
/**
* Invalidates the tracking of the lastly laid-out {#link java.awt.Container} and its last
* {#link java.awt.Insets}.
* #see #getLastParent()
* #see #getLastInsets()
*/
protected void reset() {
lastParent = null;
lastInsets = null;
}
private static void applyBigger(final Dimension inputOutput,
final Dimension input) {
if (inputOutput != null && input != null) {
inputOutput.width = (int) Math.max(inputOutput.width, input.width);
inputOutput.height = (int) Math.max(inputOutput.height, input.height);
}
}
private static void applyIfBetter(final Dimension inputOutput,
final Dimension input) {
if (inputOutput != null && input != null
&& (input.width > inputOutput.width
|| input.height > inputOutput.height)) {
inputOutput.width = input.width;
inputOutput.height = input.height;
}
}
/**
* Tries to determine the best size for {#code child}.
* #param parnt The parent {#link java.awt.Container} being laid-out.
* #param child The child {#link java.awt.Component} of {#code parnt} being laid-out.
* #return A preferred size for the {#code child} to be laid-out.
*/
protected static Dimension getPreferredSize(final Container parnt,
final Component child) {
final Dimension minDim = new Dimension();
if (child != null) {
applyIfBetter(minDim, child.getMinimumSize());
applyIfBetter(minDim, child.getSize());
applyIfBetter(minDim, child.getPreferredSize());
}
return minDim;
}
}
Updated screenshot:
And here is the new screenshot (no big visual differences):
Note for the update:
Be aware that this is my first custom LayoutManager, but I have read the documentation, and also GridLayout and SpringLayout as examples (because, in my oppinion, LayoutManager's documentation is not enough) and of course I tested it. As it is wright now, I cannot find any problem with it. Any suggestions or proposals for improvements will be appreciated of course.
Related
Does this component exist in Java? It is a a button with an attached combo box.
I have similar component which I developed in one of my own projects.
Please replace all the custom components (Like - CToolBarButton to JButton, CPanel to JPanel, CButton to JButton) wherever required. You also can use custion UI classes to get the desired look and feel.
package com.custom.ui.base.control;
import com.custom.ui.base.border.ToolbarButtonOptionsBorder;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
/**
* This is Options button in for ToolBar. This Toolbar button can contain other toolbar buttons those
* would be displayed in the popup
*
* #version 1.0.0.1 (Jul 29, 2010)
* #author Arindam Roy
*/
public class CToolbarButtonOptions extends CToolBarButton implements ActionListener, PopupMenuListener {
/**
* Holds number of buttons in the Popup
*/
int count = 0;
/**
* Holds the Columns in the popup
*/
private int maxColumnCount = 3;
/**
* Creates new form BeanForm
*/
public CToolbarButtonOptions() {
initComponents();
init();
}
/**
* Initializes additional components in this screen
*/
private void init() {
jPopupMenu.add(dmPanel);
setListeners();
}
/**
* Adds listeners to the components
*/
private void setListeners() {
this.addActionListener(this);
this.jPopupMenu.addPopupMenuListener(this);
}
/**
* Adds a Toolbar button in the popup
*
* #param toolBarButton <code>CToolBarButton</code> instance to add in the popup
* #see CToolBarButton
* #see #remove(com.dbm.base.control.DMToolBarButton)
*/
public void add(CToolBarButton toolBarButton) {
dmPanel.add(toolBarButton);
toolBarButton.addActionListener(this);
count++;
updateLayout();
}
/**
* Returns components of Option button popup menu
*
* #return <code>Component[]</code> that contains components of popup menu
*/
public Component[] getPopupComponents() {
return dmPanel.getComponents();
}
/**
* Removes a Toolbar button from the popup
*
* #param toolBarButton <code>CToolBarButton</code> instance to remove from the popup
* #see CToolBarButton
* #see #add(com.dbm.base.control.DMToolBarButton)
*/
public void remove(CToolBarButton toolBarButton) {
toolBarButton.removeActionListener(this);
count--;
dmPanel.remove(toolBarButton);
}
/**
* Recognize the columns and rows to place popup menu buttons in the Popup
*/
private void updateLayout() {
if (dmPanel.getLayout() instanceof GridLayout) {
int row = (count / getMaxColumnCount());
if (count % getMaxColumnCount() > 0) {
row++;
}
int column = count >= maxColumnCount ? maxColumnCount : count;
if (!(row == 0 && column == 0)) {
((GridLayout) dmPanel.getLayout()).setRows(row);
((GridLayout) dmPanel.getLayout()).setColumns(column);
}
}
}
/**
* This method is called from within the constructor to
* initialize the form.
*/
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jPopupMenu = new javax.swing.JPopupMenu();
dmPanel = new com.custom.ui.base.container.CPanel();
jPopupMenu.setBorder(javax.swing.BorderFactory.createCompoundBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(38, 144, 72)), javax.swing.BorderFactory.createLineBorder(new java.awt.Color(255, 255, 255))));
dmPanel.setLayout(new java.awt.GridLayout(1, 1, 0, 1));
setBorder(new ToolbarButtonOptionsBorder());
setPreferredSize(new java.awt.Dimension(25, 8));
}// </editor-fold>//GEN-END:initComponents
/**
* Action Handler for Action Events
*
* #param e <code>ActionEvent</code> instance that holds the event details
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource() == this) {
jPopupMenu.show(this, 2, this.getBounds().y + this.getBounds().height + 1);
} else {
jPopupMenu.setVisible(false);
}
}
/**
* Returns the number of columns, according to that the Popup menu buttons are placed
*
* #return <code>int</code> column count in the popup menu for button placement
* #see #setMaxColumnCount(int)
*/
public int getMaxColumnCount() {
return maxColumnCount;
}
/**
* Sets the number of columns, according to that the Popup menu buttons are placed
*
* #param maxColumnCount <code>int</code> column count in the popup menu for button
* placement, to set
* #see #getMaxColumnCount()
*/
public void setMaxColumnCount(int maxColumnCount) {
this.maxColumnCount = maxColumnCount;
updateLayout();
}
/**
* Action handler for Popup menu listener. This method handles the event of "popup menu visible"
*
* #param e <code>PopupMenuEvent</code> instance that holds the event details
*/
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
this.getModel().setRollover(true);
}
/**
* Action handler for Popup menu listener. This method handles the event of "popup menu visible"
*
* #param e <code>PopupMenuEvent</code> instance that holds the event details
*/
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
this.getModel().setRollover(false);
}
/**
* Action handler for Popup menu listener. This method handles the event of "popup menu visible"
*
* #param e <code>PopupMenuEvent</code> instance that holds the event details
*/
public void popupMenuCanceled(PopupMenuEvent e) {
this.getModel().setRollover(false);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private com.custom.ui.base.container.CPanel dmPanel;
private javax.swing.JPopupMenu jPopupMenu;
// End of variables declaration//GEN-END:variables
}
This will look like:
Custom Border class:
package com.custom.ui.base.border;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import javax.swing.border.EmptyBorder;
/**
* This border for toolbar button component holds group of buttons. Clicking on this, one popup comes that
* contains all the options buttons available against this toolbar button.
*
* #version 1.0.0.1 (Sep 15, 2010)
* #author Arindam Roy
*/
public class ToolbarButtonOptionsBorder extends EmptyBorder {
/**
* Indicates width of the Down arrow icon width
*/
private int optionArrowWidth = 7;
/**
* Creates new instance of ToolbarButtonOptionsBorder
*/
public ToolbarButtonOptionsBorder() {
super(new Insets(4, 4, 4, 8));
}
/**
* Paints the border for the specified component with the specified position and size.
*
* #param c the component for which this border is being painted
* #param g the paint graphics
* #param x the x position of the painted border
* #param y the y position of the painted border
* #param width the width of the painted border
* #param height the height of the painted border
*/
#Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
super.paintBorder(c, g, x, y, width, height);
g.setColor(Color.LIGHT_GRAY);
int xPoints[] = new int[]{x + width - optionArrowWidth - 1, x + width - 1, x + width - 2 - (optionArrowWidth / 2)};
int yPoints[] = new int[]{y + (height / 2) - 1, y + (height / 2) - 1, y + (height / 2) + 3};
g.fillPolygon(xPoints, yPoints, xPoints.length);
g.drawLine(x + width - optionArrowWidth - 2, y + 5, x + width - optionArrowWidth - 2, height - 3);
g.setColor(Color.BLACK);
xPoints = new int[]{x + width - optionArrowWidth, x + width - 2, x + width - 2 - (optionArrowWidth / 2)};
yPoints = new int[]{y + (height / 2) - 1, y + (height / 2) - 1, y + (height / 2) + 2};
g.fillPolygon(xPoints, yPoints, xPoints.length);
}
}
The goal to write the values of columns into JTable vertically. I read about DefaultTableCellRenderer, VerticalTableHeaderCellRenderer() but cannot implement it. Here is my code:
//=========================================================
private JScrollPane getTablePane() {
if (tablePane == null) {
tablePane = new JScrollPane();
tablePane.setRowHeaderView(getTableDictionary());
tablePane.setViewportView(getTableDictionary());
}
return tablePane;
}
//=============================================================
private JTable getTableDictionary(){
if (table == null) {
rowVector = new String[colCount];
for(int i=0; i<colCount;i++) {rowVector[i]="";}
data = new DefaultTableModel(rowVector, 0);
for (int i = 0; i < rowCount; i++) { data.addRow(rowVector); }
table = new JTable(data);
table.getTableHeader().setDefaultRenderer(new VerticalTableHeaderCellRenderer());
for(int i=1; i<colCount; i++) { table.getColumnModel().getColumn(i).setPreferredWidth(withCol);}
table.setSelectionForeground(Color.BLACK);
table.setSelectionBackground(Color.YELLOW);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.setAutoscrolls(true);
table.setColumnSelectionAllowed(false);
table.setRowSelectionAllowed(true);
/* DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer();
int bg = table.getTableHeader().getBackground().getRGB();
defaultTableCellRenderer.setBackground(Color.getHSBColor(125, 125, 125)); //задаем цвет столбца
table.getColumnModel().getColumn(0).setCellRenderer(defaultTableCellRenderer);
*/
return table;
}
Where is my mistake? Thank you
I read more resources and I found a decision, it works. I need to implement a render for that and call it when I am locating the table on the JScrollPane. It means that column header is processing apart from a JTable using datamodel. Also, I can define the height of the columns.
private JScrollPane getTablePane() {
if (tablePane == null) {
tablePane = new JScrollPane();
tablePane.setRowHeaderView(getTableDictionary());
tablePane.setViewportView(getTableDictionary());
tablePane.setColumnHeader(new JViewport() {
#Override public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.height = rowColumnHeigth; // Col header Height
return d;
}
});
}
return tablePane;
}
Also, I need for that additional classes:
/**
* #(#)DefaultTableHeaderCellRenderer.java 1.0 02/24/09
*/
import java.awt.Component;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
/**
* A default cell renderer for a JTableHeader.
* <P>
* DefaultTableHeaderCellRenderer attempts to provide identical behavior to the
* renderer which the Swing subsystem uses by default, the Sun proprietary
* class sun.swing.table.DefaultTableCellHeaderRenderer.
* <P>
* To apply any desired customization, DefaultTableHeaderCellRenderer may be
* suitably extended.
*
* #author Darryl
*/
public class DefaultTableHeaderCellRenderer extends DefaultTableCellRenderer {
/**
* Constructs a <code>DefaultTableHeaderCellRenderer</code>.
* <P>
* The horizontal alignment and text position are set as appropriate to a
* table header cell, and the opaque property is set to false.
*/
public DefaultTableHeaderCellRenderer() {
setHorizontalAlignment(CENTER);
setHorizontalTextPosition(LEFT);
setVerticalAlignment(BOTTOM);
setOpaque(false);
}
/**
* Returns the default table header cell renderer.
* <P>
* If the column is sorted, the approapriate icon is retrieved from the
* current Look and Feel, and a border appropriate to a table header cell
* is applied.
* <P>
* Subclasses may overide this method to provide custom content or
* formatting.
*
* #param table the <code>JTable</code>.
* #param value the value to assign to the header cell
* #param isSelected This parameter is ignored.
* #param hasFocus This parameter is ignored.
* #param row This parameter is ignored.
* #param column the column of the header cell to render
* #return the default table header cell renderer
*/
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
JTableHeader tableHeader = table.getTableHeader();
if (tableHeader != null) {
setForeground(tableHeader.getForeground());
}
setIcon(getIcon(table, column));
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
return this;
}
/**
* Overloaded to return an icon suitable to the primary sorted column, or null if
* the column is not the primary sort key.
*
* #param table the <code>JTable</code>.
* #param column the column index.
* #return the sort icon, or null if the column is unsorted.
*/
protected Icon getIcon(JTable table, int column) {
SortKey sortKey = getSortKey(table, column);
if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) {
switch (sortKey.getSortOrder()) {
case ASCENDING:
return UIManager.getIcon("Table.ascendingSortIcon");
case DESCENDING:
return UIManager.getIcon("Table.descendingSortIcon");
}
}
return null;
}
/**
* Returns the current sort key, or null if the column is unsorted.
*
* #param table the table
* #param column the column index
* #return the SortKey, or null if the column is unsorted
*/
protected SortKey getSortKey(JTable table, int column) {
RowSorter rowSorter = table.getRowSorter();
if (rowSorter == null) {
return null;
}
List sortedColumns = rowSorter.getSortKeys();
if (sortedColumns.size() > 0) {
return (SortKey) sortedColumns.get(0);
}
return null;
}
}
Also:
/**
* #(#)VerticalLabelUI.java 1.0 02/18/09
*/
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicLabelUI;
/**
* A UI delegate for JLabel that rotates the label 90є
* <P>
* Extends {#link BasicLabelUI}.
* <P>
* The only difference between the appearance of labels in the Basic and Metal
* L&Fs is the manner in which diabled text is painted. As VerticalLabelUI
* does not override the method paintDisabledText, this class can be adapted
* for Metal L&F by extending MetalLabelUI instead of BasicLabelUI.
* <P>
* No other changes are required.
*
* #author Darryl
*/
public class VerticalLabelUI extends BasicLabelUI {
private boolean clockwise = false;
// see comment in BasicLabelUI
Rectangle verticalViewR = new Rectangle();
Rectangle verticalIconR = new Rectangle();
Rectangle verticalTextR = new Rectangle();
protected static VerticalLabelUI verticalLabelUI =
new VerticalLabelUI();
private final static VerticalLabelUI SAFE_VERTICAL_LABEL_UI =
new VerticalLabelUI();
/**
* Constructs a <code>VerticalLabelUI</code> with the default anticlockwise
* rotation
*/
public VerticalLabelUI() {
}
/**
* Constructs a <code>VerticalLabelUI</code> with the desired rotation.
* <P>
* #param clockwise true to rotate clockwise, false for anticlockwise
*/
public VerticalLabelUI(boolean clockwise) {
this.clockwise = clockwise;
}
/**
* #see ComponentUI#createUI(javax.swing.JComponent)
*/
public static ComponentUI createUI(JComponent c) {
if (System.getSecurityManager() != null) {
return SAFE_VERTICAL_LABEL_UI;
} else {
return verticalLabelUI;
}
}
/**
* Overridden to always return -1, since a vertical label does not have a
* meaningful baseline.
*
* #see ComponentUI#getBaseline(JComponent, int, int)
*/
#Override
public int getBaseline(JComponent c, int width, int height) {
super.getBaseline(c, width, height);
return -1;
}
/**
* Overridden to always return Component.BaselineResizeBehavior.OTHER,
* since a vertical label does not have a meaningful baseline
*
* #see ComponentUI#getBaselineResizeBehavior(javax.swing.JComponent)
*/
#Override
public Component.BaselineResizeBehavior getBaselineResizeBehavior(
JComponent c) {
super.getBaselineResizeBehavior(c);
return Component.BaselineResizeBehavior.OTHER;
}
/**
* Transposes the view rectangles as appropriate for a vertical view
* before invoking the super method and copies them after they have been
* altered by {#link SwingUtilities#layoutCompoundLabel(FontMetrics, String,
* Icon, int, int, int, int, Rectangle, Rectangle, Rectangle, int)}
*/
#Override
protected String layoutCL(JLabel label, FontMetrics fontMetrics,
String text, Icon icon, Rectangle viewR, Rectangle iconR,
Rectangle textR) {
verticalViewR = transposeRectangle(viewR, verticalViewR);
verticalIconR = transposeRectangle(iconR, verticalIconR);
verticalTextR = transposeRectangle(textR, verticalTextR);
text = super.layoutCL(label, fontMetrics, text, icon,
verticalViewR, verticalIconR, verticalTextR);
viewR = copyRectangle(verticalViewR, viewR);
iconR = copyRectangle(verticalIconR, iconR);
textR = copyRectangle(verticalTextR, textR);
return text;
}
/**
* Transforms the Graphics for vertical rendering and invokes the
* super method.
*/
#Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
if (clockwise) {
g2.rotate(Math.PI / 2, c.getSize().width / 2, c.getSize().width / 2);
} else {
g2.rotate(-Math.PI / 2, c.getSize().height / 2, c.getSize().height / 2);
}
super.paint(g2, c);
}
/**
* Returns a Dimension appropriate for vertical rendering
*
* #see ComponentUI#getPreferredSize(javax.swing.JComponent)
*/
#Override
public Dimension getPreferredSize(JComponent c) {
return transposeDimension(super.getPreferredSize(c));
}
/**
* Returns a Dimension appropriate for vertical rendering
*
* #see ComponentUI#getMaximumSize(javax.swing.JComponent)
*/
#Override
public Dimension getMaximumSize(JComponent c) {
return transposeDimension(super.getMaximumSize(c));
}
/**
* Returns a Dimension appropriate for vertical rendering
*
* #see ComponentUI#getMinimumSize(javax.swing.JComponent)
*/
#Override
public Dimension getMinimumSize(JComponent c) {
return transposeDimension(super.getMinimumSize(c));
}
private Dimension transposeDimension(Dimension from) {
return new Dimension(from.height, from.width + 2);
}
private Rectangle transposeRectangle(Rectangle from, Rectangle to) {
if (to == null) {
to = new Rectangle();
}
to.x = from.y;
to.y = from.x;
to.width = from.height;
to.height = from.width;
return to;
}
private Rectangle copyRectangle(Rectangle from, Rectangle to) {
if (to == null) {
to = new Rectangle();
}
to.x = from.x;
to.y = from.y;
to.width = from.width;
to.height = from.height;
return to;
}
}
Also:
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.Icon;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.UIManager;
/**
* A renderer for a JTableHeader with text rotated 90В° counterclockwise.
* <P>
* Extends {#link DefaultTableHeaderCellRenderer}.
*
* #see VerticalLabelUI
* #author Darryl
*/
public class VerticalTableHeaderCellRenderer
extends DefaultTableHeaderCellRenderer {
/**
* Constructs a <code>VerticalTableHeaderCellRenderer</code>.
* <P>
* The horizontal and vertical alignments and text positions are set as
* appropriate to a vertical table header cell.
*/
public VerticalTableHeaderCellRenderer() {
setHorizontalAlignment(LEFT);
setHorizontalTextPosition(CENTER);
setVerticalAlignment(CENTER);
setVerticalTextPosition(TOP);
setUI(new VerticalLabelUI());
}
/**
* Overridden to return a rotated version of the sort icon.
*
* #param table the <code>JTable</code>.
* #param column the colummn index.
* #return the sort icon, or null if the column is unsorted.
*/
#Override
protected Icon getIcon(JTable table, int column) {
SortKey sortKey = getSortKey(table, column);
if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) {
SortOrder sortOrder = sortKey.getSortOrder();
switch (sortOrder) {
case ASCENDING:
return VerticalSortIcon.ASCENDING;
case DESCENDING:
return VerticalSortIcon.DESCENDING;
}
}
return null;
}
/**
* An icon implementation to paint the contained icon rotated 90В° clockwise.
* <P>
* This implementation assumes that the L&F provides ascending and
* descending sort icons of identical size.
*/
private enum VerticalSortIcon implements Icon {
ASCENDING(UIManager.getIcon("Table.ascendingSortIcon")),
DESCENDING(UIManager.getIcon("Table.descendingSortIcon"));
private final Icon icon;// = ;
private VerticalSortIcon(Icon icon) {
this.icon = icon;
}
/**
* Paints an icon suitable for the header of a sorted table column,
* rotated by 90В° clockwise. This rotation is applied to compensate
* the rotation already applied to the passed in Graphics reference
* by the VerticalLabelUI.
* <P>
* The icon is retrieved from the UIManager to obtain an icon
* appropriate to the L&F.
*
* #param c the component to which the icon is to be rendered
* #param g the graphics context
* #param x the X coordinate of the icon's top-left corner
* #param y the Y coordinate of the icon's top-left corner
*/
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
int maxSide = Math.max(getIconWidth(), getIconHeight()+(Integer)getIconHeight()/4);
Graphics2D g2 = (Graphics2D) g.create(x, y, maxSide, maxSide);
g2.rotate((Math.PI / 2));
g2.translate(0, -maxSide);
icon.paintIcon(c, g2, 0, 0);
g2.dispose();
}
/**
* Returns the width of the rotated icon.
*
* #return the <B>height</B> of the contained icon
*/
#Override
public int getIconWidth() {
return icon.getIconHeight();
}
/**
* Returns the height of the rotated icon.
*
* #return the <B>width</B> of the contained icon
*/
#Override
public int getIconHeight() {
return icon.getIconWidth();
}
}
}
It works fine for me. Thank you!
Also, it looks like:
I'm trying to build a web browser in java swing where i added new tab option by adding JTabbedPane. But i want to update my current tab title when i browse another web page or urls etc. I used WebView and WebEngine as html parser. I tried to update title name using WebEngine.getTitle() it didn't work.
Here is my constructor:
public class Hello extends JFrame {
private JButton backButton = new JButton();
private JButton newPage;
private JButton forwardButton = new JButton();
private JFXPanel jfxPanel = new JFXPanel();
private JTextField locationTextField = new JTextField(100);
private JPanel fPane = new JPanel(new BorderLayout());
private JPanel totalPane = new JPanel(new BorderLayout());
private WebEngine engine;
JPanel buttonPanel = new JPanel(new BorderLayout());
public ArrayList<String> pageList = new ArrayList<>();
public ArrayList<String> historyList = new ArrayList<>();
static int value = 0;
static int inI = 0;
public ArrayList<String> title = new ArrayList<>();
public ArrayList<String> titleUrl = new ArrayList<>();
Hashtable<String, String> table;
History hist;
final JTabbedPane tb;
private GraphicsDevice device;
private JFrame frame;
public Hello() {
currentScene();
table = new Hashtable<>();
setResizable(true);
setTitle("Pooh");
ImageIcon img = new ImageIcon(Hello.class.getResource("/Panda-icon.png"));
setIconImage(img.getImage());
JPanel buttonPanel1 = new JPanel();
buttonPanel1.setBackground(Color.decode("#330300"));
backButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
BorderFactory.createRaisedBevelBorder();
actionBack();
System.out.println(value + " " + pageList.size() + " back");
}
});
backButton.setEnabled(true);
backButton.setBorderPainted(false);
buttonPanel1.add(backButton);
forwardButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (pageList.size() > value) {
value++;
}
System.out.println(value + "forwad");
actionForward();
}
});
forwardButton.setEnabled(true);
locationTextField.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
value++;
String tUrl = toURL(locationTextField.getText());
actionGo(locationTextField.getText());
}
}
});
locationTextField.setDragEnabled(true);
buttonPanel1.add(locationTextField, BorderLayout.EAST);
JPanel buttonPanel2 = new JPanel(new BorderLayout(5, 5));
buttonPanel2.setBackground(Color.decode("#330300"));
newPage = new JButton();
newPage.setIcon(new ImageIcon(Hello.class.getResource("/Plus.png")));
newPage.setPreferredSize(new Dimension(30,30));
newPage.setBorderPainted(false);
newPage.setBackground(Color.decode("#330300"));
newPage.setToolTipText("New Page");
newPage.setEnabled(true);
buttonPanel2.add(newPage,BorderLayout.CENTER);
buttonPanel.setBackground(Color.decode("#330300"));
buttonPanel.add(buttonPanel1, BorderLayout.WEST);
buttonPanel.add(buttonPanel2, BorderLayout.EAST);
JPanel lblBar = new JPanel(new BorderLayout());
lblBar.setPreferredSize(new Dimension(50,10));
JPanel jp = new JPanel();
jp.setLayout(new BorderLayout());
UIManager.put("TabbedPane.background",Color.decode("#330300"));
tb = new JTabbedPane();
tb.setUI(new CustomTabbedPaneUI());
tb.setForeground(Color.decode("#330300"));
**//creating initial tab with default name "Google" which i want to change when new urls are clicked**
tb.addTab("Google",fPane);
jp.add(new JLayer<JTabbedPane>(tb));
**//creating new tabs when button is clicked**
newPage.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
tb.addTab("New Tab",new JPanel());
**//a method which takes index of tabbed pane and returns a tabbed pane**
initTabComponent(inI+1);
inI++;
}
});
tb.setOpaque(true);
fPane.add(jfxPanel,BorderLayout.CENTER);
totalPane.add(buttonPanel, BorderLayout.NORTH);
totalPane.add(jp, BorderLayout.CENTER);
setExtendedState(JFrame.MAXIMIZED_BOTH);
setLayout(new BorderLayout());
getContentPane().add(totalPane,BorderLayout.CENTER);
getContentPane().add(lblStatus,BorderLayout.PAGE_END);
setPreferredSize(new Dimension(1024, 600));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
}
}
Here is my currentScene() method:
private void currentScene() {
Platform.runLater(new Runnable() {
#Override
public void run() {
WebView view = new WebView();
engine = view.getEngine();
engine.createPopupHandlerProperty();
engine.titleProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, final String newValue) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Hello.this.setTitle(newValue);
if (newValue != null) {
table.put(newValue, tUrl);
title.add(newValue);
historyList.add(newValue);
pageList.add(tUrl);
}
}
});
}
});
engine.setOnStatusChanged(new EventHandler<WebEvent<String>>() {
#Override
public void handle(final WebEvent<String> event) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
lblStatus.setText(event.getData());
}
});
}
});
engine.locationProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String oldValue, final String newValue) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
titleUrl.add(newValue);
locationTextField.setText(newValue);
}
});
}
});
engine.getLoadWorker().workDoneProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, final Number newValue) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
progressBar.setValue(newValue.intValue());
}
});
}
});
engine.getLoadWorker()
.exceptionProperty()
.addListener(new ChangeListener<Throwable>() {
public void changed(ObservableValue<? extends Throwable> o, Throwable old, final Throwable value) {
if (engine.getLoadWorker().getState() == FAILED) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JOptionPane.showMessageDialog(
fPane,
(value != null)
? engine.getLocation() + "\n" + value.getMessage()
: engine.getLocation() + "\nUnexpected error.",
"Loading error...",
JOptionPane.ERROR_MESSAGE);
}
});
}
}
});
jfxPanel.setScene(new Scene(view));
}
});
}
You will want to call setTitleAt(...) on the JTabbedPane to change the title of the tab.
I prefer CloseableTabbedPane . It has methid addTab with tab title and icon
You can download it here https://github.com/dimensionv/closeabletabbedpane
package de.dimensionv.java.gui.swing.panels;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.Icon;
/**
* Draws an icon representing an "X" in a box. The constructor accepts an icon, which will be placed next (right) to the
* 'X' icon drawn by this class. If no extra icon is needed, the empty default constructor can be used, or provide
* "null" as a value for the icon-object.
*
* #author mjoellnir
* #version 1.0
*/
public class CloseTabIcon implements Icon {
private int xPos;
private int yPos;
private int width = 16;
private int height = 16;
private final int offsetFrame = 2;
private final int offsetCross1 = 3;
private final int offsetCross2 = 4;
private Icon extraIcon = null;
/**
* Creates new "X" Icon.
*/
public CloseTabIcon() {
}
/**
* Creates new "X" Icon with an extra icon next to it.
*
* #param fileIcon the Icon-object to be placed next to this icon.
* #see javax.swing.Icon
*/
public CloseTabIcon(Icon fileIcon) {
extraIcon = fileIcon;
}
#Override
public void paintIcon(Component component, Graphics graphics, int x, int y) {
setXPos(x);
setYPos(y);
Color col = graphics.getColor();
graphics.setColor(Color.black);
// prepare coordinates for the frame...
int frameTop = y + offsetFrame;
int frameBottom = y + (height - offsetFrame);
int frameLeft = x + offsetFrame;
int frameRight = x + (width - offsetFrame);
// top line of rectangle-frame...
graphics.drawLine(frameLeft + 2, frameTop, frameRight - 2, frameTop);
// bottom line of rectangle-frame...
graphics.drawLine(frameLeft + 2, frameBottom, frameRight - 2, frameBottom);
// left line of rectangle-frame...
graphics.drawLine(frameLeft, frameTop + 2, frameLeft, frameBottom - 2);
// right line of rectangle-frame...
graphics.drawLine(frameRight, frameTop + 2, frameRight, frameBottom - 2);
// rounding
graphics.drawLine(frameLeft + 1, frameTop + 1, frameLeft + 1, frameTop + 1);
graphics.drawLine(frameRight - 1, frameTop + 1, frameRight - 1, frameTop + 1);
graphics.drawLine(frameLeft + 1, frameBottom - 1, frameLeft + 1, frameBottom - 1);
graphics.drawLine(frameRight - 1, frameBottom - 1, frameRight - 1, frameBottom - 1);
// prepare coordinates for the "X"
int crossTop1 = frameTop + offsetCross1;
int crossBottom1 = frameBottom - offsetCross1;
int crossTop2 = frameTop + offsetCross2;
int crossBottom2 = frameBottom - offsetCross2;
int crossRight1 = frameRight - offsetCross1;
int crossLeft1 = frameLeft + offsetCross1;
int crossRight2 = frameRight - offsetCross2;
int crossLeft2 = frameLeft + offsetCross2;
// first diagonal of "X": top left to bottom right...
graphics.drawLine(crossLeft1, crossTop1, crossRight1, crossBottom1);
graphics.drawLine(crossLeft1, crossTop2, crossRight2, crossBottom1);
graphics.drawLine(crossLeft2, crossTop1, crossRight1, crossBottom2);
// second diagonal of "X": top right to bottom left...
graphics.drawLine(crossRight1, crossTop1, crossLeft1, crossBottom1);
graphics.drawLine(crossRight1, crossTop2, crossLeft2, crossBottom1);
graphics.drawLine(crossRight2, crossTop1, crossLeft1, crossBottom2);
graphics.setColor(col);
if (extraIcon != null) {
extraIcon.paintIcon(component, graphics, x + getWidth(), y + 2);
}
}
#Override
public int getIconWidth() {
return getWidth() + (extraIcon != null ? extraIcon.getIconWidth() : 0);
}
#Override
public int getIconHeight() {
return getHeight();
}
/**
* Returns the bounding rectangle of this icon.
*
* #return the bounding rectangle of this icon.
* #see java.awt.Rectangle
*/
public Rectangle getBounds() {
return new Rectangle(getXPos(), getYPos(), getWidth(), getHeight());
}
/**
* Returns the x-coordinate of the position of this icon.
*
* #return the x-coordinate of the position of this icon.
*/
public int getXPos() {
return xPos;
}
/**
* Returns the y-coordinate of the position of this icon.
*
* #return the y-coordinate of the position of this icon.
*/
public int getYPos() {
return yPos;
}
/**
* Returns the width of this icon.
*
* #return the width of this icon.
*/
public int getWidth() {
return width;
}
/**
* Returns the height of this icon.
*
* #return the height of this icon.
*/
public int getHeight() {
return height;
}
/**
* Returns the extra-icon, which is to be displayed next to this icon. Might be null.
*
* #return the extra-icon.
*/
public Icon getExtraIcon() {
return extraIcon;
}
/**
* Sets the x-coordinate of the position of this icon.
*
* #param xPos the x-coordinate of the position of this icon.
*/
protected void setXPos(int xPos) {
this.xPos = xPos;
}
/**
* Sets the y-coordinate of the position of this icon.
*
* #param yPos the y-coordinate of the position of this icon.
*/
protected void setYPos(int yPos) {
this.yPos = yPos;
}
/**
* Sets the width of this icon.
* <p>
* This method should be called only within the constructor-methods.</p>
*
* #param width the width of this icon.
*/
protected void setWidth(int width) {
this.width = width;
}
/**
* Sets the height of this icon.
* <p>
* This method should be called only within the constructor-methods.</p>
*
* #param height the height of this icon.
*/
protected void setHeight(int height) {
this.height = height;
}
/**
* Sets the extra-icon to be displayed next to this icon.
* <p>
* This method should be called only within the constructor-methods.</p>
*
* #param extraIcon the extra icon to display.
*/
protected void setExtraIcon(Icon extraIcon) {
this.extraIcon = extraIcon;
}
}
package de.dimensionv.java.gui.swing.panels;
import java.awt.Component;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.Icon;
import javax.swing.JTabbedPane;
/**
* A JTabbedPane that can have a close-icon ('X') on each tab.
*
* <p>
* Since it is derived from JTabbedPane directly, it is used in exactly the same manner as the JTabbedPane. By default,
* it even behaves equally, i.e. it does not add the close-icon, thus disabling the closing-capabilities completely for
* a tab. To enable the closing-capabilities of a tab, add a boolean value (true) the the addTab-method-call.</p>
*
* <p>
* To have an additional extra icon on each tab (e.g. showing the file type), use the method addTab(String, Component,
* Icon) or addTab(String, Component, Icon, boolean). The first variant goes without closing-capabilities, while the
* second, if the boolean is set to true, comes with closing-capabilities.</p>
*
* <p>
* Clicking the 'X', of course, closes the tab. If you like to perform action, <b>after</b> the tab has already been
* closed, implement an event-listener to capture the ComponentRemoved-event. The removed tab can be retrieved by
* calling java.awt.event.ContainerEvent.getChild(), which is the event the listener will receive.</p>
*
* #author mjoellnir
*/
public class CloseableTabbedPane extends JTabbedPane implements MouseListener {
/**
* Creates a new instance of ClosableTabbedPane
*/
public CloseableTabbedPane() {
super();
initializeMouseListener();
}
/**
* Appends a tab without closing-capabilities, just as the standard JTabbedPane would do.
*
* #see javax.swing.JTabbedPane#addTab(String title, Component component) addTab
*/
#Override
public void addTab(String title, Component component) {
this.addTab(title, component, null, false);
}
/**
* Appends a tab with or without closing-capabilities, depending on the flag isClosable. If isClosable is true, a
* close-icon ('X') is displayed left of the title.
*
* #param title Title of this tab.
* #param component Contents of this tab.
* #param isClosable en-/disable closing-capabilities
* #see javax.swing.JTabbedPane#addTab(String title, Component component) addTab
*/
public void addTab(String title, Component component, boolean isClosable) {
this.addTab(title, component, null, isClosable);
}
/**
* Appends a tab with or without closing-capabilities, depending on the flag isClosable. If isClosable is true, a
* close-icon ('X') is displayed left of the title. If extraIcon is not null, it will be displayed between the closing
* icon (if present) and the tab's title. The extraIcon will be displayed indepently of the closing-icon.
*
* #param title Title of this tab.
* #param component Contents of this tab.
* #param extraIcon Extra icon to be displayed.
* #param isClosable en-/disable closing-capabilities
* #see javax.swing.JTabbedPane#addTab(String title, Component component) addTab
*/
public void addTab(String title, Component component, Icon extraIcon, boolean isClosable) {
if (isClosable) {
super.addTab(title, new CloseTabIcon(extraIcon), component);
} else {
if (extraIcon != null) {
super.addTab(title, extraIcon, component);
} else {
super.addTab(title, component);
}
}
}
#Override
public void mouseClicked(MouseEvent evt) {
int tabIndex = getUI().tabForCoordinate(this, evt.getX(), evt.getY());
if (tabIndex < 0) {
return;
}
Icon icon = getIconAt(tabIndex);
if ((icon == null) || !(icon instanceof CloseTabIcon)) {
// This tab is not intended to be closeable.
return;
}
Rectangle rect = ((CloseTabIcon) icon).getBounds();
if (rect.contains(evt.getX(), evt.getY())) {
//the tab is being closed
this.removeTabAt(tabIndex);
}
}
#Override
public void mouseEntered(MouseEvent evt) {
}
#Override
public void mouseExited(MouseEvent evt) {
}
#Override
public void mousePressed(MouseEvent evt) {
}
#Override
public void mouseReleased(MouseEvent evt) {
}
private void initializeMouseListener() {
addMouseListener(this);
}
}
Found this sample code which should produce a drawn line after clicking, but does not show anything or works. Assume all import statements are correct, code gives no errors and I have no idea why it would not work. The line color is red while the background is white so it should show clearly if it works. The mouse listener seems to be correct as well. Any reason why this code will not work?
public class PathPanel extends JPanel {
/**
* The panel width.
*/
public static final int WIDTH = 400;
/**
* The panel height.
*/
public static final int HEIGHT = 400;
/**
* The background color of the panel.
*/
public static final Color BACKGROUND_COLOR = Color.WHITE;
/**
* The color to paint with.
*/
public static final Color FOREGROUND_COLOR = Color.RED;
/**
* The line width.
*/
public static final int LINE_WIDTH = 8;
// Instance Fields
/**
*
*/
private static final long serialVersionUID = -3644129903653409515L;
/**
* The path being created.
*/
private final Path2D myPath;
// OR you could use Path2D.Double instead of GeneralPath
// Constructor
/**
* Constructs a new general path panel.
*/
public PathPanel() {
super();
myPath = new GeneralPath();
myPath.setWindingRule(GeneralPath.WIND_EVEN_ODD);
//myPath = new Path2D.Double();
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setBackground(BACKGROUND_COLOR);
addMouseListener(new MyMouseListener());
}
/**
* Paints the current path.
*
* #param theGraphics The graphics context to use for painting.
*/
#Override
public void paintComponent(final Graphics theGraphics) {
super.paintComponent(theGraphics);
final Graphics2D g2d = (Graphics2D) theGraphics;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(FOREGROUND_COLOR);
g2d.setStroke(new BasicStroke(LINE_WIDTH));
g2d.draw(myPath);
}
// Main Method
/**
* Creates and displays a GeneralPathPanel.
*
* #param theArgs Command line arguments (ignored).
*/
public static void main(final String... theArgs) {
final PathPanel panel = new PathPanel();
final JFrame frame = new JFrame("GeneralPathPanel Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// Inner Class
/**
* Listens for mouse clicks, to draw on our panel.
*/
private class MyMouseListener extends MouseAdapter {
/**
* Handles a click event.
*
* #param theEvent The event.
*/
#Override
public void mouseClicked(final MouseEvent theEvent) {
if (myPath.getCurrentPoint() == null) {
myPath.moveTo(theEvent.getX(), theEvent.getY());
} else if (theEvent.getClickCount() == 2) {
myPath.closePath();
} else {
myPath.lineTo(theEvent.getX(), theEvent.getY());
}
repaint();
}
}
}
The Sample Code you posted works fine for me. Have you tried to add a System.out.println() in the mouseClicked(final MouseEvent theEvent) Method to check if it is actually called? If it is not called, you could try to change it to mouseReleased.
The imports I used:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
im using the Canvas class to make a screensaver as a schoolproject.
But the window generated by Canvas doesnt show my objects on it (current time)
until i minimize it an resize it again. After that all things works fine.
so what is wrong?
thank you for coming answers!
with kind regards
leon
those are the classes, i peronally think that the problem is in the class Start or BufferedCanvas
import java.awt.*;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class Start
{
int fensterX = 900;
int fensterY = 700;
Farbengenerator fg = new Farbengenerator();
BufferedCanvas c =newBufferedCanvas("Bild",fensterX,fensterY);
Zeit z = new Zeit();
SchriftParameter sp = new SchriftParameter();
public void zeichneText(){
double x = 100;
double y = 100;
double fy =0.01;
double fx =0.02;
int red=0;
int green=0;
int blue=0;
double colourGrowRate=0.05;
String uhr;
c.setFont(sp.setzteSchrift());
c.setForegroundColour(Color.BLACK);
c.setBackgroundColour(Color.WHITE);
for(int i=0;i<100;i++){
c.drawString("Starting...",(int)x,(int)y);
c.updateAndShow();
try{Thread.sleep(50);}
catch(Exception e){};
c.updateAndShow();
}
CreateButton d = new CreateButton();
d.run();
while(true) {
c.erase();
uhr = z.erstelleZeit();
c.drawString(uhr,(int)x,(int)y);
if((int)x >fensterX-93 || (int)x <5){
fx = fx * (-1);
red=fg.gibROT();
green=fg.gibGRUEN();
blue=fg.gibBLAU();
Color colour = new Color(red,green,blue);
c.setForegroundColour(colour);
}
if((int)y > fensterY-1 || (int)y < 46){
fy = fy * (-1);
red=fg.gibROT();
green=fg.gibGRUEN();
blue=fg.gibBLAU();
Color colour = new Color(red,green,blue);
c.setForegroundColour(colour);
}
if((int)colourGrowRate>=1){
fg.generiereFarbe();
colourGrowRate = 0.05;
}
colourGrowRate=colourGrowRate+colourGrowRate;
x = x + fx;
y = y + fy;
c.updateAndShow();
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferStrategy;
public class BufferedCanvas
{
private JFrame frame;
private CanvasPane canvas;
private Graphics2D graphic;
private Color backgroundColour;
private Image canvasImage;
BufferStrategy buff;
/**
* Create a BufferedCanvas with default height,
width and background colour
* (300, 300, white).
* #param title title to appear in Canvas Frame
*/
public BufferedCanvas(String title)
{
this(title, 300, 300, Color.white);
}
/**
* Create a BufferedCanvas with default background colour (white).
* #param title title to appear in Canvas Frame
* #param width the desired width for the canvas
* #param height the desired height for the canvas
*/
public BufferedCanvas(String title, int width, int height)
{
this(title, width, height, Color.white);
}
/**
* Create a BufferedCanvas.
* #param title title to appear in Canvas Frame
* #param width the desired width for the canvas
* #param height the desired height for the canvas
* #param bgClour the desired background colour of the canvas
*/
public BufferedCanvas(String title, int width, int height, Color bgColour)
{
frame = new JFrame();
canvas = new CanvasPane();
frame.setContentPane(canvas);
frame.setTitle(title);
canvas.setPreferredSize(new Dimension(width, height));
backgroundColour = bgColour;
frame.pack();
frame.createBufferStrategy(2);
buff = frame.getBufferStrategy();
graphic = (Graphics2D)buff.getDrawGraphics();
setVisible(true);
}
/**
* Set the canvas visibility and brings canvas to the front of screen
* when made visible. This method can also be used to bring an already
* visible canvas to the front of other windows.
* #param visible boolean value representing the desired visibility of
* the canvas (true or false)
*/
public void setVisible(boolean visible)
{
if(graphic == null) {
// first time: instantiate the offscreen image and fill it with
// the background colour
Dimension size = canvas.getSize();
canvasImage = canvas.createImage(size.width, size.height);
graphic = (Graphics2D)canvasImage.getGraphics();
graphic.setColor(backgroundColour);
graphic.fillRect(0, 0, size.width, size.height);
graphic.setColor(Color.black);
}
frame.setVisible(true);
}
/**
* Update the canvas and show the new image.
*/
public void updateAndShow(){
buff.show();
}
/**
* Provide information on visibility of the Canvas.
* #return true if canvas is visible, false otherwise
*/
public boolean isVisible()
{
return frame.isVisible();
}
/**
* Draw a given shape onto the canvas.
* #param shape the shape object to be drawn on the canvas
*/
public void draw(Shape shape)
{
graphic.draw(shape);
//canvas.repaint();
}
/**
* Fill the internal dimensions of a given shape with the current
* foreground colour of the canvas.
* #param shape the shape object to be filled
*/
public void fill(Shape shape)
{
graphic.fill(shape);
//canvas.repaint();
}
/**
* Erase the whole canvas.
*/
public void erase()
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
Dimension size = canvas.getSize();
graphic.fill(new Rectangle(0, 0, size.width, size.height));
graphic.setColor(original);
//canvas.repaint();
}
/**
* Erase a given shape's interior on the screen.
* #param shape the shape object to be erased
*/
public void erase(Shape shape)
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
graphic.fill(shape); // erase by filling background colour
graphic.setColor(original);
//canvas.repaint();
}
/**
* Erases a given shape's outline on the screen.
* #param shape the shape object to be erased
*/
public void eraseOutline(Shape shape)
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
graphic.draw(shape); // erase by drawing background colour
graphic.setColor(original);
//canvas.repaint();
}
/**
* Draws an image onto the canvas.
* #param image the Image object to be displayed
* #param x x co-ordinate for Image placement
* #param y y co-ordinate for Image placement
* #return returns boolean value representing whether the image was
* completely loaded
*/
public boolean drawImage(Image image, int x, int y)
{
boolean result = graphic.drawImage(image, x, y, null);
//canvas.repaint();
return result;
}
/**
* Draws a String on the Canvas.
* #param text the String to be displayed
* #param x x co-ordinate for text placement
* #param y y co-ordinate for text placement
*/
public void drawString(String text, int x, int y)
{
graphic.drawString(text, x, y);
//canvas.repaint();
}
/**
* Erases a String on the Canvas.
* #param text the String to be displayed
* #param x x co-ordinate for text placement
* #param y y co-ordinate for text placement
*/
public void eraseString(String text, int x, int y)
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
graphic.drawString(text, x, y);
graphic.setColor(original);
//canvas.repaint();
}
/**
* Draws a line on the Canvas.
* #param x1 x co-ordinate of start of line
* #param y1 y co-ordinate of start of line
* #param x2 x co-ordinate of end of line
* #param y2 y co-ordinate of end of line
*/
public void drawLine(int x1, int y1, int x2, int y2)
{
graphic.drawLine(x1, y1, x2, y2);
//canvas.repaint();
}
/**
* Draws a dot/pixel on the Canvas.
* #param x x co-ordinate of dot
* #param y y co-ordinate of dot
*/
public void drawDot(int x, int y)
{
graphic.drawLine(x, y, x, y);
//canvas.repaint();
}
/**
* Sets the foreground colour of the Canvas.
* #param newColour the new colour for the foreground of the Canvas
*/
public void setForegroundColour(Color newColour)
{
graphic.setColor(newColour);
}
/**
* Returns the current colour of the foreground.
* #return the colour of the foreground of the Canvas
*/
public Color getForegroundColour()
{
return graphic.getColor();
}
/**
* Sets the background colour of the Canvas.
* #param newColour the new colour for the background of the Canvas
*/
public void setBackgroundColour(Color newColour)
{
backgroundColour = newColour;
graphic.setBackground(newColour);
}
/**
* Returns the current colour of the background
* #return the colour of the background of the Canvas
*/
public Color getBackgroundColour()
{
return backgroundColour;
}
/**
* changes the current Font used on the Canvas
* #param newFont new font to be used for String output
*/
public void setFont(Font newFont)
{
graphic.setFont(newFont);
}
/**
* Returns the current font of the canvas.
* #return the font currently in use
**/
public Font getFont()
{
return graphic.getFont();
}
/**
* Sets the size of the canvas.
* #param width new width
* #param height new height
*/
public void setSize(int width, int height)
{
canvas.setPreferredSize(new Dimension(width, height));
Image oldImage = canvasImage;
canvasImage = canvas.createImage(width, height);
graphic = (Graphics2D)canvasImage.getGraphics();
graphic.drawImage(oldImage, 0, 0, null);
frame.pack();
}
/**
* Returns the size of the canvas.
* #return The current dimension of the canvas
*/
public Dimension getSize()
{
return canvas.getSize();
}
/**
* Waits for a specified number of milliseconds before finishing.
* This provides an easy way to specify a small delay which can be
* used when producing animations.
* #param milliseconds the number
*/
public void wait(int milliseconds)
{
try
{
Thread.sleep(milliseconds);
}
catch (Exception e)
{
// ignoring exception at the moment
}
}
/************************************************************************
* Nested class CanvasPane - the actual canvas component contained in the
* Canvas frame. This is essentially a JPanel with added capability to
* refresh the image drawn on it.
*/
private class CanvasPane extends JPanel
{
public void paint(Graphics g)
{
g.drawImage(canvasImage, 0, 0, null);
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
public class CreateButton extends JFrame implements ActionListener{
public void run() {
createAndShowGUI();
}
public CreateButton() {
// set layout for the frame
this.getContentPane().setLayout(new FlowLayout());
JButton button1 = new JButton();
button1.setText("closeApp");
//set actionlisteners for the buttons
button1.addActionListener(this);
// define a custom short action command for the button
button1.setActionCommand("closeApp");
// add buttons to frame
add(button1);
}
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new CreateButton();
//Display the window.
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent ae) {
String action = ae.getActionCommand();
if (action.equals("closeApp")) {
System.exit(1);
}
}
}
import java.awt.*;
public class SchriftParameter
{
public Font setzteSchrift(){
Font f = new Font("Fixed",1,24);
return (f);
}
}
public class Farbengenerator
{
int r=0;
int g=0;
int b=0;
public void generiereFarbe(){
if (r<255&&g==0&&b==0){
r++;
}
else if (r==255&&g<255&&b==0){
g++;
}
else if (r>0&&g==255&&b==0){
r= r-1;
}
else if (r==0&&g==255&&b<255){
b++;
}
else if (r==0&&g>0&&b==255){
g=g-1;
}
else if (r<255&&g==0&&b==255){
r++;
}
else if (r==255&&g==0&&b>0){
b=b-1;
}
}
public int gibROT () {
return(r);
}
public int gibGRUEN () {
return(g);
}
public int gibBLAU () {
return(b);
}
}
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class Zeit
{
public String erstelleZeit(){
DateFormat df = new SimpleDateFormat("HH:mm:ss");
Date d = new Date();
String uhr = df.format(d);
return (uhr);
}
}