我正在尝试使用MVC设计模式在Java中创建图像编辑应用程序。因此,事件处理在控制器中,状态和与状态相关的操作存储在模型中,用户看到的所有内容都存储在视图中。
当我打开图像时,会出现一个缩放框,显示正在显示的图像的缩放级别。首次在paintComponent()
中渲染时,会自动计算缩放(请参阅步骤3)。当我打开图像时,我希望将缩放级别设置为计算的级别。 问题是缩放级别显示0 ,我知道原因。让我解释一下:
1。 打开菜单项的 ActionListener
被触发
:
class MenuBarFileOpenListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
File fileChooserReturnValue = view.showAndGetValueOfFileChooser();
if (fileChooserReturnValue != null) {
try {
// irrelevant code omitted
view.addDocument(newDocument);
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
}
2。 view.addDocument()
被称为
注意:这是问题的根源
JPSView中的:
public void addDocument(DocumentModel document) {
// irrelevant code omitted
// CanvasPanelView extends JPanel
documentsTabbedPane.add(newDocumentView.getCanvasPanelView());
// THIS IS THE ROOT OF THE PROBLEM
double currentZoomFactor = getCurrentCanvasPanelView().getZoomFactor();
// formatting the text
String zoomLevelText = statusBar_zoomLevelTextField_formatter.format(currentZoomFactor);
// setting the text of the text field
statusBar_zoomLevelTextField.setText(zoomLevelText);
}
第3。一段时间后, paintComponent()
正在运行
扩展了JPanel:
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (initialRender) {
initialRender = false;
// calculates a good zoom level
setZoomFit();
}
// irrelevant code omitted
g.drawImage(image, destinationX1, destinationY1, destinationX2,
destinationY2, sourceX1, sourceY1, sourceX2, sourceY2, null);
}
在第2部分中,我们有这行代码:
double currentZoomFactor = getCurrentCanvasPanelView().getZoomFactor();
调用getZoomFactor()
时,当前CanvasPanelView
的大小不得超过,因为它会返回0
。我以前遇到过这个问题,而我的解决方案是#3中的这些代码:
if (initialRender) {
initialRender = false;
setZoomFit();
}
当调用paintComponent()时,CanvasPanelView
必须在此时被赋予大小,但是在调用getZoomFactor()时则不行。 paintComponent(),因此也就是setZoomFit(),显然是在getZoomFactor()之后。
如何在图片打开时正确显示图像的缩放级别?
答案 0 :(得分:4)
基本上,你有一种竞争条件(尽可能多地在单线程中)。
正在发生的事情是您正在添加新视图,希望它能够立即布局。这不是Swing中的工作原理。向容器添加新组件时,会发出请求以更新层次结构中所有组件的布局,并布置所有已标记为无效的容器。在当前的调用周期完成且Event Dispatching Thread有时间处理所有请求之前,这种情况不会发生。
相反,您可以通过向事件调度线程的末尾添加请求来安排稍后查找。这可以确保您的请求将在EDT上的所有当前任务执行之前执行(可能还有其他人要遵循,但您已经挤进去了。)
以下示例说明了这一点。
它向JTabbedPane
添加一个新面板,转储它的当前大小,使用SwingUtilities.invokeLater
在将来一次请求回调并再次转储面板大小。您会发现第一个请求是0x0
,第二个请求是有效值(取决于框架的大小和外观)
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestTabbedPane01 {
public static void main(String[] args) {
new TestTabbedPane01();
}
private JTabbedPane tabbedPane;
public TestTabbedPane01() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
tabbedPane = new JTabbedPane();
JButton btnAdd = new JButton("Add");
btnAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(new JLabel(String.valueOf(tabbedPane.getComponentCount())));
tabbedPane.add(String.valueOf(tabbedPane.getComponentCount()), panel);
System.out.println("New Panel Size = " + panel.getSize());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
System.out.println("New Panel Size (later) = " + panel.getSize());
}
});
}
});
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(tabbedPane);
frame.add(btnAdd, BorderLayout.SOUTH);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}