Scrollable area without scrollbars - java

I need to create an area on which one would normally apply a scrollbar, it has to scroll horizontally (the contents is only a view into a larger logical area), but I have to use some special controls placed left and right to the control in order to scroll.
I have thougth about using absolute values (according to the logical view and subtract an offset. Thus, the controls right to the offset would be placed with negative x- values and thus discarded. Controls with x values above the width would also be discarded.
Is this a valid approach?
Best regards
Soeren

You can can create a JScrollPane over a Component (your larger logical area) and remove the scrollbars.
You can then add buttons to scroll left and right. When clicked these buttons should move the view of your scrollpane. This is done by setting the absolute position of the view. You can make this relative by first getting the absolute position of the view and then incrementing/decrementing it and setting it again.
Here's a class that shows a scrollable window of a larger image.
public class ViewScroller {
public ViewScroller() {
JFrame frame = new JFrame("ViewScroller");
final ImageIcon image = new ImageIcon("path\\to\\my\\image");
JLabel label = new JLabel(image);
final JScrollPane scrollPane = new JScrollPane(label);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
JButton left = new JButton("<");
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Point p = scrollPane.getViewport().getViewPosition();
p.x = p.x < 10 ? 0 : p.x - 10;
scrollPane.getViewport().setViewPosition(p);
}
});
JButton right = new JButton(">");
right.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Point p = scrollPane.getViewport().getViewPosition();
int offset = p.x + scrollPane.getViewport().getWidth();
p.x = offset + 10 > image.getIconWidth() ? p.x : p.x + 10;
scrollPane.getViewport().setViewPosition(p);
}
});
frame.add(right, BorderLayout.EAST);
frame.add(left, BorderLayout.WEST);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setVisible(true);
}
}

Related

Prevent MigLayout from assigning space to a component

I have a JFrame that consists of a potentially arbitrarily-large set of "headers" which can be clicked on to expose panels, one per header (conceptually similar to a vertically-arranged toolbar, with the headers being tabs that expose various tool panels). I want the headers to have a fixed amount of vertical space assigned to them, but to grow horizontally to fill the width of the frame. I want the panels exposed by the headers to consume all excess vertical space in the frame -- but only if they're visible; when invisible they should take zero space.
Unfortunately, no matter how I try to tweak the layout, I can't keep it from assigning extra space to the headers and to invisible panels. In the demo app, simply make the window larger. The desired behavior is that the blue "Hello 0" panel should grow taller while all other components stay "compact"; in practice I see empty space around the two "Compact label" labels and below the two lower "Click me" headers.
Thanks for your time!
public class Demo {
public static void main(String[] args) {
final JFrame frame = new JFrame("Inspector");
frame.setLayout(new MigLayout("debug, fill, flowy, insets 0, gap 0"));
frame.add(new JLabel("Compact label 1"));
frame.add(new JLabel("Compact label 2"));
for (int i = 0; i < 3; ++i) {
JPanel wrapper = new JPanel(
new MigLayout("debug, fill, flowy, insets 0, gap 0",
"", "0[]0[grow]0"));
// Fixed-height header should grow horizontally only
JPanel header = new JPanel(
new MigLayout("flowx, fillx, insets 0, gap 0"));
header.add(new JLabel("Click me!"), "growx, height 40!");
header.setBackground(Color.RED);
// Variably-sized body should fill any extra space.
final JPanel body = new JPanel(new MigLayout("fill"));
body.add(new JLabel("Hello, " + i), "grow");
body.setBackground(Color.BLUE);
body.setVisible(i == 0);
wrapper.add(header, "growx");
wrapper.add(body, "growy, hidemode 2");
header.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
boolean shouldShow = !body.isVisible();
body.setVisible(shouldShow);
frame.pack();
}
});
frame.add(wrapper, "grow");
}
frame.pack();
frame.setVisible(true);
}
}
It might be best to use a JTabbedPane to get what you want.
Otherwise you can put percentage width and height constraints on your components.
So for your headers you can tell them to use 100% of the width with:
wrapper.add(header, "growx , w 100%");
For the body you can put "h 100%" (you can use h or height)
wrapper.add(body, "growy, hidemode 2, h 100%");
It's not perfect, but hopefully this helps.
Ultimately I had to resort to removing and re-adding components to the layout manager, using different constraints depending on whether or not a given body was visible. This isn't ideal, but it does result in the desired behavior. MigLayout does have a setComponentConstraints() method which you'd think would work for this, but, like Matt Hubbard's suggestion, it doesn't recalculate sizes when the bodies are shown/hidden, resulting in an uneven distribution of space to the visible bodies.
Here's the updated demo program:
public class Demo {
public static void main(String[] args) {
final JFrame frame = new JFrame("Inspector");
final JPanel contents = new JPanel(
new MigLayout("debug, fill, flowy, insets 0, gap 0"));
contents.setBackground(Color.GRAY);
frame.add(contents);
final ArrayList<Component> components = new ArrayList<Component>();
components.add(new JLabel("I should not grow"));
components.add(new JLabel("I shouldn't grow either"));
for (int i = 0; i < 3; ++i) {
// Fixed-height header should grow horizontally only
final JPanel header = new JPanel(
new MigLayout("flowx, fillx, insets 0, gap 0"));
header.add(new JLabel("Click me!"), "growx, height 40!");
header.setBackground(Color.RED);
components.add(header);
// Variably-sized body should fill any extra space.
final JPanel body = new JPanel(new MigLayout("fill"));
body.add(new JLabel("Hello, " + i), "grow");
body.setBackground(Color.BLUE);
body.setVisible(i == 0);
components.add(body);
header.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
boolean shouldShow = !body.isVisible();
body.setVisible(shouldShow);
refill(frame, contents, components);
}
});
}
refill(frame, contents, components);
frame.setVisible(true);
}
private static void refill(JFrame frame, JPanel contents,
ArrayList<Component> components) {
contents.removeAll();
for (int i = 0; i < components.size(); ++i) {
Component component = components.get(i);
// First two components don't grow at all.
if (i < 2) {
contents.add(component, "gap 0, grow 0, pushy 0");
}
// Even-numbered components are headers.
else if (i % 2 == 0) {
contents.add(component, "gap 0, growx, growy 0, pushy 0");
}
// Odd-numbered components are bodies, only if visible.
else if (component.isVisible()) {
contents.add(component, "gap 0, grow, pushy 100");
}
}
Dimension curSize = frame.getSize();
frame.pack();
frame.setSize(curSize);
}
}
I think what you're looking for is the hidemode command. This will prevent MigLayout from assigning space to a component that is not yet visible.
I'll leave this SO question here which explains how to use this option.

JScrollPane not scrolling to show whole pannel

I have developed a Code which will display a panel & added a scrollpane which will scroll down if panel size is more than the visible region.
My code is
CT3= new CT3TycoDULocalPanel(anexflag1,anexflag2);
cnt =(JPanel)getContentPane ();
cnt.setLayout (null);
pane = new JPanel(new BorderLayout());
pane.add (CT3,BorderLayout.CENTER);
scPane = new JScrollPane(CT3);
scPane.setLayout(null);
scPane.setBackground(Color.GREEN);
scPane.setBounds(20,12,550,600);
scp=scPane.getVerticalScrollBar();
scp.setBounds (528,0,20,600);
scp.addAdjustmentListener (this);
scPane.setViewportView(CT3);
scPane.add(scp);
scPane.addMouseWheelListener(this);
scPane.add(CT3);
CT3.setBounds(10, 10, 510,CT3.y);
scPane.validate();
scPane.repaint();
cnt.add(scPane);
And in AdjustmentListener i have written following code:
public void adjustmentValueChanged(AdjustmentEvent e)
{
int y = (int)(e.getValue()*3.2);
CT3.reshape (10,(int)-Math.ceil(y)+25,510,CT3.y);
CT3.validate();
CT3.repaint();
}
And I have Dynmically calculated the size of CT3 as per following code
public void resize()
{
if(itemVect.size()<=17)
{
pnlAnex.setBounds(0, 50, 510, 700);
}
else
{
int size=itemVect.size();
y=(size*30)+90;
pnlAnex.setBounds(0, 50, 510, y+50);
pnlAnex.validate();
pnlAnex.repaint();
}
}
But I am not getting the desired output.
The CT3 panel is showing correct height but the scrollpane is getting more height as per shown below image.
The green part is of ScrollPane & the White part is of my desired output.
As you can see my scrollbar is still scrollable even if my desired output(White Panel) is dispalyed.
Please help me out to limit the scroll bar till my white panel is visible only(CT3 Panel)
Thanks in advance......

Vertical JPanel manual positioning: which layout?

Given my requirements:
Single vertical column of JPanels.
Set the vertical location* of the JPanel without using the properties of a sibling.
Component position and size are fixed when the frame is resized.
Keep other layout aspects automatic (such as preferred size calculation), as much as possible
(*) Location: I mean location as in Component.setLocation(x, y).
is there a solution which is obvious, and if this is GridBagLayout, how to do this?
Details:
I want to put components vertically in a column container (like a vertical Box) by specifying their vertical location only. What is the best way to do this without loosing the other benefits of a layout such as BoxLayout?
In a vertical Box, setting the vertical position of a component must be done using a filler, or by adjusting the size of the component just above, there is no such possibility like:
panel.setLocation(getLocation().x, y)
On the other hand using a no layout container puts on me the task manage:
The initial size of the component
The container resizing events.
Here the solution of null layout is recommended, here this is a custom one, and here this is GridBagLaout. Also MIGLayout appears to be universal one (but I'd prefer no adding another library to my project).
I have written the following program for someone who was also looking for the same requirements.
Note: Make sure to add the first element to 0 position because there will be no more components and no position will be available other than 0, 2nd to 0 or 1, 3rd to 0 or 1 or 2 and so on
public class VerticalList extends JFrame {
JPanel pnl = null;
TextField tf = new TextField(10);
Box center = Box.createVerticalBox();
JScrollPane jsp = new JScrollPane(center);
JPanel ctrl = new JPanel(new FlowLayout());
JButton send = new JButton("Send");
public VerticalList() {
jsp.setAutoscrolls(true);
ctrl.add(send);
ctrl.add(new JLabel("Position:"));
ctrl.add(tf);
Container cnt = getContentPane();
cnt.add(jsp, BorderLayout.CENTER);
cnt.add(ctrl, BorderLayout.SOUTH);
send.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pnl = new JPanel(new FlowLayout());
pnl.setBorder(BorderFactory.createLineBorder(Color.red));
pnl.add(new JLabel("Added to Position: "+tf.getText()));
pnl.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int)pnl.getPreferredSize().getHeight()));
try{
int index = Integer.parseInt(tf.getText());
center.add(pnl, index);
}catch(Exception ex){
JOptionPane.showMessageDialog(null, "Please Provide a Valid position", "Position Error", JOptionPane.ERROR_MESSAGE);
}
validate();
}
});
setPreferredSize(new Dimension(300, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
pack();
}
public static void main(String[] args) {
new VerticalList();
}
}

Fit JTable to fill the JPanel

Hi here are my codes for my table settings:
String [] column = {"MacAddress","PcName","OperatingSystem","IpAddress","Port","Status"};
model = new DefaultTableModel(0,column.length);
model.setColumnIdentifiers(column);
mainTable = new JTable(model);
mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for(int i=0;i<=column.length-1;i++){
mainTable.getColumnModel().getColumn(i).setPreferredWidth(300);
}
pane = new JScrollPane(mainTable);
pnlTabel = new JPanel();
pnlTabel.setBorder(BorderFactory.createTitledBorder(""));
pnlTabel.setPreferredSize(new Dimension(dim.width*70/100, dim.height*60/100));
pnlTabel.add(pane);
addMainPanel(pnlTabel);
Here is my addMainPanel() function:
public void addMainPanel(Component pnl){
mainPanel.add(pnl);
mainPanel.revalidate();
}
And here is my code for my mainPanel:
mainPanel = new JPanel();
mainPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
add(mainPanel,"Center");
and I'm using border layout for my frame:
setLayout(new BorderLayout(0,0));
My problem is that, even i use this set of code to set my JTable to fit but it seems to fail all the this, this code:
mainTable.setAutoResizeMode(JTa![enter image description here][1]ble.AUTO_RESIZE_OFF);
for(int i=0;i<=column.length-1;i++){
mainTable.getColumnModel().getColumn(i).setPreferredWidth(300);
}
When is use that code, my jtable does not resize but only add on a horizontal scroll bar at the bottom.
No offense meant but .. your code and consequently your question is a mess ;-) Plus you don't explain what exactly you want to achieve.
Trying to detangle, taking the nested layouts/resizing characteristics (as seen in the snippets, might not be complete):
frame // BorderLayout
mainPanel // FlowLayout
pnlTabel // FlowLayout, hard-coded prefSize
pane // scrollPane
mainTable // auto-resize-off
Aside: intentionally kept untelling names to demonstrate how mixing naming schemes tend to contribute to overall confusion :-) Doesn't really matter whether you decide for pre or postFixing some type-related marker, but if you do be consistent.
In that hierarchy, there are two levels of FlowLayout which basically will layout their children at their respective prefs and adjusting their own pref accordingly, lest the pref were hard-coded on the level of the pnlTable: however the table's pref will be changed (by changing the column prefs) it cannot bubble further up because ... hard-coding the pref leads not calculating its size (neither by layoutManager and nor uiDelegate, both never get a chance to do it)
Another issue - the more interesting one :-) - is that the JScrollPane is somewhat special in
calculating its own prefSize from its view's pref/scrollablePrefViewportSize depending on whether or not the view implements Scrollable (JTable does so, though in a crappy way)
being a validationRoot: invalidating the view (or any children) doesn't bubble further up the hierarchy
Assuming that you want the table's scrollPane to grow if the prefWidts of the columns change, there are two thingies to tweak:
implement table's getPreferredScrollableWidth to return a value calculated based on the prefWidth of the columns
revalidate a container higher up in the hierarchy
Some code to play with:
final JTable table = new JTable(50, 10) {
// properties to base a reasonable prefScrollable size
int visibleColumns = 3;
int visibleRows = 10;
// hard-coded default in super
Dimension dummySuper = new Dimension(450, 400);
#Override
public Dimension getPreferredScrollableViewportSize() {
Dimension dim = super.getPreferredScrollableViewportSize();
if (!dummySuper.equals(dim)) return dim;
dim = new Dimension();
for (int column = 0; column < Math.min(visibleColumns, getColumnCount()); column++) {
dim.width += getColumnModel().getColumn(column).getPreferredWidth();
}
dim.height = visibleRows * getRowHeight();
return dim;
}
};
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for (int i = 0; i < table.getRowCount(); i++) {
table.setValueAt("row: " + i, i, 0);
}
JComponent tablePanel = new JPanel();
tablePanel.add(new JScrollPane(table));
Action sizeColumns = new AbstractAction("size columns") {
int prefWidth = 75;
#Override
public void actionPerformed(ActionEvent e) {
int newWidth = prefWidth + 15;
for (int i = 0; i < table.getColumnCount(); i++) {
if (table.getColumnModel().getColumn(i).getPreferredWidth() == prefWidth)
table.getColumnModel().getColumn(i).setPreferredWidth(newWidth);
}
prefWidth = newWidth;
// revalidate "higher up" than the table itself
frame.revalidate();
}
};
frame.add(new JButton(sizeColumns), BorderLayout.SOUTH);
If you want a JTable to fill the available space, you should put it inside a JPanel which has a BorderLayout layout manager. Also don't forget about the JScrollPane which ensures that if the table doesn't fit into the view (e.g. too many rows), scrollbars will appear:
JFrame frame = new JFrame();
// set up frame
JTable table = new JTable();
// Set up table, add data
// Frame has a content pane with BorderLayout by default
frame.getContentPane().add( new JScrollPane( table ), BorderLayout.CENTER );
If you have other content you wish to display besides the table, you can add those to the NORTH, SOUTH, EAST, WEST parts of the content panel (which can be wrapped into other panels if more components are to be placed there).

java: Problem with JSplitpane

I would like to have a jsplitPane and swap right component by left component while running my program. I set division location about 0.2. when I swapped my left component and right component and set division location about 0.8; there is a problem with jSplitPane. It is locked and I can't move divisor. also after that; when I try to assign another component to right or left side of JSplitPane, the components appear bollixed. I tried by setDivisionLocation() method before swapping right and left component; but it is not effective. and also repaint() method....
please guide me
regards...sajad
I think your problem is that you add a component twice (that could really make thinks look strange). E.g you do something like: split.setLeftComponent(split.getRightComponent()).
So when you do the swap you need to remove the components first:
private static void swap(JSplitPane split) {
Component r = split.getRightComponent();
Component l = split.getLeftComponent();
// remove the components
split.setLeftComponent(null);
split.setRightComponent(null);
// add them swapped
split.setLeftComponent(r);
split.setRightComponent(l);
}
And the demo is here (also moves the divider location):
public static void main(String[] args) {
JFrame frame = new JFrame("Test");
final JSplitPane split = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
new JLabel("first"),
new JLabel("second"));
frame.add(split, BorderLayout.CENTER);
frame.add(new JButton(new AbstractAction("Swap") {
#Override
public void actionPerformed(ActionEvent e) {
// get the state of the devider
int location = split.getDividerLocation();
// do the swap
swap(split);
// update the devider
split.setDividerLocation(split.getWidth() - location
- split.getDividerSize());
}
}), BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
frame.setVisible(true);
}

Categories

Resources