Вопрос

I have a problem with showing specific component placed in JScrollPane. I have horizontal JScrollPane with GridLayout(1,0) and it contains variable number of JPanels - each containing image. It's like a preview of frames in GIF image. I use button to move among these JPanels (by changing borders and keeping index of chosen one), but I don't know how to force JScrollPane to show me JPanel if it's chosen (and center it if possible).

So I want this enter image description here

force to do this: enter image description here

Thanks in advance!

EDIT: almost working code with scrollRectToVisible() method

public class MiniatursPanel extends JPanel{


private int indexOfChosenFrame = 0;
private ArrayList<JPanel> frames;
private JScrollPane scrollPane;
private JPanel innerPanel;



public MiniatursPanel(){
    setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),BorderFactory.createLoweredBevelBorder()));
    setPreferredSize(new Dimension(1200,170));
    setLayout(null);     
}

public void initialize(){
    int width = GifImageStats.getInstance().getWidth();
    int height = GifImageStats.getInstance().getHeight();
    int numberOfFrames = GifImageStats.getInstance().getNumberOfFrames();

    frames = new ArrayList(numberOfFrames);

    for (int i = 0; i < numberOfFrames; i++) {
        JPanel frameBox = new JPanel();
        frameBox.setLayout(new FlowLayout(FlowLayout.CENTER));
        JButton button = new JButton(String.valueOf(i+1));
        button.setPreferredSize(new Dimension(2*width,2*height));
        button.setBackground(Color.white);
        button.setFocusable(false);
        frameBox.add(button);
        frames.add(frameBox);
    }

    innerPanel = new JPanel();
    innerPanel.setLayout(new GridLayout(1,0,10,10));


    for (JPanel button : frames) {
        innerPanel.add(button);
    }

    scrollPane = new JScrollPane(innerPanel);
    scrollPane.setBounds(10, 10, 1180, 145);
    scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);

    highlightFrame(frames.get(0));

    add(scrollPane);
}


public void nextFrame(){
    if (indexOfChosenFrame == frames.size() - 1) {           
        unhighlightFrame(frames.get(indexOfChosenFrame));
        indexOfChosenFrame = 0;
        highlightFrame(frames.get(0));
    }else{
        unhighlightFrame(frames.get(indexOfChosenFrame));
        indexOfChosenFrame++;
        highlightFrame(frames.get(indexOfChosenFrame));
    }
}

public void previousFrame(){
    if (indexOfChosenFrame == 0) {           
        unhighlightFrame(frames.get(0));
        indexOfChosenFrame = frames.size()-1;
        highlightFrame(frames.get(indexOfChosenFrame));
    }else{
        unhighlightFrame(frames.get(indexOfChosenFrame));
        indexOfChosenFrame--;
        highlightFrame(frames.get(indexOfChosenFrame));
    }
}

private void highlightFrame(JPanel frame){
    Rectangle rect = frame.getBounds();
    rect.setBounds(frame.getX()-550, frame.getY(), frame.getWidth()+1050, frame.getHeight());
    innerPanel.scrollRectToVisible(rect);     
    frame.setBorder(BorderFactory.createLineBorder(Color.red,2));
}          

private void unhighlightFrame(JPanel frame){
    frame.setBorder(null);
}
Это было полезно?

Решение

The relevant method here is JComponent#scrollRectToVisible(Rectangle). It has to be called on the component that is in the viewport of the scroll pane. (In your case, this is the panel with the grid layout, which contains the other sub-panels).

The rectangle that is passed to this method can be the bounds of one sub-panel. In this case, the scoll pane will do the "minimum" scrolling that is necessary to make the given rectangle visible. If you want to make sure that the respective sub-panel is in the center, then you can increase the size of this rectangle - that is, you define a rectangle in a way that the desired sub-panel will be in the center.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


public class ScrollToVisible
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        int n = 20;
        final JPanel panel = new JPanel(new GridLayout(1,0));
        final List<JComponent> components = new ArrayList<JComponent>();
        for (int i=0; i<n; i++)
        {
            JComponent component = new JLabel(String.valueOf(i), SwingConstants.CENTER);
            component.setPreferredSize(new Dimension(100,100));
            component.setBorder(BorderFactory.createLineBorder(Color.BLACK));
            components.add(component);
            panel.add(component);
        }
        final JScrollPane scrollPane = new JScrollPane(panel);

        final JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, n-1, 1));
        spinner.addChangeListener(new ChangeListener()
        {
            JComponent selectedComponent = components.get(0);

            @Override
            public void stateChanged(ChangeEvent e)
            {
                selectedComponent.setBorder(BorderFactory.createLineBorder(Color.BLACK));

                int index = (Integer)spinner.getValue();
                JComponent component = components.get(index);
                Rectangle bounds = component.getBounds();

                // This would make the component "just" visible:
                //panel.scrollRectToVisible(bounds);

                // This will center the component:
                int cx = bounds.x + bounds.width / 2;
                int w = scrollPane.getViewport().getWidth();
                Rectangle r = new Rectangle(cx-w/2, bounds.y, w, bounds.height);
                panel.scrollRectToVisible(r);


                selectedComponent = component;
                selectedComponent.setBorder(BorderFactory.createLineBorder(Color.RED));

            }
        });

        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(scrollPane, BorderLayout.CENTER);
        f.getContentPane().add(spinner, BorderLayout.NORTH);


        f.setSize(800, 300);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

}

EDIT: You should NOT use setLayout(null), and you should not do manual calls to setBounds, and you should rarely use setPreferredSize. And... when you post code that already is so close to a https://stackoverflow.com/help/mcve (or even was created from a runnable example of another post) then you should make it really runnable. It's annoying to re-insert the boilerplate code and waste some time with debugging until you realize that initialize() is not called at all...

However, change the code according to this:

private void highlightFrame(JPanel frame){
    Rectangle rect = frame.getBounds();

    int c = rect.x + rect.width / 2; 
    int w = scrollPane.getViewport().getWidth();
    int x = c-w/2;
    rect.setBounds(x, rect.y, w, rect.height);

    innerPanel.scrollRectToVisible(rect);     
    frame.setBorder(BorderFactory.createLineBorder(Color.red,2));
}          

private void unhighlightFrame(JPanel frame){
    frame.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
}

The most important thing is to make sure that the size of the components is correct, by setting an empty border with the same size as the "highlighting" border.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top