Pergunta

O código que tenho aqui está usando um MouseAdapter para ouvir o usuário "desenhar" uma caixa em torno da área de uma imagem que eles gostariam de aumentar o zoom e calcular a proporção da caixa e a imagem. Em seguida, redimensiona a imagem para a proporção calculada. Esta parte funciona.

A questão que estou tendo está fazendo com que a visualização JScrollPane pareça como se ainda esteja na mesma posição superior esquerda após a redação da imagem. Eu tentei vários métodos que parecem ter chegado perto do resultado que eu quero, mas não exatamente.

Este é o ouvinte que encontra a proporção de escala e define a posição:

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Graphics;
import java.awt.Point;

import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.JComponent;

public class DynamicZoom extends MouseAdapter {

private Point start;
private Point end;
private double zoom = 1.0;
private JScrollPane pane;
private JViewport port;


public void mousePressed(MouseEvent e) {
    if(e.getButton() == MouseEvent.BUTTON1) {
        this.pane = (JScrollPane)e.getSource();
        this.port = pane.getViewport();
        start = e.getPoint();
    }
}

public void mouseReleased(MouseEvent e) {
    if(this.pane != null) {
        Point curr = this.port.getViewPosition();
        end = e.getPoint();

        ImageComponent canvas = (ImageComponent)this.port.getView();
        zoom = canvas.getScale();
        double factor = 0.0;
        double selectedWidth = Math.abs(end.getX() - start.getX());
        double selectedHeight = Math.abs(end.getY() - start.getY());
        if(selectedWidth > selectedHeight)
            factor = this.port.getWidth() / selectedWidth;
        else
            factor = this.port.getHeight() / selectedHeight;

        zoom *= factor;
        int x = (int)((start.x+curr.x)*zoom);
        int y = (int)((start.y+curr.y)*zoom);

        Point point = new Point(x, y);

        ((ImageComponent)(this.port.getView())).setScale(zoom);

        ResizeViewport.resize(pane);
        this.port.setViewPosition(point);
    }
}

public void mouseDragged(MouseEvent e) {
    if(this.pane != null) {
        Graphics g = this.port.getGraphics();
        int width = this.start.x - e.getX();
        int height = this.start.y - e.getY();

        int w = Math.abs( width );
        int h = Math.abs( height );
        int x = width < 0 ? this.start.x : e.getX();
        int y = height < 0 ? this.start.y : e.getY();
        g.drawRect(x, y, w, h);
        this.port.repaint();
    }
}

}

Este é o componente de imagem que ele redimensiona e exibe a imagem:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;

public class ImageComponent extends JComponent {
private static final long serialVersionUID = 1975488835382044371L;
private BufferedImage img = null;

private double scale = 0.0;

public ImageComponent() {}

public ImageComponent(BufferedImage img) {
    this.displayPage(img);
}

@Override
public void paint(Graphics g) {
    Graphics2D g2 = ((Graphics2D)g);
    if(this.img != null) {
        int width = (int)(this.img.getWidth() * this.scale);
        int height = (int)(this.img.getHeight() * this.scale);
        this.setPreferredSize(new Dimension(width, height));
        g2.drawImage(this.img, 0, 0, width, height, null, null);
        g2.dispose();
    }
}

public void displayPage(BufferedImage pic) {
    if(img != null) {
        this.img = pic;
    }
}

public BufferedImage getPage() {
    return this.img;
}

public void setScale(double ratio) {
    if(ratio > .04) {
        this.scale = ratio;
        this.repaint();
    }
}

public double getScale() {
    return this.scale;
}
}

O REDRIEDVIEWPORT força a viewport a mostrar todo o componente de imagem quando é ampliado, porque, caso contrário, prenderá a imagem do tamanho que era anteriormente:

import java.awt.Dimension;

import javax.swing.JScrollPane;
public class ResizeViewport {
public static void resize(JScrollPane scroll) {
    int vw = scroll.getWidth()-scroll.getVerticalScrollBar().getWidth();
    int vh = scroll.getHeight()-scroll.getHorizontalScrollBar().getHeight();
    scroll.getViewport().setViewSize(new Dimension(vw, vh));
}
}
Foi útil?

Solução

Acontece que não há nada de errado com a matemática usada para calcular a posição ou a maneira como eu projetei o código. O problema era que o componente da imagem ainda estava pintando em outro fio quando a posição estava sendo calculada; Portanto, retornando valores "falsos" para os métodos getWidth () e GetHeight (). Eu resolvi o problema usando o seguinte código:

EventQueue.invokeLater(new Runnable() {
   public void run() {
      port.setViewPosition(new Point(x,y));
   }
});

Isso permite que a pintura termine antes de tentar calcular o tamanho da imagem e definir a posição no JScrollPane. Problema resolvido. Eu ainda gostaria de agradecer às pessoas que dedicaram um tempo para pelo menos revisar esse problema.

Outras dicas

Eu tive um problema semelhante, mas às vezes o painel foi pintado errado na primeira vez após o redimensionamento, depois de horas tentando encontrar uma solução alternativa, encontrei uma solução!

Após um redimensionamento do painel no rollpane, é melhor destruir a viewport, definindo para NULL e adicionando o painel de volta ao scrollpane que recriará uma nova viewport e tudo está funcionando!

// Old viewport has to be replaced
scrollPane.setViewport(null);
scrollPane.setViewportView(zoomablePanel);

/Anders

A questão que estou tendo está fazendo com que a visualização JScrollPane pareça como se ainda esteja na mesma posição superior esquerda após a redação da imagem.

Desculpe, não tenho certeza do que você está perguntando. Eu posso dar uma resposta se você esclarecer um pouco.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top