Set the amount of rows JList show (Java) - java

Problem: I have a method that creates a list from the parsed ArrayList. I manage to show the list in the GUI, without scrollbar. However, I am having problem setting it to show only the size of ArrayList. Meaning, say if the size is 6, there should only be 6 rows in the shown List. Below is the code that I am using. I tried setting the visibleRowCount as below but it does not work. I tried printing out the result and it shows that the change is made.
private void createSuggestionList(ArrayList<String> str) {
int visibleRowCount = str.size();
System.out.println("visibleRowCount " + visibleRowCount);
listForSuggestion = new JList(str.toArray());
listForSuggestion.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
listForSuggestion.setSelectedIndex(0);
listForSuggestion.setVisibleRowCount(visibleRowCount);
System.out.println(listForSuggestion.getVisibleRowCount());
listScrollPane = new JScrollPane(listForSuggestion);
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent mouseEvent) {
JList theList = (JList) mouseEvent.getSource();
if (mouseEvent.getClickCount() == 2) {
int index = theList.locationToIndex(mouseEvent.getPoint());
if (index >= 0) {
Object o = theList.getModel().getElementAt(index);
System.out.println("Double-clicked on: " + o.toString());
}
}
}
};
listForSuggestion.addMouseListener(mouseListener);
textPane.add(listScrollPane);
repaint();
}
To summarize: I want the JList to show as many rows as the size of the parsed ArrayList, without a scrollbar.
Here is the picture of the problem:
Here's the link to the other 2 as the picture resolution is quite big I'm scared it will distort the view:
JList 1 & JList 2
The JList 1 and 2 pictures shows it clearly. The JList displays empty rows, which I do not want it to happen.
Any ideas? Please help. Thanks. Please let me know if a picture of the problem is needed in case I did not phrase my question correctly.
--
Edit:
JScrollPane scrollPane = new JScrollPane(textPane);
scrollPane.setPreferredSize(new Dimension(200, 200));
//Create the text area for the status log and configure it.
changeLog = new JTextArea(5, 30);
changeLog.setEditable(false);
JScrollPane scrollPaneForLog = new JScrollPane(changeLog);
//Create a split pane for the change log and the text area.
JSplitPane splitPane = new JSplitPane(
JSplitPane.VERTICAL_SPLIT,
scrollPane, scrollPaneForLog);
splitPane.setOneTouchExpandable(true);
//Create the status area.
JPanel statusPane = new JPanel(new GridLayout(1, 1));
CaretListenerLabel caretListenerLabel =
new CaretListenerLabel("Caret Status");
statusPane.add(caretListenerLabel);
//Add the components.
getContentPane().add(splitPane, BorderLayout.CENTER);
getContentPane().add(statusPane, BorderLayout.PAGE_END);
How the textPane is included into the container, if that helps
Another edit:
public void showSuggestionList(JScrollPane pane, Rectangle caretCoords) {
pane.setVisible(false);
pane.setBounds(caretCoords.x - 5, caretCoords.y + 25, 400, 250);
pane.setVisible(true);
repaint();
}
showSuggestionList() is being called my CaretListener, to show the JScrollPane when the caret moves.

I suspect it's the layout-management of textPane that is the issue. From what I can see, the listForSuggestions should not occupy more space than it needs to display those items, if it's preferred size is respected.
So the JTextPane is a Container, that is, you can add subcomponents to it. But how are those subcomponents layed out? That is up to the layout manager currently in use. If the layout manager respects the preferred dimension of the listForSuggestios I think you should be ok. Not sure though.
From what I can see, you get the "null-layout" by just instantiating a JTextPane, which means that unless you set another layout manager explicitly, you would need to take care of placement / resizing of the subcomponents yourself.
You could try to do something like
Dimension dim = listForSuggestions.getPreferredSize();
listForSuggestions.setBounds(xPos, yPos, dim.getWidth(), dim.getHeight());
Here is a complete example
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class FrameTest {
public static void main(String[] args) {
JFrame f = new JFrame("Frame Test");
ArrayList<String> str = new ArrayList<String>();
for (int i = 0; i < 20; i++)
str.add("number " + i);
JTextPane tp = new JTextPane();
int visibleRowCount = str.size();
System.out.println("visibleRowCount " + visibleRowCount);
JList listForSuggestion = new JList(str.toArray());
listForSuggestion.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
listForSuggestion.setSelectedIndex(0);
listForSuggestion.setVisibleRowCount(5);
System.out.println(listForSuggestion.getVisibleRowCount());
JScrollPane listScrollPane = new JScrollPane(listForSuggestion);
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent mouseEvent) {
JList theList = (JList) mouseEvent.getSource();
if (mouseEvent.getClickCount() == 2) {
int index = theList.locationToIndex(mouseEvent.getPoint());
if (index >= 0) {
Object o = theList.getModel().getElementAt(index);
System.out.println("Double-clicked on: " + o.toString());
}
}
}
};
listForSuggestion.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.BLACK));
listForSuggestion.addMouseListener(mouseListener);
Dimension dim = listForSuggestion.getPreferredSize();
listForSuggestion.setBounds(20, 20, (int) dim.getWidth(), (int) dim.getHeight());
tp.add(listForSuggestion);
f.add(tp);
f.setSize(400, 400);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
I think the most elegant way of doing this is to roll your own layout-manager. (It's actually quite simple.) And then, instead of doing textPane.add(list), you do textPane.add(list, YourLayoutManager.POPUP_LIST). The layout-manager then remembers the fact that list was supposed to be layed out according to it's preferred size, and layes it out accordingly in its layoutContainer-method. (If you give the YourLayoutManager a reference to the JTextPane that it is attached to, you could probably even make it layout the list right beside the current caret location.)

If you are dynamically (via code) filling your list, it is bad idea not to use scrollbar. It might work as you want f.e. for 20 list items, but imagine what happens once you need use more data - like 2000. Your GUI will be ruined.

Related

How can get components to stretch across an entire row using BoxLayout?

I'm looking at the How To Use BoxLayout documentation, which clearly says that
What if none of the components has a maximum width? In this case, if
all the components have identical X alignment, then all components are
made as wide as their container.
Let's assume we're adding a lot of JButton instances to a JPanel. If the maximum width of these buttons are none AND we invoke setAlignmentX(Component.LEFT_ALIGNMENT) on all of these buttons - then each of these buttons should stretch across its entire row. The documentation even illustrates this using the below picture.
I can't get this to work!
I've tried doing setMaximumSize(null) and setMaximumSize(new Dimension(-1,-1)) and setMaximumSize(new Dimension(0,0)) on the buttons but nothing gives me the described behaviour.
What excactly does the documentation mean when it says :
What if none of the components has a maximum width?
What is a maximum width of none?
The best I've been able to produce is the below. Reading the documentation I would expect that the buttons should be able to stretch across their entire rows. I know I can use other layout managers as well for this, but I would like to achieve this with BoxLayout (granted the documentation is right / I've understood the documentation right).
public class CustomList extends JPanel {
private final Box box = Box.createVerticalBox();
public CustomList() {
for (int i = 0; i < 10; i++) {
JButton b = new JButton("Button item" + i);
//b.setMaximumSize(new Dimension(0,0));
b.setAlignmentX(Component.LEFT_ALIGNMENT);
box.add(b);
}
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(box, BorderLayout.CENTER);
}
public static void main(String[] args) {
CustomList l = new CustomList();
l.setSize(200, 200);
l.setBackground(Color.red);
JFrame frame = new JFrame("Vertical Box");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(l, BorderLayout.CENTER);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
Your buttons actually have a maximum width.
What you can do is create JPanel objects with BorderLayout in your loop, add each button to each panel (to BorderLayout.CENTER, which is the default anyway).
BorderLayout.CENTER doesn't care about the maximum size of its child Component, so you end up with a JPanel whose whole content is filled by a JButton.
Since the JPanel itself has a huge default maximum size of new Dimension(Short.MAX_VALUE, Short.MAX_VALUE) (this is width=32767,height=32767 !!) which is the default maximum size of Component, you will get the expected result :
public CustomList() {
for (int i = 0; i < 10; i++) {
JPanel panel = new JPanel(new BorderLayout());
JButton b = new JButton("Button item" + i);
//b.setMaximumSize(new Dimension(0,0));
b.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.add(b);
box.add(panel);
}
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(box, BorderLayout.CENTER);
}

Java swing GUI absolute positioning

I know that absolute positioning is not recommended, but I need to show my labels randomly scattered as well as randomly changing their positions.
I have researched how to use setBounds but it doesn't seem to work. The following code shows the labels in a Flow Layout, and when I use setLayout(null) it shows a blank frame.
public class GUI extends JFrame{
device mobiles[];
device station;
JPanel pane= new JPanel();
public GUI()
{
setTitle("communication is Key");
setSize(1000, 1000);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pane.setBackground(Color.WHITE);
int x=0; int y=0;
mobiles= new device[10];
for (int i = 0; i < 10; i++) {
x=randInt();
y=randInt();
mobiles[i]= new device(1,x,y);
pane.add(mobiles[i]);
}
x=randInt();
y=randInt();
station = new device(0,x,y);
pane.add(station);
this.add(pane);
}
and this is class "devices" that extends JLabel
public class device extends JLabel{
ImageIcon mob = new ImageIcon("mob.png");
ImageIcon tow = new ImageIcon("tower.png");
public device(int num, int x, int y)
{ if(num==1)
this.setIcon(mob);
else this.setIcon(tow);
this.setBounds(x, y, 3, 7);
}
}
any help in finding out what the problem is, would be be appreciated.
The following code shows the labels in a Flow Layout, and when I use setLayout(null) it shows a blank frame.
The layout manager sets the size and location of the component.
If you don't use the layout manager, then you are responsible for set the size and location of each component.
Typically I would set the size to equal to the components preferred size.
Also, did you display the x/y value that are randomly generated? Maybe the values are larger than the size of the panel.
and when I use setLayout(null) it shows a blank frame.
What layout is set to null? The panel of the frame. Your code doesn't use the above method. Post the code that you use to cause the problem. We don't want to guess what you may or may not be doing.
thanks to #CasparNoree ... the answer suggested was to initialize the Japnel from the start:
JPanel pane = new JPanel(null);
When you set the layout to null you can set the bounds manually with coordinates.
JFrame jf = new JFrame();
JPanel p = new JPanel();
JButton jb = new JButton();
// this is where you make it so setting the bounds actually does something
p.setLayout(null);
jb.setBounds(100,100,100,100);
p.add(jb);
jf.add(p);
jf.setVisible(true);

can i replace labels ? or can i give a GUI and ID ? like in html?

The following code lays out the JLabels using the GridLayout. The arguments to the GridLayout are the following: rows, cols, horizontal gap, vertical gap. In the example below I have 3 pixels wide gap between labels both vertically and horizontally.
To use images instead of numbers, you could pass an ImageIcon to the constructor of the JLabel instead of the text.
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.border.BevelBorder;
public class FrameTest {
public static void main(String[] args) {
final JFrame f = new JFrame("Frame Test");
JPanel panel = new JPanel(new GridLayout(4, 4, 3, 3));
for (int i = 0; i < 16; i++) {
JLabel l = new JLabel("" + i, JLabel.CENTER);
// JLabel l = new JLabel(new ImageIcon("image_file.png"),
// JLabel.CENTER);
l.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
l.setFont(l.getFont().deriveFont(20f));
panel.add(l);
}
f.setContentPane(panel);
f.setSize(200, 200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
in that code the applets build up like a stack , but I want to make it replaceable like if I have a moving robot and I only want to replace the label where the robot moves instead of building a whole new grid ? or can I do that with any other GUI in java ?
First of all, I'd suggest to use an array of JLabel to keep track of the references of each label.
JLabel[] labels = JLabel[16];
Then, when an event happens, you could use the JLabel#setIcon method to change the Icon dynamically.

Need a layout? How it should be used?

So im writing a small test program for fun replicating a inventory from games like minecraft and runescape. Basically a frame with another one inside it, and pictures of your items in it, and a scroll bar to scroll down through all the stuff you have in your inventory. The "Stuff" i would have in my inventory would be buttons added later on with their own functionality, so you can scroll through vertically and see all the "stuff." Right now i have some test buttons being added to deomsntrate the error. Basically i want the buttons to be 100,100 and for them to be in a row of 4, and go onto the next column. I though GridLayout would be the best choice, but it seems to add more rows after being added into a scrollpane. Well heres the code skimmed down:
public class inventory extends JFrame{
public static void main(String[] args){
new inventory();
}
JPanel mainInv = new JPanel();
JScrollPane sp;
public inventory(){
setSize(500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Toolkit tk = this.getToolkit();
setLocation(tk.getScreenSize().width/2-getWidth()/2, tk.getScreenSize().height/2-getHeight()/2);
setLayout(null);
mainInv.setSize(getWidth()-10, 1000);
mainInv.setBackground(Color.blue);
mainInv.setLayout(new GridLayout(8,4));
sp = new JScrollPane(mainInv, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
sp.setMaximumSize(new Dimension(400,400));
sp.setBounds(5, 5, 500-10, 500-130);
JButton[] testButs = new JButton[100];
for(int i = 0; i < 50; i++){
testButs[i] = new JButton("Test Button " + i);
testButs[i].setSize(100,100);
mainInv.add(testButs[i]);
}
add(sp);
setVisible(true);
}
}
With GridLayout the number of rows is the dominating factor.
If you have 8 rows and 4 columns that can only fit 48 buttons, if you try to add a 49th button it will create a 5th column not a 9th row.
You can solve your problem by setting up the GridLayout with more rows.

How to increase the slow scroll speed on a JScrollPane?

I am adding a JPanel in a JScrollPane in my project.
All is working fine, but there is one problem about mouse scroll using the mouse-Wheel in JPanel. It's speed is very slow on scrolling. How to make it faster?
My code is :
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
objCheckBoxList = new CheckBoxList();
BaseTreeExplorer node = (BaseTreeExplorer)projectMain.objCommon.tree.getLastSelectedPathComponent();
if (node.getObject() != null) {
cmbList.setSelectedItem(node.getParent().toString());
} else {
if (node.toString().equalsIgnoreCase("List of attributes")) {
cmbList.setSelectedIndex(0);
} else {
cmbList.setSelectedItem(node.toString());
}
}
panel.add(objCheckBoxList);
JScrollPane myScrollPanel = new JScrollPane(panel);
myScrollPanel.setPreferredSize(new Dimension(200, 200));
myScrollPanel.setBorder(BorderFactory.createTitledBorder("Attribute List"));
You can set your scrolling speed with this line of code myJScrollPane.getVerticalScrollBar().setUnitIncrement(16);
Here is details.
This bug seems to occur because swing interprets the scroll speed in pixels instead of lines of text. If you are looking for a more accessible alternative to the accepted solution, you can use the following function to calculate and set the actual desired scroll speed in pixels:
public static void fixScrolling(JScrollPane scrollpane) {
JLabel systemLabel = new JLabel();
FontMetrics metrics = systemLabel.getFontMetrics(systemLabel.getFont());
int lineHeight = metrics.getHeight();
int charWidth = metrics.getMaxAdvance();
JScrollBar systemVBar = new JScrollBar(JScrollBar.VERTICAL);
JScrollBar systemHBar = new JScrollBar(JScrollBar.HORIZONTAL);
int verticalIncrement = systemVBar.getUnitIncrement();
int horizontalIncrement = systemHBar.getUnitIncrement();
scrollpane.getVerticalScrollBar().setUnitIncrement(lineHeight * verticalIncrement);
scrollpane.getHorizontalScrollBar().setUnitIncrement(charWidth * horizontalIncrement);
}
Note that swing does calculate the scroll speed correctly when it contains a single component like a JTable or JTextArea. This fix is specifically for when your scroll pane contains a JPanel.

Categories