JScrollPane Устанавливает видовое положение После “Масштабирования”
-
20-09-2019 - |
Вопрос
Код, который у меня есть здесь, использует MouseAdapter для прослушивания, чтобы пользователь "нарисовал" рамку вокруг области изображения, которую он хотел бы увеличить, и рассчитал отношение рамки к изображению.Затем он изменяет размер изображения в соответствии с рассчитанным соотношением.Эта часть работает.
Проблема, с которой я сталкиваюсь, заключается в том, что представление JScrollPane выглядит так, как будто оно все еще находится в том же верхнем левом положении после изменения размера изображения.Я попробовал несколько методов, которые, кажется, приблизились к желаемому результату, но не совсем.
Это прослушиватель, который находит коэффициент масштабирования и устанавливает позицию:
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();
}
}
}
Это компонент ImageComponent, который изменяет размер и отображает изображение:
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;
}
}
ResizeViewport заставляет viewport отображать весь ImageComponent при его увеличении, потому что в противном случае он обрезает изображение до того размера, который был у него ранее:
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));
}
}
Решение
Оказывается, нет ничего плохого ни в математике, использованной для расчета позиции, ни в том, как я разработал код.Проблема заключалась в том, что ImageComponent все еще рисовал в другом потоке, когда вычислялась позиция;поэтому возвращаются «ложные» значения для методов getWidth() и getHeight().Я решил проблему, используя следующий код:
EventQueue.invokeLater(new Runnable() {
public void run() {
port.setViewPosition(new Point(x,y));
}
});
Это позволяет завершить рисование, прежде чем пытаться вычислить размер изображения и установить положение в JScrollPane.Задача решена.Я все же хотел бы выразить благодарность людям, которые нашли время хотя бы рассмотреть эту проблему.
Другие советы
У меня была похожая проблема, но иногда панель окрашивалась неправильно в первый раз после изменения размера. После нескольких часов попыток найти обходной путь я нашел решение!
После изменения размера панели в области прокрутки лучше всего уничтожить область просмотра, установив значение null, а затем добавить панель обратно в область прокрутки, которая воссоздаст новую область просмотра, и все работает!
// Old viewport has to be replaced
scrollPane.setViewport(null);
scrollPane.setViewportView(zoomablePanel);
/Андерс
Проблема, с которой я сталкиваюсь, заключается в том, что представление JScrollPane отображается так, как будто оно все еще находится в том же верхнем левом положении после изменения размера изображения.
Извините, я не совсем понимаю, о чем вы спрашиваете.Возможно, я смог бы дать ответ, если бы вы немного прояснили ситуацию.