Dúvidas ao trabalhar com JScrollPane e JImagePanel (Towel)

Boa tarde,

não sou um profundo conhecedor de interfaces gráficas e layout ,e como preciso desenvolver uma aplicação usando swing estou apanhanho um pouco.

Vamos lá :

Eu tenho um JFrame, tenho um panel ( ficarei trocando esse panel usando CardLayout). Esse painel é responsavel por armazenar todos os componentes dessa tela.

Dentro desse painel eu terei um JScrolPane, que por sua vez irá conter um JImagePanel do projeto towel com uma imagem bem grande (por isso o uso do JScrolPane).

O meu problema é que, eu consigo adicionar o panel dentro do JScrolPane porém, as barras de rolagem nao aparecem e eu nao consigo “navegar” pela imagem. Alguem mais experiente consegue ver oq estou fazendo de errado?

segue o código

Construtor do JFrame :

//Construtor do frame
 this.cardLayout = new CardLayout();
        this.setLayout(null);
        this.setResizable(false);
        this.setMinimumSize(new Dimension(1280, 720));
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);

        //panel de background
        this.backgroundPanel = new JPanel(this.cardLayout);
        this.setContentPane(backgroundPanel);

        //panel inicial
        JImagePanel panelInicial = new JImagePanel(loadImage("resources/LogoPNG.png"));
        panelInicial.setFillType(JImagePanel.FillType.CENTER);
        panelInicial.setBackground(Color.WHITE);
        panelInicial.setLayout(null);
        panelInicial.setMinimumSize(new Dimension(panelInicial.getImage().getWidth(), panelInicial.getImage().getHeight()));

        //panel de novo projeto
        JPanel newProject = this.createPanelNewProject();

        this.backgroundPanel.add(panelInicial, CardEnum.CARD_VAZIO.getCard());
        this.backgroundPanel.add(newProject, CardEnum.PAINT.getCard());

Criação do Scroll e do ImagePanel

/**
     * Cria o Painel de Visualização de imagem
     */
    private JScrollPane createImagePanel() {

        BufferedImage image = loadImage("resources/imagem.png");
        this.imagePanel = new JImagePanel(image);
        imagePanel.setFillType(JImagePanel.FillType.CENTER);
        imagePanel.setBounds(300, 250, image.getWidth(), image.getHeight());

        //centralizar o painel no scrollPane
        imagePanel.setVisible(true);
        imagePanel.setLayout(null);
        imagePanel.repaint();

        JScrollPane scrol = new JScrollPane(imagePanel);
        scrol.setBounds(190, 35, 500, 620);
        scrol.setVisible(true);

        return scrol;
   }

Esse é o painel principal do frame ,que ira conter os componentes

 /**
     * Cria o painel de novo projeto
     */
    private JPanel createPanelNewProject() {
        JPanel panel = new JPanel();
        panel.setBackground(new Color(245, 245, 245));
        panel.setBorder(BorderFactory.createTitledBorder(null, "Novo Projeto",
                TitledBorder.LEFT, TitledBorder.CENTER, null, new Color(0, 0, 0)));
        panel.setLayout(null);

        //painel da imagem
        JScrollPane projectPanel = this.createImagePanel();
        panel.add(projectPanel);
        panel.repaint();
        panel.setVisible(true);
        return panel;
    }

Obrigado a todos.

Pessoal acabei mudando um pouquinho o código e resolveu o problema, porém me gerou outro.

Uma das funcionalidades da aplicação é permitir ao usuario pintar a imagem usando um algoritmo de FloodFill.

Eu criava o JImagePanel exatamente do tamanho da minha imagem ( usava um setBounds() ) , e adicionava esse JImagePanel em um outro JPanel de background. Feito isso eu adicionei um MouseListener pra quando eu clicasse em uma região do JImagePanel, consequentemente seria a coordenada da imagem que eu gostaria de preencher, dado que os 2 tem a mesma dimensão.

ai ai tudo lindo, o flood fill funcionava direitinho. Porem se eu utilizasse imagens muito grandes, eu não teria a opção de scroll.

Entao resolvi criar o JImagePanel do mesmo modo, mas adiciona-lo a um JScrollPane. Ao fazer isso percebi um problema:

as dimensões da imagem que estou trabalhando para teste é de 800 x 520 pixels, porém ao clicar no JImagePanel a coordenada em Y que estou pegando é 548, 550 , logo estou tomando um “java.lang.ArrayIndexOutOfBoundsException: Coordinate out of bounds!” no momento de pegar o RGB daquele ponto.

Eu não faço idéia do porque isso acontece, e muito menos como resolver.

Por favor pessoal, me deem uma luz!!

Segue o código utilizado :

public FramePrincipal() {
        //frame principal
        this.cardLayout = new CardLayout();
        this.setLayout(new GridLayout(1, 1));
        this.setResizable(false);
        this.setMinimumSize(new Dimension(1280, 720));
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.pack();
        this.setLocationRelativeTo(null);

        //panel de background
        this.backgroundPanel = new JPanel(this.cardLayout);
        this.getContentPane().add(backgroundPanel);

        //panel inicial
        JImagePanel panelInicial = new JImagePanel(loadImage("resources/LogoPNG.png"));
        panelInicial.setFillType(JImagePanel.FillType.CENTER);
        panelInicial.setBackground(Color.WHITE);
        panelInicial.setLayout(new GridLayout(1, 1));
        panelInicial.setMinimumSize(new Dimension(panelInicial.getImage().getWidth(), panelInicial.getImage().getHeight()));

        //panel de novo projeto
        JPanel newProject = this.createPanelNewProject();

        //barra de menu
        JMenuBar menuBar = createMenuBar();
        JMenu menuConsultas = this.createMenuProjeto();
        menuBar.add(menuConsultas);
        this.setJMenuBar(menuBar);

        this.backgroundPanel.add(panelInicial, CardEnum.CARD_VAZIO.getCard());
        this.backgroundPanel.add(newProject, CardEnum.PAINT.getCard());

    }

    /**
     * Cria o painel de novo projeto
     */
    private JPanel createPanelNewProject() {
        JPanel panelNewProject = new JPanel();
        panelNewProject.setBackground(new Color(245, 245, 245));
        panelNewProject.setBorder(BorderFactory.createTitledBorder(null, "Novo Projeto",
                TitledBorder.LEFT, TitledBorder.CENTER, null, new Color(0, 0, 0)));
        panelNewProject.setLayout(null);

        //painel da imagem
        JScrollPane painelComScroll = this.createImagePanel();
        panelNewProject.add(painelComScroll);

        //color chooser
        this.createColorChooser();
        JPanel panelColor = new JPanel();
        panelColor.setLayout(null);
        panelColor.setBounds(20, 30, 280, 500);
        panelColor.add(chooser);
        panelNewProject.add(panelColor);

        panelNewProject.setVisible(true);
        panelNewProject.repaint();
        return panelNewProject;
    }

    /**
     * Cria o Painel de Visualização de imagem
     */
    private JScrollPane createImagePanel() {

        BufferedImage image = loadImage("resources/arcored.png");
        this.imagePanel = new JImagePanel(image);
        imagePanel.setFillType(JImagePanel.FillType.CENTER);
        imagePanel.setBounds(0, 0, image.getWidth(), image.getHeight());
        imagePanel.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
        imagePanel.setBackground(Color.WHITE);
        imagePanel.setVisible(true);
        imagePanel.setLayout(null);
        imagePanel.repaint();

        JScrollPane scrol = new JScrollPane(imagePanel);
        scrol.setBounds(350, 30, 650, 630);
        scrol.setBorder(new LineBorder(Color.BLACK, 1));
        scrol.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        scrol.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scrol.setVisible(true);

        imagePanel.addMouseListener(new MouseListener() {
            @Override
            public void mousePressed(MouseEvent me) {
            }

            @Override
            public void mouseReleased(MouseEvent me) {
            }

            @Override
            public void mouseEntered(MouseEvent me) {
            }

            @Override
            public void mouseExited(MouseEvent me) {
            }

            @Override
            public void mouseClicked(MouseEvent me) {
                int posicaoX = me.getX();
                int posicaoY = me.getY();

                final Point p1 = new Point();
                p1.x = posicaoX;
                p1.y = posicaoY;

                BufferedImage image = FramePrincipal.this.imagePanel.getImage();
                int oldColor = image.getRGB(posicaoX, posicaoY);
                Color newColor = FramePrincipal.this.chooser.getColor();
                FloodFill.floodFill(image, p1, oldColor, newColor.getRGB());
                FramePrincipal.this.repaint();
            }
        });

        return scrol;
    }

O problema é que para o JScrollPane funcionar, é necessário implementar o método getPreferredSize(). Esse método deve retornar o tamanho total da imagem.

Faça o objeto LoopImage retornar o tamanho da imagem atual e repasse isso para o JImagePanel.

Fiz a modificação das classes lá no GitHub do Towel:

Agora, se você está fazendo uma ferramenta de desenho, recomendo que faça tudo pelo BufferedImage e só jogue resultados para o JImagePanel.

Cara, me desculpe, mas eu não entendi.

O problema do JScrollPane era realmente o preferedSize. Eu setei isso no painel da minha imagem, com as dimensões da imagem e o Scroll funcionou direitinho.

Agora o problema em relação a coordenada do clique eu realmente não entendi.

Essa do LoopImage tbm. Desculpe a minha ignorancia.

Aproveito para tirar outra duvida. Se eu tenho o seguinte frame :

        JFrame frame = new JFrame();
        frame.setLayout(new BorderLayout());
        frame.setResizable(false);
        frame.setSize(new Dimension(1280, 720));
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);

E eu faço o seguinte :

        JImagePanel panelInicial = new JImagePanel(loadImage("src/resources/LogoPNG.png"));
        panelInicial.setFillType(JImagePanel.FillType.CENTER);
        panelInicial.setBackground(Color.WHITE);
        frame.getContentPane().add(BorderLayout.CENTER, panelInicial);
        frame.repaint();

Se eu executo o código no netbeans a minha imagem aparece normalmente.
Agora se eu mando construir o jar, e executo o programa pelo jar, a minha imagem não aparece.

Não creio que isso tenha relação com o JImagePanel, é mais uma duvida mesmo.

muito obrigado pela ajuda cara.

Abraço

Eu sugeri uma modificação para que o JImagePanel utilize como PreferredSize o tamanho da imagem automaticamente. E isso envolvia alterar 2 classes: a do JImagePanel e a LoopImage. Coloquei ali o link para as duas classes alteradas.

Não dei nenhuma sugestão sobre a coordenada do clique.

Quanto ao problema da imagem não aparecer no .jar, poste o método loadImage. Se for igual ao do post do Mark não irá funcionar, pois ele é feito para usar Files e File é um recurso que está no disco, e não no .jar.

Se for para usar recursos de dentro do .jar, o método deveria ser assim:

public BufferedImage loadImage(String source) { return ImageIO.read(getClass().getResource(source)); }

E a carga deveria ser assim:

[quote=ViniGodoy]Eu sugeri uma modificação para que o JImagePanel utilize como PreferredSize o tamanho da imagem automaticamente. E isso envolvia alterar 2 classes: a do JImagePanel e a LoopImage. Coloquei ali o link para as duas classes alteradas.

Não dei nenhuma sugestão sobre a coordenada do clique.

Quanto ao problema da imagem não aparecer no .jar, poste o método loadImage. Se for igual ao do post do Mark não irá funcionar, pois ele é feito para usar Files e File é um recurso que está no disco, e não no .jar.

Se for para usar recursos de dentro do .jar, o método deveria ser assim:

public BufferedImage loadImage(String source) { return ImageIO.read(getClass().getResource(source)); }

E a carga deveria ser assim:

Obrigado ViniGodoy, suas dicas foram de grande ajuda.