Hello good day. I have an urgent need as it relates to creating a table. Is it possible to create sub-columns under a main column? Please see image file for clarification.
Ultimately, I want to create a coulmn heading, and then beneath that heading divide it's width into a number of other columns each with their own heading. I hope I am clear. Is there any way to do this please?
bguild 163 Posting Whiz
javax.swing.JTable
is a class with many features and complexities, but I am sure that it can't do what you are asking for directly. I recommend that you use ordinary javax.swing.JLabel
s for your top column headings and then use two JTable
s, one for "Subjects" and one for "Group Letters". That won't give you all of the features of real JTable
columns, but it should be much simpler than trying to make them full-featured. If you want your top columns to be resizable then you can use a javax.swing.JSplitPane
.
CoilFyzx 0 Newbie Poster
Thank you very much for this response. I understand what you are saying.
A few questions:
- Can I add two JTables to one ScrollPane?
- How will I make the Tables interact? e.g. Copying entire rows.
bguild 163 Posting Whiz
It retrospect it is obvious that you would want to be able to select rows and you would want to keep the rows lined up when scrolling vertically, so I take back my suggestion of using two JTables
. That option was too easy. Instead I suggest that you use one JTable
which has all of the columns of your lower row of columns: Totals, Subject name, A, B, C, etc. Then you can use getTableHeader
to get the component that draws the column headings. You can put the header into a custom JPanel
of your own design that will draw your upper level of column headings. Then you can give that custom header to the JScrollPane
using setColumnHeaderView
.
I haven't actually tested this idea, but it seems likely to work.
CoilFyzx 0 Newbie Poster
Um...Hmm. I am new to GUI designing. So that flew right over my head. But I appreciate you taking the time. I'll have to research
those methods.
Edited by CoilFyzx
CoilFyzx 0 Newbie Poster
Can you be off any assitance with the JPanel please? I've gotten as far as here.
You can see the JPanel peeking out at the top there. But I'm hoping to make it look like this below.
I've been struggling with the JPanel, but to no avail. Any help please?
bguild 163 Posting Whiz
I see you have the word "Subjects" there. I recommend that you center that word above the "Totals" and "Subject Name" columns, and also add a "Group Letters" heading on the right side. I expect you have tried to do this and encountered some difficulty, but since I don't know anything about that specific difficulty I can't be very specific in my advice.
I suggest that you use javax.swing.JLabel
to draw your headings. You can use the javax.swing.table.TableColumnModel
to give you the information that you need to put the labels in the right places, and you can write a javax.swing.event.TableColumnModelListener
to allow you to update the positions of the labels when the positions of the columns change.
CoilFyzx 0 Newbie Poster
This is hlepful advice. I'll give it a try, and then I'll come back to you. I appreciate your patience thus far.
CoilFyzx 0 Newbie Poster
I'm not too sure about the positioning of the JLabels. This is my code so far.
JTableHeader groupsTblHead;
JPanel panel1=new JPanel(new GridBagLayout());//I chose GridBag due to its more flexible nature
public DefaultTableModel groupingTableModel = new DefaultTableModel(data_2,groupColumns);
JTable groupsTable= new JTable(groupingTableModel);
GridBagConstraints grp = new GridBagContraints();
JLabel subLabel = new JLabel("SUBJECTS");
JLabel lettLabel = new JLabel("GROUP LETTERS");
...
groupsTblHead = groupsTable.getTableHeader();
grp.gridx=0;
grp.gridy=0;
panel1.add(subLabel,grp);
grp.gridx=1;
grp.gridy=0;
panel1.add(lettLabel, grp);
grp.gridx=0;
grp.gridy=1;
panel1.add(groupsTblHead,grp);
tableScrollArea.setColumnHeaderView(panel1);
This results in the following interface.
Not quite what I want. I am trying to at least see both of those labels. Any suggestions please?
CoilFyzx 0 Newbie Poster
You realize that I added two labels, but neither show up. Is there a reason for that please?
CoilFyzx 0 Newbie Poster
So I changed the code once more. I was messing around
Here are: the code and the visual result.
You'll notice that it still has a glitch in it, I can't figure out why it is behaving like this. Please help me, I'm truly trying.
//Same declarations
groupsTblHead = groupsTable.getTableHeader();
grp.gridx=0;
grp.gridy=0;
panel1.add(subLabel,grp);
grp.gridx=1;
grp.gridy=0;
panel1.add(lettLabel, grp);
grp.gridx=0;
grp.gridy=1;
panel1.setPreferredSize(new Dimension(150,50));
grp.weightx=1;
grp.weighty=1;
grp.fill=GridBagConstraints.HORIZONTAL;
panel1.add(groupsTblHead,grp);
tableScrollArea.setColumnHeaderView(panel1);
bguild 163 Posting Whiz
I recommend that you avoid java.awt.GridBagLayout
. It is troublesome and overly complicated, so people rarely use it in practice which makes it hard to find good advice about its use.
In this case especially you should avoid layout managers because your layout needs are very specialized and specific. No existing layout manager is going to give you what you need, so you either need to write your own or else use null
for your layout manager and position your labels directly. Since you know exactly where you want your labels, that should be much easier than struggling with a layout manager.
mKorbel 274 Veteran Poster
@bguild answers here going wrong directions, please to review that, from end to the start,
@CoilFyzx please whats your goal, there are simpler ways, by using JTable and/or with another, both JTables can has the same TableModel, depends of your final idea, used notifiers and Listeners
Edited by mKorbel
JamesCherrill 4,733 Most Valuable Poster Team Colleague Featured Poster
I recommend that you avoid java.awt.GridBagLayout
Just for balance - most of my Java work is GUI-centric, and GridBagLayout is the only layout manager I use for anything other than trivial cases. It's the only one that gives me enough control over what happens when the systen font is different or the window is resized. In my opinion it's well worth the effort to learn to use it.
JamesCherrill 4,733 Most Valuable Poster Team Colleague Featured Poster
Here's a simple way to do it provided you don't want to get too clever...
It just adds a JLabel across the top of the whole table, and paints the top-level headings into that JLabel at the right places - which it gets from the table's column model. By listening for column resizes it also adjusts to match the new column widths.
The following runnable proof of concept shows the idea... feel free to criticise!
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import static java.awt.GridBagConstraints.*;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableWithHeadings {
public static void main(String... args) {
new TableWithHeadings();
}
TableWithHeadings() {
JFrame frame = new JFrame("Demo");
JTable table = demoTable();
frame.setLayout(new GridBagLayout());
frame.add(new HeadingsLabel(table), new GridBagConstraints(0, RELATIVE, 1, 1, 0.0, 0.0,
NORTHWEST, HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
frame.add(table.getTableHeader(), new GridBagConstraints(0, RELATIVE, 1, 1, 0.0, 0.0,
NORTHWEST, HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
frame.add(table, new GridBagConstraints(0, RELATIVE, 1, 1, 0.0, 0.0,
NORTHWEST, HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
JTable demoTable() {
String[] headings = {"A", "B", "C", "D"};
String[][] data = {{"1", "2", "3", "4"}, {"5", "6", "7", "8"}};
JTable table = new JTable(data, headings);
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
return table;
}
class HeadingsLabel extends JLabel {
JTable table;
DefaultTableColumnModel colModel;
String heading1 = "Heading 1";
String heading2 = "Heading 2";
int colsForHeading1 = 2; // how many cols to span
HeadingsLabel(JTable table) {
super(" ");
this.table = table;
colModel = (DefaultTableColumnModel) table.getColumnModel();
colModel.addColumnModelListener(new TableColumnModelListener() {
// just handle columnMarginChanged to re-paint headings
@Override
public void columnAdded(TableColumnModelEvent e) {
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
}
@Override
public void columnMoved(TableColumnModelEvent e) {
}
@Override
public void columnMarginChanged(ChangeEvent e) {
repaint();
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
});
}
@Override
public void paintComponent(Graphics g) {
int head1width = -1;
for (int cNumber = 0; cNumber < colsForHeading1; cNumber++) {
TableColumn col = colModel.getColumn(cNumber);
head1width += col.getWidth();
}
g.drawRect(0, 0, head1width, getHeight()-1);
g.drawString(heading1, 20, 12);
g.drawRect(head1width, 0, getWidth() - head1width-1, getHeight()-1);
g.drawString(heading2, head1width + 20, 12);
}
}
}
Edited by JamesCherrill
bguild 163 Posting Whiz
feel free to criticise!
I find the way HeadingsLabel
extends JLabel
to be a bit strange. HeadingsLabel
doesn't conform to the rules described in the documentation for JLabel
, and therefore isn't a proper JLabel
. It looks like a JPanel
could have served the same purpose with less potential confusion.
JamesCherrill 4,733 Most Valuable Poster Team Colleague Featured Poster
Not sure what rules you are referring to... "A display area for a short text string or an image, or both. A label does not react to input events."
But anyway, I chose JLabel because it comes with font/text attributes that make it easy to implement some control of formatting of the headings, and sets its height appropriately, but otherwize, yes, a JPanel would be a sensible choice.
CoilFyzx 0 Newbie Poster
Wow. This has been a great help. I'll update you guys as soon as I implement the suggestions. Thank you.
mKorbel 274 Veteran Poster
again I thik that answers going wrong directions
_________________________________________________________________never to use paintComponent for JTableHeader, is called more times than Renderer,
- use Renderer for JTableHeader
________________________________________________________________ never to use JTableHeader out of JScrollPane
hide JScrollBars
________________________________________________________________JTable, JScrollPane and JComboBox cant returns reasonable PreferredSize from methods implemented in theirs API
JTable/JScrollPane required to overide setPreferred(ScrollableViewport)Size for pack()
Standard LayoutManagers (most of todays customs too) accepting only PreferredSize with pack()
____________________________________________________________________________________________________________________________________________
three reasons for ColumnModelListener
convertColumnIndexToModel for prepareRenderer/Editor, but by default code line convertXxxIndexToXxx replace, supply usage of this Listener
auto column adjuster for table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);, to hold rellative coordinates after container resized
- and (put all mentioned points together)
.
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableFilterRow extends JFrame implements TableColumnModelListener {
private static final long serialVersionUID = 1L;
private JTable table;
private JPanel filterRow;
public TableFilterRow() {
table = new JTable(3, 5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane);
table.getColumnModel().addColumnModelListener(this);
filterRow = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
for (int i = 0; i < table.getColumnCount(); i++) {
filterRow.add(new JTextField(" Sum at - " + i));
}
columnMarginChanged(new ChangeEvent(table.getColumnModel()));
getContentPane().add(filterRow, BorderLayout.SOUTH);
}
@Override
public void columnMarginChanged(ChangeEvent e) {
TableColumnModel tcm = table.getColumnModel();
int columns = tcm.getColumnCount();
for (int i = 0; i < columns; i++) {
JTextField textField = (JTextField) filterRow.getComponent(i);
Dimension d = textField.getPreferredSize();
d.width = tcm.getColumn(i).getWidth();
textField.setPreferredSize(d);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
filterRow.revalidate();
}
});
}
@Override
public void columnMoved(TableColumnModelEvent e) {
Component moved = filterRow.getComponent(e.getFromIndex());
filterRow.remove(e.getFromIndex());
filterRow.add(moved, e.getToIndex());
filterRow.validate();
}
@Override
public void columnAdded(TableColumnModelEvent e) {
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new TableFilterRow();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
- other events from ColumnModelListener are properly implemented in TableModel and its derivates (Default/Abstract/...)
bguild 163 Posting Whiz
Not sure what rules you are referring to... "A display area for a short text string or an image, or both. A label does not react to input events."
I'm refering to the fact that a HeadingsLabel
is a JLabel
and therefore everything in the documentation for JLabel
should apply to HeadingsLabel
. Even that rule you quoted is being bent, or at least not being followed in the spirit in which it was intended, since a HeadingsLabel
is incapable of ever displaying an image. More importantly, HeadingsLabel
has methods that are clearly broken such as getText
, setText
, getIcon
, setIcon
, and many other methods.
Of course, this is only a technicality. Your solution works very nicely.
CoilFyzx 0 Newbie Poster
I understand where you guys are going but none of your implementations have really imprved my understanding of the matter. You all seem to have varying views on what is acceptable, and what is not.
Sigh.
I'd love to understand. But anyway Mr. James Cherill, I'm currently using your code. The issue I have is that I don't want to fill the available Horizontal space. But whenever I change that field GridBagConstraints.HORIZONTAL, the whole thing is thrown off.
I'm trying to tweak the implementation to suit the images I have in the picture. I want the whole thing to be centered In the Middle of the frame. Would you like to see my entire code?
mKorbel 274 Veteran Poster
I want the whole thing to be centered In the Middle of the frame. Would you like to see my entire code?
I think that not, if is based on answers here, then code will be potentially big mess
reason why I wrote twice that answers going wrong direction, asked for goal, clarification
____________________________________________________________________________________main problems are
never to separe JTableHeader out of JTable,
add JTable to JScrollPane, then all bothering with possitioning in used LayoutManagers are irrelevant
use BorderLayout (GridLayout too for one JComponent in container) for JComponents implements Scrollable in API
describtion of Q is poor, without main goal, just to back out of the impasse, based on pictures
then answers (from 1st. to last) are wild shots to the dark
Edited by mKorbel
JamesCherrill 4,733 Most Valuable Poster Team Colleague Featured Poster
I have to say that mKorbel is not always easy to understand, but he is usually right, and he is making some very valid points here.
The code I posted was an example of how you could fudge or fake a quick solution for a simple case, but that's all. I certainly don't recommend it as a general approach. It's up to you to decide whether your current requirement can be met with a quick fudge or whether it needs a properly engineered solution.
I want the whole thing to be centered In the Middle of the frame
When I run my code the whole thing is centered in the frame (?)
If you want to use it in a frame with other components then you would need to put my three components in a JPanel then put that JPanel with your other components in the main JFrame.
mKorbel 274 Veteran Poster
I'm copy - paste (very similair question) from another Java Forum ( shadowing crosspost ???)
I think that only (notice rows never will be resized by using any of LayoutManager, only columns)
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.weighty = 0;GBC isn't proper LayoutManager for JComponents implements Scrollable, use BorderLayout(ev GridLayout) for these JComponents
for example
import java.awt.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class JTableAndGBC {
private String[] columnNames = {"Source", "Hit", "Last", "Ur_Diff"};
private JTable table;
private Object[][] data = {{"Swing Timer", 2.99, 5, 1.01},
{"Swing Worker", 7.10, 5, 1.010}, {"TableModelListener", 25.05, 5, 1.01}};
private DefaultTableModel model = new DefaultTableModel(data, columnNames);
public JTableAndGBC() {
JPanel panel = new JPanel(new GridBagLayout());
table = new JTable(model);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
panel.add(table, gbc);
JFrame frame = new JFrame();
frame.add(panel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) throws Exception {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new JTableAndGBC();
}
});
}
}
EDIT 1
JTable
,JScollPane
,JComboBox
can't returns reasonablePreferredSize
, see my code in the edit, then wokrs for allLayoutManagers
,notice carefully with
table.setPreferredScrollableViewportSize(table.getPreferredSize());
, I'd suggest to test if Dimension overload desired coordinates or screen resolution or not overload :-),otherwise to shrink with
new Dimension(int, int)
instead oftable.getPreferredSize()
.
import java.awt.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class JTableAndGBC {
private String[] columnNames = {"Source", "Hit", "Last", "Ur_Diff"};
private JTable table;
private Object[][] data = {{"Swing Timer", 2.99, 5, 1.01},
{"Swing Worker", 7.10, 5, 1.010}, {"TableModelListener", 25.05, 5, 1.01}};
private DefaultTableModel model = new DefaultTableModel(data, columnNames);
public JTableAndGBC() {
JPanel panel = new JPanel(new GridBagLayout());
table = new JTable(model);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
JScrollPane pane = new JScrollPane(table);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
panel.add(pane, gbc);
JFrame frame = new JFrame();
frame.add(panel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) throws Exception {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new JTableAndGBC();
}
});
}
}
EDIT 2
I'm strongly to suggest to use BorderLayout, GridLayout, instead of GBC (JTable
, JScollPane
, JComboBox
can't returns reasonable PreferredSize
, required to override GBC, brrrr, not why bothering)
code for BorderLayout
import java.awt.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
public class JTableAndGBC {
private String[] columnNames = {"Source", "Hit", "Last", "Ur_Diff"};
private JTable table;
private Object[][] data = {{"Swing Timer", 2.99, 5, 1.01},
{"Swing Worker", 7.10, 5, 1.010}, {"TableModelListener", 25.05, 5, 1.01}};
private DefaultTableModel model = new DefaultTableModel(data, columnNames);
public JTableAndGBC() {
JPanel panel = new JPanel(new BorderLayout()/*(new GridBagLayout()*/);
table = new JTable(model);
//GridBagConstraints gbc = new GridBagConstraints();
//gbc.weightx = 1.0;
//gbc.fill = GridBagConstraints.HORIZONTAL;
JScrollPane pane = new JScrollPane(table,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
/*TableColumn firstColumn = table.getColumnModel().getColumn(0);
firstColumn.setMinWidth(150);
firstColumn.setMaxWidth(200);
TableColumn secondColumn = table.getColumnModel().getColumn(1);
secondColumn.setMinWidth(200);
secondColumn.setMaxWidth(250);
TableColumn thirdColumn = table.getColumnModel().getColumn(1);
thirdColumn.setMinWidth(50);
thirdColumn.setMaxWidth(100); */
table.setRowHeight(30);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
panel.add(pane/*, gbc*/);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) throws Exception {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new JTableAndGBC();
}
});
}
}
Edited by mKorbel
CoilFyzx 0 Newbie Poster
Nice responses again. I don't want to "fudge" a solution. Haha. I really wanted to understand how to do it, as I had stated (best practises included). Um...I'm not quite sure where to proceed from here.
My main objective: Is to create a table that accepts user data into a table. The tables design(which is what I'm trying to accomplish now), is to create a structured view of the information(which really is the purpose of every table).
So the user will add data to the section labelled subject letters.
As it relates to functionality, that will be simple to implement. All I wish for is the visual design. I don't know how to make that any more clear(I sya this because I keep being asked for the objective or my goal; which is exactly to create what I designed in the image).
So what would be the best practice for me to get this done please?
(mKorbel is difficult to comprehend, even though I think that I understand him.)
CoilFyzx 0 Newbie Poster
Well what I've done so far is to use the, er...'fugde'-d example by Mr James Cherill while changing the insets values.
CoilFyzx 0 Newbie Poster
This Looks okay but I haven't benefited as it relates to a learning experience.
bguild 163 Posting Whiz
I haven't benefited as it relates to a learning experience.
If there is any part of it that you don't understand, I'm sure we will be glad to explain it to you.
CoilFyzx commented: Firstly, what would one consider to be the a good way to solve this problem, keeping in mind the principles of best-practice? (You guys seem to refer to trying to code as properly as possible) +1
CoilFyzx 0 Newbie Poster
Firstly, what would one consider to be the a good way to solve this problem, keeping in mind the principles of best-practice? (You guys seem to refer to trying to code as properly as possible)
JamesCherrill 4,733 Most Valuable Poster Team Colleague Featured Poster
The "proper" way to do it is probably to subclass JTable and override the method that implements the UI for the table header. There are a few discussions about that on the web. It looks pretty difficult to me.
In my opinion while you should always strive to do the best, real life constrains you, and sometimes a quick fix is the most appropriate thing to do.
In this case for example, if the exercise is all about UI then you need to do it properly, but if it's an exercise in database mananagement then spending a too much time on prettyfying the UI is probably a mistake...
Edited by JamesCherrill
Be a part of the DaniWeb community
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.