@torresdyl
2017-09-14T15:10:55.000000Z
字数 48946
阅读 5012
java
swing
web
snippets
code
所有reflexion方法必须通过getter来取得类中的元素。所以,如果一些元素可以通过reflexion取得, 而另外一些不行,首先要检查getter是否生成了。
If we do
int number = Integer.parseInt("123456");
double i = number / 100;
i
will be 1234.0
, not 1234.56
.
To preserve the decimal part scale, you must convert the number into double first.
double i = ((double)number)/100;
//The order of these lines matters!!!
pack();
setBounds(0, 0, getSize().width, getSize().height);
setLocationRelativeTo(null);
//if you don't want to set the min size of window, better set this. Otherwise, when resizing, unexpected results happen.
setResizable(false);
setVisible(true);
最好的办法是使用MigLayout。用百分比来定义尺寸,这样每次改变窗口尺寸,内部元素都会重新自动排布。
wrap the text with <html></html>
. Nothing more.
Java Swing adds components in an nonintuitive way: the firstly added components overlap the preceding ones. If you add component A first and then component B, both in the same place, A will block B instead of B blocking A. It's very weird and not mentioned in the Oracle DOC........
JTabbedPane
's "tab row" will block components behind them, even if it's not fully filledImagine a JTabbedPane
with 2 tabs on the left. The row where all the tabs are placed has only the left part filled, on the right there're no tabs. But this row is not blank! It will blocked the components behind them, preventing them from user interractions. A button behind this row cannot be clicked, for example.
JTextPane
First you must select the text, and then set the ParagraghAttributes
. Then, if needed, get the caret to top.
/**
* Select all the text of a <code>JTextPane</code> first and then set the line spacing.
* @param the <code>JTextPane</code> to apply the change
* @param factor the factor of line spacing. For example, <code>1.0f</code>.
* @param replace whether the new <code>AttributeSet</code> should replace the old set. If set to <code>false</code>, will merge with the old one.
*/
/* Applied once, and forever changed the line spacing, even if there were no text. So do it at the beginning. */
public static void changeLineSpacing(JTextPane pane, float factor, boolean replace) {
pane.selectAll();
MutableAttributeSet set = new SimpleAttributeSet(pane.getParagraphAttributes());
StyleConstants.setLineSpacing(set, factor);
txtAtributosImpresora.setParagraphAttributes(set, replace);
pane.setCaretPosition(0); //scroll to the top, if it's in a JScrollPane. Usually useful because JTextPane cannot scroll.
}
Better to get the already set ParagraphAttributes
from the text, or set replace
to false
, to prevent the old attributes to be overwritten.
JList
list.setFixedCellHeight(30); //in pixeles
JTable
custom renderer: Icon opaque to show background colorActually it's easy. We have two styles in even and uneven lines. To show the background color in a cell where we see Icons, we must setOpaque(true)
to see it. If not, the background is white.
I think that in the renderer, when creating the JLabel
to render the cell, by default it occupies the whole cell, in spite that the icon image can be smaller. So the renderer as JLabel
must show the pixeles behind the white part around the image.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
public class IconTableWithTwoColores extends JFrame {
private static final String imgPath = "img/carpeta.png";
public IconTableWithTwoColores() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = new JPanel();
content.setLayout(null);
JTable table = new JTable();
table.setFillsViewportHeight(true);
table.setPreferredScrollableViewportSize(new Dimension(300, 300));
ImageIcon icon1 = new ImageIcon(imgPath);
table.setModel(new DefaultTableModel(new Object[][] {{icon1, "no way!"}, {icon1, "now what"}}, new String[] {"Col 1", "Col 2"}));
class MyRenderer extends JLabel implements TableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
JLabel label = new JLabel();
if (row % 2 == 0) {
label.setBackground(Color.pink);
} else {
label.setBackground(Color.green);
}
if (value instanceof Icon) {
label.setIcon((Icon)value);
//if this line is commented, you won't see the background color of the cell
label.setOpaque(true);
return label;
} else {
label.setText(value.toString());
label.setOpaque(true);
return label;
}
}
}
table.setDefaultRenderer(Object.class, new MyRenderer());
JScrollPane scroll = new JScrollPane(table);
scroll.setVisible(true);
scroll.setBounds(0, 0, 300, 300);
content.add(scroll);
getContentPane().add(content);
pack();
setVisible(true);
setBounds(0, 0, 300, scroll.getHeight());
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
IconTableWithTwoColores frame = new IconTableWithTwoColores();
}
});
}
}
最好的办法是使用MigLayout。用百分比来定义尺寸,这样每次改变窗口尺寸,内部元素都会重新自动分布。
使用CardLayout
. 这是Swing自带的,不用加jar。
JPanel parent = new JPanel(new CardLayout());
parent.add(a, "panelA");
parent.add(b, "panelB");
JButton button1 = new JButton("Toggle A");
button1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout)parent.getLayout();
cl.show(parent, "panelA");
}
});
JButton button2 = new JButton("Toggle B");
button2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout)parent.getLayout();
cl.show(parent, "panelB");
}
});
JSplitPane
divider weight/percentage
splitPane.setResizeWeight(0.333);
It will make the first panel sized to the half of the second panel.
Get the default renderer and do changes to it, and return it. Remember: now it's your responsibility to apply all the styles according to the data type/column index, etc. Only the uneven/even row background is preserved, as what we see in Nimbus L&F.
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
//do changes to label
return label;
}
Font
You can register one font in the GraphicEnvironment
and use it. It applies only to the scope of this application, and will not affect other Java applications, neither other programs, because it doesn't install something new into the system.
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Font custom1 = Font.createFont(Font.TRUETYPE_FONT, new File("resources/fonts/YaHei Consolas Hybrid 1.12.ttf"));
Font custom2 = custom1.deriveFont(14f).deriveFont(Font.BOLD);
ge.registerFont(custom2);
就算不register,也可以用的,但是scope就限制了。
JSplitPane
golden rules for sizinghttp://docs.oracle.com/javase/tutorial/uiswing/components/splitpane.html#rules
too much to discuss.
http://stackoverflow.com/questions/7613582/order-of-listeners-in-java
Listeners are NOT guaranteed to fire in certain order. Use other mecanisms to ensure it.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
public class CompositeActionListenerWithPriorities implements ActionListener {
private Map<Integer, ArrayList<ActionListener>> listeners =
new TreeMap<Integer,ArrayList<ActionListener>>();
@Override
public void actionPerformed(ActionEvent e) {
TreeSet<Integer> t = new TreeSet<Integer>();
t.addAll(listeners.keySet());
Iterator<Integer> it = t.descendingIterator();
while(it.hasNext()){
int x = it.next();
ArrayList<ActionListener> l = listeners.get(x);
for(ActionListener a : l){
a.actionPerformed(e);
}
}
}
public boolean deleteActionListener(ActionListener a){
for(Integer x : listeners.keySet()){
for(int i=0;i<listeners.get(x).size();i++){
if(listeners.get(x).get(i) == a){
listeners.get(x).remove(i);
return true;
}
}
}
return false;
}
public void addActionListener(ActionListener a, int priority){
deleteActionListener(a);
if(!listeners.containsKey(priority)){
listeners.put(priority,new ArrayList<ActionListener>());
}
listeners.get(priority).add(a);
}
}
/**
* Adjust and configure the preferer size for each column of a table, according to
* the length of each header cell's text. This methods does not ensure that the container
* of the table can show all the text, because the panel can be too small.
* @param table the table with the data model to set width
* @param panel the JPanel containing the table
* @param identifiers column header names
*/
public static void configPreferredSizeForColumns(JTable table, JPanel panel, Vector<String> identifiers) {
Graphics g = panel.getGraphics();
int width = 0;
for (int i=0; i < identifiers.size(); i++) {
width = (int)g.getFontMetrics().getStringBounds(identifiers.get(i), g).getWidth();
table.getColumnModel().getColumn(i).setPreferredWidth(width);
}
}
Table must have DEFAULT_CURSOR
to make the two-headed cursor visible. (Or you will see your HAND_CURSOR or whatever when the mouse pointer hovers over column header edge)
You must set preferredWidth
to all columns. It's better to measure them with a screen pixel ruler, and do this:
public void setAnchoColumnasLiq() {
TableColumn columnaTabla = null;
int anchoColumna = 0;
for (int i = 0; i < table.getColumnCount(); i++) {
columnaTabla = table.getColumnModel().getColumn(i);
switch (i) {
case 0:
anchoColumna = 22;
break;
case 1:
anchoColumna = 129;
break;
case 2:
anchoColumna = 137;
break;
case 3: //razon social, el mas ancho
anchoColumna = 215;
break;
case 4:
anchoColumna = 97;
break;
case 5:
anchoColumna = 40;
break;
case 6:
anchoColumna = 111;
break;
default:
break;
}
columnaTabla.setPreferredWidth(anchoColumna);
columnaTabla.setResizable(false);
}
}
The maxisumWidth
and minimumWidth
are not so important. But preferredWidth
is the base of resizing.
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
. Swing will increase/decrease the width of each column proportionally (according to the preferredWidth
) to fill the whole width of the JScrollPane. If not, there will be blank overflowing at the right.JTable
This is funny: you must keep removing the first row because the change is reflected inmediately in the table model. Due to the same reason, you must record the original row count, to avoid remove just half the rows. For example, the code below is not correct:
/**
* Clear the table model of a JTable, and call <code>fireTableDataChanged()</code>.
*/
public static void clearJTableData(JTable table){
TableModel model = table.getModel();
if (model.getRowCount() > 0) {
DefaultTableModel model1 = (DefaultTableModel)model;
for (int i=0; i < model1.getRowCount(); i++) {
model1.removeRow(i);
}
model1.fireTableDataChanged();
}
}
You must do:
/**
* Clear the table model of a JTable, and call <code>fireTableDataChanged()</code>.
*/
public static void clearJTableData(JTable table){
TableModel model = table.getModel();
if (model.getRowCount() > 0) {
DefaultTableModel model1 = (DefaultTableModel)model;
//must preserve the model's vector's size, or only half of the rows will be removed.
int size = model1.getRowCount();
for (int i=0; i < size; i++) {
model1.removeRow(0);
}
model1.fireTableDataChanged();
}
}
First, it's of type DefaultTableCellRenderer
. There is a class sun.swing.table.TableCellHeaderRenderer
, but it's not what we want.
And, to get a default renderer, you must call table.getTableHeader().getDefaultRenderer()
, and pass it as parameter/use it in your inner class. It's for preserving all the border/color/etc. of default table header.
At last, use it and setText(value.toString())
, or it will be blank.
Cast it to JLabel
to setHorizontalAlignment()
. Remember to return
it and set it back to table.getTableHeader().setDefaultRenderer()
.
final DefaultTableCellRenderer headerRenderer = (DefaultTableCellRenderer)table.getTableHeader().getDefaultRenderer();
table.getTableHeader().setDefaultRenderer(new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
DefaultTableCellRenderer r = headerRenderer;
r.setFont(ARIAL_PLAIN_14);
r.setText(value.toString()); //must set. Or the header is blank.
switch (column) {
case 0:
case 2:
case 3:
r.setHorizontalAlignment(SwingConstants.CENTER);
return r;
default:
r.setHorizontalAlignment(SwingConstants.LEADING);
return r;
}
}
});
The default behaviour of Nimbus stripped table is: first line white, second line light grey, but it's not good if there're less rows than the preferred size, and the area without data in a JScrollPane needs to be painted white. (If the line number is uneven, the last line is white, same as the background). But, with some changes, we can make a JTable paint a stripped table as below:
We must know that:
table.setBackground()
will set all lines to be a color, ruining the stripped table style. It's useless in our case.pane.getViewport().setBackground(Color.WHITE);
.pane.getViewport().setOpaque(true)
.opaque
to false
. Or it will paint the white lines.setFillsViewportHeight(false/true)
does not affect.
package com.WindThunderStudio.JTableWithStripe;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class JTableWithStripe extends JFrame {
class CustomRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
// TODO Auto-generated method stub
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
return c;
}
}
public JTableWithStripe() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setBackground(Color.green);
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
}
JTable table = new JTable();
table.setFillsViewportHeight(true);
table.setOpaque(false);
table.setBorder(BorderFactory.createLineBorder(Color.RED, 2));
DefaultTableModel model = new DefaultTableModel();
model.setColumnIdentifiers(new String[] {"Class", "Age", "Name"});
model.addRow(new String[] {"A", "43", "Nostrom"});
model.addRow(new String[] {"B", "24", "Alicia"});
model.addRow(new String[] {"C", "28", "Sulaco"});
model.addRow(new String[] {"", "", ""});
model.addRow(new String[] {"", "", ""});
model.addRow(new String[] {"", "", ""});
model.addRow(new String[] {"", "", ""});
model.addRow(new String[] {"", "", ""});
table.setModel(model);
// table.setBackground(Color.YELLOW);
table.setDefaultRenderer(Object.class, new CustomRenderer());
JScrollPane pane = new JScrollPane(table);
pane.getViewport().setPreferredSize(new Dimension(500, 300));
// pane.getViewport().setBackground(Color.MAGENTA);
pane.getViewport().setBackground(Color.WHITE);
pane.getViewport().setOpaque(true);
pane.setPreferredSize(new Dimension(500, 300));
pane.setOpaque(true);
add(pane, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setVisible(true);
System.out.println(table.getBackground());
System.out.println(pane.getViewport().getBackground());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JTableWithStripe frame = new JTableWithStripe();
}
});
}
}
Although it seesm odd, a ChangeListener fires when the mouse cursor enters the component, without clicking it. Apparently, it detects mouseEntered()
too, without the API mentioning it.
If you only want to detect the click, use ItemListener
.
This is because a JFrame has a border space around it for drawing resizing handler when set to resizable. When it is not resizable, it looks like an extra border; when it is resizable, a line is drawn onto it to indicate it is draggable. It has a size of 10 pixels wide.
Java Metal L&F draws it explicitly, while all other L&F does not. What they do, is: when a JFrame is set to be resizable, it omit it and change the frame's size to accomodate to original size. That's why we see the size change. We can reproduce the bug by setting the L&F to be Metal, and setDefaultLookAndFeelDecorated(true)
.
Code to reproduce the case:(play with L&F to see the difference)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
public class TitleHeightChange extends JFrame {
private static final String lp = System.lineSeparator();
public TitleHeightChange() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setDefaultLookAndFeelDecorated(true);
try {
// UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
// UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
// UIManager.setLookAndFeel(new MetalLookAndFeel());
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
} catch (Exception e) {
}
final JFrame frame1 = new JFrame();
frame1.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame1.setTitle("Frame1");
frame1.setLayout(new BorderLayout());
final JTextArea area1 = new JTextArea();
area1.setBorder(BorderFactory.createLineBorder(Color.darkGray,1));
frame1.add(area1, BorderLayout.CENTER);
frame1.addComponentListener(new ComponentListener() {
@Override
public void componentShown(ComponentEvent e) {
// TODO Auto-generated method stub
area1.setText("frame height: " + frame1.getBounds().height + lp
+ "frame width: " + frame1.getBounds().width + lp
+ "content pane height: " + frame1.getContentPane().getBounds().height + lp
+ "content pane width: " + frame1.getContentPane().getBounds().width + lp
+ "title bar height: " + (frame1.getBounds().height-frame1.getContentPane().getBounds().height) + lp
+ "isResizable() value: false");
}
@Override
public void componentResized(ComponentEvent e) {
// TODO Auto-generated method stub
}
@Override
public void componentMoved(ComponentEvent e) {
// TODO Auto-generated method stub
}
@Override
public void componentHidden(ComponentEvent e) {
// TODO Auto-generated method stub
}
});
frame1.setResizable(false);
frame1.pack();
frame1.setBounds(0, 0, 300, 300);
frame1.setLocationRelativeTo(null);
frame1.setVisible(true);
final JFrame frame2 = new JFrame();
frame2.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame2.setTitle("Frame2");
frame2.setLayout(new BorderLayout());
final JTextArea area2 = new JTextArea();
area2.setBorder(BorderFactory.createLineBorder(Color.darkGray,1));
frame2.add(area2, BorderLayout.CENTER);
frame2.addComponentListener(new ComponentListener() {
@Override
public void componentShown(ComponentEvent e) {
// TODO Auto-generated method stub
area2.setText("frame height: " + frame2.getBounds().height + lp
+ "frame width: " + frame2.getBounds().width + lp
+ "content pane height: " + frame2.getContentPane().getBounds().height + lp
+ "content pane width: " + frame2.getContentPane().getBounds().width + lp
+ "title bar height: " + (frame2.getBounds().height-frame2.getContentPane().getBounds().height) + lp
+ "isResizable() value: true");
}
@Override
public void componentResized(ComponentEvent e) {
// TODO Auto-generated method stub
}
@Override
public void componentMoved(ComponentEvent e) {
// TODO Auto-generated method stub
}
@Override
public void componentHidden(ComponentEvent e) {
// TODO Auto-generated method stub
}
});
frame2.setResizable(true);
frame2.pack();
frame2.setBounds(0, 0, 300, 300);
frame2.setLocationRelativeTo(null);
frame2.setVisible(false);
setLayout(new BorderLayout());
JButton b = new JButton();
b.setText("switch");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if (frame1.isVisible()) {
frame1.setVisible(false);
frame2.setVisible(true);
} else {
frame1.setVisible(true);
frame2.setVisible(false);
}
}
});
b.setPreferredSize(new Dimension(100, 30));
b.setSize(new Dimension(100, 30));
add(b, BorderLayout.CENTER);
pack();
setBounds(600, 700, 100, 30);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
TitleHeightChange frame = new TitleHeightChange();
}
});
}
}
The only way to fix, is to change the resizable window to be 10 pixels wider and higher to maintain the size. Cannot change the unresizable windows. Reason unknown.
remove()
component needs revalidate()
and repaint()
After removing an component already visible, we must revalidate()
to tell the Layout Manager to allocate it, and repaint()
to make it completely visible.
If we add another component in the same place, the same must be done to the new component, too.
JTree
paint problemIf when adding nodes in to a JTree
, we see paint problems(processing and paint are synchronizedly and new nodes are not painted, left in blank), we can add SwingUtilities.invokeAndWait()
to force waiting for paint.
When inserting node, insertNodeInto
will inform the listeners, so this part can be left safely.
At last, we may need to call model.reload()
to repaint all nodes.
getText()
of JTextPane adds extra blank lines if we touch its Document
underneathIt is a bug I found:
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8180276
Custom class extending JTextPane
to fix this problem:
public class MyJTextPane extends JTextPane {
/**
* Append some text to this pane.
* @param s
*/
public void append(String s) {
try {
Document doc = this.getDocument();
doc.insertString(doc.getLength(), s, this.getParagraphAttributes());
} catch(BadLocationException e) {
System.err.println(e);
}
}
/**
* Append some text and change line.
* @param s
*/
public void appendLine(String s) {
try {
Document doc = this.getDocument();
doc.insertString(doc.getLength(), s + System.lineSeparator(), this.getParagraphAttributes());
} catch(BadLocationException e) {
System.err.println(e);
}
}
@Override
public String getText() {
String string = "";
try {
string = this.getDocument().getText(0, this.getDocument().getLength());
} catch (BadLocationException e) {
System.err.println(e);
}
return string;
}
/**
* Clear the text content.
*/
public void clearText() {
try {
this.getDocument().remove(0, this.getDocument().getLength());
} catch (BadLocationException e) {
System.err.println(e);
}
}
}
Normally we use JOptionPane.showConfirmDialog
to show a confirm/cancel dialog. But the text in the buttons are in English.
To customize them with other texts, we use:
int javax.swing.JOptionPane.showOptionDialog(Component parentComponent, Object message, String title, int optionType, int messageType, Icon icon, Object[] options, Object initialValue) throws HeadlessException
For example, to make a centered dialog with two buttons, whose text are "Good!" and "Bad...", I have:
String[] options = new String[]{"Good!", "Bad..."};
int status = JOptionPane.showOptionDialog(
null, // parent of dialog. Can be null and is centered.
"Do you want some milk?", //dialog question message
"A silly question", //dialog title
JOptionPane.OK_CANCEL_OPTION, //dialog type
JOptionPane.QUESTION_MESSAGE, //message type
null, //question mark icon, null for default question mark
options, //different text in buttons
options[0]); //default selected button
Code:
for (LookAndFeelInfo lf: UIManager.getInstalledLookAndFeels()) {
System.out.println(lf.getName());
System.out.println(lf.getClassName());
}
Result: (Windows 10 Home, Java 1.8.111):
Metal
javax.swing.plaf.metal.MetalLookAndFeel
Nimbus
javax.swing.plaf.nimbus.NimbusLookAndFeel
CDE/Motif
com.sun.java.swing.plaf.motif.MotifLookAndFeel
Windows
com.sun.java.swing.plaf.windows.WindowsLookAndFeel
Windows Classic
com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel
To set a l&f:
try {
UIManager.setLookAndFeel(new MetalLookAndFeel());
} catch (UnsupportedLookAndFeelException e) {
log.error("Look&Feel not found: ", e);
}
Some Look and feel exists as class, like Nimbus and Metal. For Windows L&F, we need class name.
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
log.error("Look&Feel not found: ", e);
}
ImageIcon icon = new ImageIcon(Constantes.ICONO_RENDER_SEGURIDAD_SOCIAL);
lblSegSocial.setIcon(new ImageIcon(icon.getImage().getScaledInstance(113, 35, Image.SCALE_SMOOTH)));
Use setFont()
on the JTabbedPane to change layer title font. Not on layers.
red line: cells
blue line: components
green line: preferred size (??)
It depends on the biggest component it holds. Please, check if there're some big components, not just check one line/one column, but also somewhere else. **In JTabbedPane, some big tab will increase the size of other tabs. And in CardLayout
, also. Some card will increase the total size of the container. **
"cell 0 1", first col, then row.
"cell 0 1 2 3", col, row, width, height.
Don't do anything about alignment in col/row constraint. "align left" not working. Instead, when adding the component, add gapleft push
. There must be a gap on the left, though. But setting insets will do it.
If, we have more then 1 button in a row, even if we specify the size of every cell width, and only add component cons like gapleft push
, will fail: the row will be divided into cells with equal width to allocate elements. We must add []push[]
in the column constraint.
JTable
/JTabbedPane
/JTextfield
, etc.(Components that don't have fixed size. ) JLabel
will be shrink to the text size. JButton
also. But JScrollPane
will need a size(when adding them or in col/row constraints), and JTabbedPane
also. JTextfield
needs a width.setBounds()
. Let MigLayout handle it. If necessary, just assign a fixed width and compact the height:
setBounds(0, 0, 1000, getSize().height);
//this will prevent the elements shrink to 0 width, but pack them with proper height.
The reason why we do this, is that the textfields usually must extend horizontally even without content, but the height of tables cannot extend to maximum. Using this, all component will extend horizontally and shrink vertically, table will be their preffered height.
split
and alignmentsplit 2
will divide a cell into 2, with equal width, default align (left). If adding comps into the first cell with grow
, the first cell will be larger than needed, larger than the half size, and the second will be pushed to the far right side. If you want the first cell shrink to proper size and the two components sticking together, don't use grow
.
To add an element with relative position, we have pos x y [x2] [y2]
comp constraints.
add(btnGuardarConfig, "pos (tabsPane.x+tabsPane.w-120) (tabsPane.y), grow");
//add it to the right top corner above. 120 is inset to right.
//if x2 and y2 are not present, they will be taken as null and the comp will be preferred size.
When to assign bounds to components, is decided by the manager layout. In MigLayout, this is done after pack()
I guess. So, when adding elements, pack()
is not done, and they will have a size of 0*0. So, the relative positioning like this will not work without an actual size of the comps with id tabsPane
. What we can do is add a size to the tabsPane
when adding it:
add(tpConfiguracion, "cell 0 1, grow, id tabsPane, w :" + frameWidth + ":, h :" + frameHeight + ":");
//note the ':' part. Here we only set the preferred size. "min:pref:max"
If you want to change col/row size when resizing, you must specify the percentage of column width or row height when defining the layout. Also, no ComponentListener
implementing ComponentResized()
should be added.
And, when calculating the percentages, make sure not to make the sum 100%
. Leave some spaces for insets.
Add fill, grow
in the column/row constraint to make some column/row occupy the extra space when the window resizes, especially when maximized.
If we add two components in a cell like:
panel.add(label, "align 10%");
panel.add(textField, "grow");
The field will grow and the label will not have space on the left. It just will be pushed to the left. So we must define the left gap and the push priority group (the gap higher and the textField lower.) Or, just make the left gap push, and textfield with some fixed width.
MigLayout
handles the maximize behaviour, if set properlyIn the new CEL in the WinCreta HEAD and in the branch UNI_BUZ, the CEL can handle the location and size of all elements when the windows is maximized: ocupy the whole width, table increases its size while other panels doesn't change the height. Here is the code:
/**
* Clase de Panel para mostrar informaciones detalladas del nuevo tipo de
* XML: CEL.
*
* @author 99GU6879
*
*/
public class PanelConsultaEstadoLiquidacion extends JFrame {
public PanelConsultaEstadoLiquidacion(File f) {
xmlFile = f;
initialize();
}
public void initialize() {
primeraBusqueda = true;
PanelImagen panelImagen = new PanelImagen();
panelImagen.setLayout(new MigLayout("insets 0 0 0 0, fillx, debug", "[center]", "[]5[]5[300:400:800, grow, fill]5[]5[::30]"));
getContentPane().add(panelImagen);
setPreferredSize(new Dimension(panelWidth,panelHeight));
if (Gestor_Interface.contenedorLiquidacionDirecta != null) {
setBounds(Gestor_Interface.getContenedorLiquidacionDirecta().getX(), Gestor_Interface.getContenedorLiquidacionDirecta().getY(), 1190, 600);
} else {
setBounds(Gestor_Interface.getContenedorCRETA().getX(), Gestor_Interface.getContenedorCRETA().getY(), 1190, 600);
}
// pane de cabecera, linea 0, para tabla de resumen.
panelCabecera = new JPanel();
panelCabecera.setLayout(new MigLayout("insets 2 2 2 2, fillx, debug",
"[15%, grow]5push[20%, grow]5push[15%, grow]5push[40%, grow]5[10%, center]",
"[20!]5[25!]5[25!]"));
panelCabecera.setBorder(null);
lblInfoConsultas = new JLabel("Consulta Estado de Liquidaciones");
panelCabecera.add(lblInfoConsultas, "cell 0 0, span 5, grow");
// Tabla de resumen de las liquidaciones, la superior
panelCabecera.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblResumenAuto", "Autorizado: "), "cell 0 1, grow");
panelCabecera.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblResumenAutoResult", ""), "cell 1 1, grow");
panelCabecera.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblResumenRazonSocial", "Raz\u00F3n Social: "), "cell 2 1, grow");
panelCabecera.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblResumenRazonSocialResult", ""), "cell 3 1, grow");
panelCabecera.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblResumenPeriodo", "Periodo Presentaci\u00F3n: "), "cell 0 2, grow");
panelCabecera.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblResumenPeriodoResult", ""), "cell 1 2, grow");
panelCabecera.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblResumenFechaHora", "<html>Fecha y Hora <br> de Generaci\u00F3n: </html>"), "cell 2 2, grow");
panelCabecera.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblResumenFechaHoraResult", ""), "cell 3 2, grow");
btnLimpiar = new JButton();
btnLimpiar.setBounds(900, 30, 60, 30);
panelCabecera.add(btnLimpiar, "cell 4 1 1 2, center");
panelImagen.add(panelCabecera, "cell 0 0, grow, h 90!");
Object[][] filtroData = new Object[1][7];
DefaultTableModel model = new DefaultTableModel(filtroData,
cabeceraTablaParcial);
tbFiltro = new JTable(model);
tbFiltro.setBounds(5, 90, 970, 40);
tbFiltro.setPreferredSize(new Dimension(970,40));
tbFiltro.setPreferredScrollableViewportSize(tbFiltro.getPreferredSize());
spFiltro = new JScrollPane(tbFiltro, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
spFiltro.setViewportView(tbFiltro);
spFiltro.setBackground(new Color(0, 0, 0, 0));
spFiltro.getViewport().setOpaque(false);
spFiltro.setBorder(null);
spFiltro.setBounds(5, 95, 970, 40);
panelImagen.add(spFiltro, "cell 0 1, grow, h 50!");
Object[][] filtroData2 = new Object[1][14];
tbDetalleLiqs = new JTable(model2);
tbDetalleLiqs.setSize(new Dimension(1000, 420));
tbDetalleLiqs.setPreferredScrollableViewportSize(new Dimension(1000, 420));
tbDetalleLiqs.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
// Panel que contiene la tabla de los detalles de las liquidaciones
spDetalleLiqs = new JScrollPane();
spDetalleLiqs.setBounds(5, 155, 970, 400);
spDetalleLiqs.setViewportView(tbDetalleLiqs);
spDetalleLiqs.getViewport().setOpaque(false);
panelImagen.add(spDetalleLiqs, "cell 0 2, grow");
// Pane totalizador
panelTotalizador = new JPanel();
panelTotalizador.setOpaque(false);
panelTotalizador.setBounds(5, 560, 970, 65);
panelTotalizador.setLayout(new MigLayout("insets 2 2 2 2, fill, debug", "[15%]3[15%]push[15%]3[15%]push[15%]3[15%]", "[]2[]" ));
// Totalizadores
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblTotalCCC", "Total CCC: "), "cell 0 0, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblTotalCCCResult", ""), "cell 1 0, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblTotalLiqs", "Tot. Liquidaciones: "), "cell 2 0, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblTotalLiqsResult", ""), "cell 3 0, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblTotalImporte", "Tot. Importe: "), "cell 4 0, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblTotalImporteResult", ""), "cell 5 0, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblTotalTramos", "Tot. Tramos: "), "cell 0 1, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblTotalTramosResult", ""), "cell 1 1, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblTotalTramoCal", "Tot. Tramos Cal: "), "cell 2 1, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblTotalTramoCalResult", ""), "cell 3 1, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_TITLE, "lblTotalTramoNoCal", "Tot. Tramos No Cal: "), "cell 4 1, grow");
panelTotalizador.add(setUnifiedStyle(Constantes.LABEL_CONTENT, "lblTotalTramoNoCalResult", ""), "cell 5 1, grow");
panelImagen.add(panelTotalizador, "cell 0 3, grow, h 65!");
btnSalir = new JButton("");
panelImagen.add(btnSalir, "cell 0 4, gapleft push, w 130!");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLocationByPlatform(true);
setResizable(true);
pack();
setBounds(0, 0, 1000, getSize().height);
setLocationRelativeTo(null);
//to show the content and then show the windows
showCEL(xmlFile);
setVisible(true);
}
If we don't specify any size when adding the components and the two panels, the first panel (on the left/top) will be bigger than it should be and the total size of windows will increase.
To avoid this, we must specify the height of top panel/the width of left panel. For example, in Siltra I have set the width of all buttons/textfields of PanelConsultaLiquidaciones
to give the left panel a fixed width, and the windows goes back to normal. If not, it's wider than it should be. No extra setBounds()
setSize()
is used.
In the row/column constraint, we add fill, grow
. Both.
If some panel has failed, and is a Card
, check if the panel with CardLayout
has done this. The container panel control the total size of extra space all the children can ocupy.
JLabel
with html text always occupy the whole cell lengthMigLayout has problem calculating the length if a JTextPane enables text wrapping, or a JLabel
has html text in it to enable inside-label wrapping. You can specify width when adding. w 200!
and w :200:
both do the trick.
JFrame
maximized and cannot return to original sizeIn Siltra, when you maximized the frame and return to original size, the width is correct but some buttons are hidden at the bottom. Clicking at the border to adjust a little and all is good. (Panels are almost with MigLayout
, those without it has a smaller preferred size and the frame has ComponentListener
. JTabbedPane's tabs too.)
I suspect it to be a MigLayout problem: when returning to normal size, all the panels decrease and no time to calculate the proper preferred size for all. Some panels stay behind and have a larger size. But setting all the panels a smaller preferred size doesn't work. (Overriding the getPreferredSize()
)
So I override the frame's preferred size, add 80 pixels to the height and all good. Reason unknown.
In PanelMensajesInforme, the panelFiltro is too high/long and the first time the panel is shown, the split pane containing it has no bottom border (cut off). I changed some rows' gap to be 0
in the panelFiltro and the split pane is no longer too high. Just check every panel's bounds in a container. If some rows/columns are defined like:
[][][]
the gap is not 0. It's 2 or something. To set it to 0
you must set it explicitly.
[]0[]0[]
And, defining line height/column width with percentages and pixels(mixing these two) may also be a problem. In the layout constraints, define them with pixeles only. [7%]::10:push[25!]
will cause problem. Just use [25!]::10:push[25!]
.
JTextArea
with line wrap will grow to maximum size if added with grow
JTextArea
will reset its minimum size every time its container changes size. It causes problems because it will increase size every time its container's size changes if is added with grow
. But without grow
, it will shrink.
To make it occupy all extra space and respect container's size control, e.g., to ocuppy only 50%
of total width, we must add wmin 0
to prevent minimum size change.
package gui.paneles.elementos;
import java.awt.Color;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JFormattedTextField;
import org.apache.commons.lang.StringUtils;
/**
* The subclass of <code>JFormattedTextField</code> for passing a <code>String</code>
* as the colored format hint.
*
* @author 99GU6879
*
*/
public class FormattedTextFieldWithHint extends JFormattedTextField {
/**
*
*/
private static final long serialVersionUID = 1L;
public static final Color hintGrey = new Color(195,195,195);
public static final Color textBlack = Color.BLACK;
public static final String hint_mmaaaa = "mm/aaaa";
private String hint;
public String getHint() {
return hint;
}
public void setHint(String hint) {
this.hint = hint;
}
public FormattedTextFieldWithHint() {
super();
this.addFocusListener(new FocusListenerForHint());
}
public FormattedTextFieldWithHint(String hint) {
this(); //will add focusListener as well.
this.hint = hint;
this.setText(hint);
resetPlaceholder();
}
public void resetPlaceholder(){
if (this.getText().equals(hint)){
this.setForeground(hintGrey);
} else {
this.setForeground(textBlack);
}
}
public void readyForInput(){
this.selectAll();
this.setForeground(textBlack);
}
/**
* Abstract ancestor for all the focus listeners that
* want to manipulate the behaviours about the hints.
*/
public class FocusListenerForHint implements FocusListener {
@Override
public void focusGained(FocusEvent e) {
if (e.getSource() instanceof JFormattedTextField) {
JFormattedTextField field = (JFormattedTextField) e.getSource();
field.setForeground(textBlack);
if (field.getText().trim().equals(hint)) {
field.setText(""); //if only hint is present, make it dissapear.
} else {
readyForInput();//if some other text is present.
}
}
}
@Override
public void focusLost(FocusEvent e) {
if (e.getSource() instanceof JFormattedTextField) {
JFormattedTextField field = (JFormattedTextField) e.getSource();
if (StringUtils.isBlank(field.getText())) {
field.setForeground(hintGrey); //change color back to grey.
field.setText(hint); //if blank, make it back to hint.
}
}
}
}
}
grow 30
vs grow 30%
grow 30
is different from grow, 30%
.
According to http://stackoverflow.com/questions/23001696/miglayout-column-constraint#answer-23028022, the former is about eagerness of occupying the space left out of the component(will occupy 30% of the blank space between two components), while grow, 30%
means "occupy all the space which is left, and this col/row is 30% of the total width/height".
File
path starting point, and relative/absolute pathWhen talking about Java File
path, there are relative ones and absolute ones.
Absolute ones are those begining with drive (in Windows) and /
(in Linux). So below are two absolute paths:
C:\Windows\system32\test.txt
\home\usr\myDir\result.txt
Relative paths are those starting from the working directory, represented by .
. It is often the root path of your Java project. So new File("test.txt")
is the same as new File(".\\test.txt");
.
From Javadoc:
By default the classes in the java.io package always resolve relative pathnames against the current user directory. This directory is named by the system property
user.dir
, and is typically the directory in which the Java virtual machine was invoked.
If you have a Maven project like this:
MyProject
└ src
└ main
└ java
└ com.package.Clazz
└ resources
└ settings.properties
You can access settings.properties
by this line:
File f = new File("src\main\resources\settings.properties");
Old ways in Java
split(str)
cut off trailing empty elementsIf we have dd/dd///
, and we use str.split("/")
, we will have:
{"dd", "dd"};
instead of
{"dd", "dd", "", "", ""}
Because split(s)
will cut off all the empty elements at last.
If we want to keep them, use str.split("/", -1)
. (-1
can be any negative value).
http://stackoverflow.com/questions/14602062/java-string-split-removed-empty-values
SimpleDateFormat
The SimpleDateFormat
takes into consideration the time the Gregorian calendar is introduced (year 1582). Before that, a year divided into 4 is a leap year. So, year 1000 is a leap year, but 1700 is not.
So, for SimpleDateFormat
, 29/02/1400 is a valid date, while 29/02/1700 is not. (if setLenient(false)
. If this is false
, the formatter will not convert 32 of January into 1 of February)
With regex, we can refine the pattern. To test if a String matched the pattern ##/##/####
(#
for a number):
Pattern p = Pattern.compile("\\d{2}/\\d{2}/\\d{4}");
Matcher m = p.matcher(string);
if (m.matches()) {
return true;
}
For more information about greedy, reluctant and prossessive quantifier, see here.
The max value of an Integer
is , 2147483647
and min value is , -2147483648
. If we don't know if the String parsed is in this range:
try {
Integer i = Integer.parseInt(s);
result = f.format(i / 100);
} catch (NumberFormatException e) {
Long l = Long.parseLong(s);
result = f.format(l / 100);
}
NumberFormat
With only NumberFormat
we can format the number, if the Locale
, group divider, etc. are set. But, if we want to keep the fraction part, we must take care to convert the number first into double
, then divide to 100. See the basic part.
/**
* Formatear el literal en formato de numeros segun el Locale de Espana.
*
* @param s el literal a parsear
* @param isInteger si se parsea el valor como Integer. Si no, lo parsea como double de dos digitos de decimal.
* @return el numero parseado. "1234567" -> "1.234.567". "1234.56" -> "1.234,56".
*/
private String formatWithLocale(String s, boolean isInteger) {
String result = "";
try {
NumberFormat f = NumberFormat.getInstance(new Locale("es", "ES"));
f.setGroupingUsed(true);
if (isInteger) {
f.setMaximumFractionDigits(0);
f.setMinimumFractionDigits(0);
f.setParseIntegerOnly(true);
try {
Integer i = Integer.parseInt(s);
result = f.format(i);
} catch (NumberFormatException e) {
Long l = Long.parseLong(s);
result = f.format(l);
}
} else { //para double. El parametro ya tiene dos decimales.
f.setMaximumFractionDigits(2);
f.setMinimumFractionDigits(2);
f.setParseIntegerOnly(false);
try {
Double i = Double.parseDouble(s);
result = f.format(i);
} catch (NumberFormatException e) {
Long l = Long.parseLong(s);
result = f.format(l);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return result;
}
解压,并把Maven根目录下的bin
添加到路径。
在marketplace里面搜索m2e插件,下载并安装。我装了两个:
安装后重启。并在Maven的run configuration里面,在Maven Runtime里选择新装的Maven作为compiler,代替Embedded。
否则在pom.xml
里面的<build>
部分会出错。如果找不到maven-compiler-plugin
,删除<build>
部分。
-source
和-target
和目前的代码的运行时不相符问题比如,使用switch-case
时,用String作key直到1.7才支持,但Mavenmaven-compiler-plugin
的设置默认支持1.5。显示为用-X
开关得到的debug信息中,含有-source 1.7 -target 1.7
的字样。
错误信息为:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project SystemTrayAlarm: Compilation failure
[ERROR] /D:/desarrollo/workspace_NEW/SystemTrayAlarm/src/main/java/com/windthunderstudio/logic/util/I18N_Manager.java:[27,20] strings in switch are not supported in -source 1.5
[ERROR] (use -source 7 or higher to enable strings in switch)
[ERROR] -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project SystemTrayAlarm: Compilation failure
/D:/desarrollo/workspace_NEW/SystemTrayAlarm/src/main/java/com/windthunderstudio/logic/util/I18N_Manager.java:[27,20] strings in switch are not supported in -source 1.5
解决方法是:
https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html
在pom.xml
加入:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
如果不指定Encoding,则会有警告:
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
解决方法是:
https://maven.apache.org/general.html#encoding-warning
在pom.xml
中加入:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
We should use maven-dependancy-plugin
. Add these lines to pom.xml
:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<outputDirectory>
[some-path]
</outputDirectory>
</configuration>
</plugin>
Here, [some-path]
can be absolute, like C:\mydir
, or relative to usr.dir
(project root), like main\java\resources\libs
, or some placeholder, like ${project.build.directory}
.
And run Maven task:
dependency:copy-dependencies
And all the jars will be downloaded and copied to the directory.
Normally, convert the project into Maven project, and run "Maven - update project" is good enough. If that does not work, you should try this:
Open the .classpath
file, you will find something like this:
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="core/src/main/java"/>
......
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
</classpath>
In every <classpathentry>
either of type src
or con
, make sure this paragragh is present:
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
The good, healthy .classpath
file may contain something like <attribute name="optional" value="true"/>
, that is not important. Don't forget to change <classpathentry />
to <classpathentry>xxx</classpathentry>
, because now it has children.
After doing this, recompile with maven and clean/rebuild the project. You should be fine.
In a multiply module Maven project, some modules may depend on code in another module. In this case, the build order of modules in the principal pom.xml
does not matter, but in every module's pom.xml
, you should add as dependency the other modules, and install the depended modules as jar
in the local repo first.
For example, I have a project of 3 modules, A
, B
and C
. B
and C
has their own pom.xml
, so as A
. The project also has a pom.xml
. When I run clean compile
in the project pom.xml
, the compilation succeeds in B and C, but fails in A with errors like:
cannot find symbol, class: xxxxx (some class in B)
cannot find symbol, class: xxxxx (some class in C)
So, what we can do is:
clean compile install
on B.clean compile install
on C.pom.xml
in A, like:
<dependency>
<groupId>com.company</groupId>
<artifactId>B</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.company</groupId>
<artifactId>C</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
and then clean compile install
on A.
4. clean compile
on the whole project.
Now all is good. Maven seems only goes to the local repo to find the jar as dependency, so install
of B and C is necessary. Very bad idea, though, because I think it should detect the dependency even though no JAR is installed.
Must change the output folder to target/classes
. If there is not this folder, create it and then change the class location. If a project is a maven project, when running the main class, it will not find it under bin
, but target/classes
. This is Maven convention, but not pre-set when the project is converted to Maven project.
<p align="center">Some Text</p>