Browse Source

REPORT-51919 主题切换

【问题原因】
新增模版主题功能:
1. 修复主题缩略图模糊有锯齿的问题
2. 修复主题报表背景可能NPE的问题
3. 优化主题读取时的性能: 按需读取,延迟加载
4. 保存和删除主题时使用事务
5. 使用线程池优化主题列表界面的数据加载性能
【改动思路】
同上
research/11.0
Starryi 3 years ago
parent
commit
d1eadfe6a4
  1. 66
      designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeBlock.java
  2. 26
      designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeListPane.java
  3. 21
      designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeManagePane.java
  4. 61
      designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemePreviewPane.java
  5. 93
      designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeProfilePane.java
  6. 4
      designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeManageDialog.java
  7. 3
      designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeUsingDialog.java

66
designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeBlock.java

@ -23,11 +23,12 @@ import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.concurrent.ExecutorService;
/**
* @author Starryi
@ -54,12 +55,16 @@ public class TemplateThemeBlock<T extends TemplateTheme> extends JPanel {
private boolean hovering = false;
private MouseAdapter clickListener;
private final ExecutorService asyncThemeFetcher;
public TemplateThemeBlock(String name, TemplateThemeConfig<T> config, TemplateThemeProfilePane<T> profilePane) {
public TemplateThemeBlock(String name,
TemplateThemeConfig<T> config,
TemplateThemeProfilePane<T> profilePane,
ExecutorService asyncThemeFetcher) {
this.name = name;
this.config = config;
this.theme = this.config.find(name);
this.profilePane = profilePane;
this.asyncThemeFetcher = asyncThemeFetcher;
initializePane();
addMouseListener(new MouseAdapter() {
@ -87,7 +92,7 @@ public class TemplateThemeBlock<T extends TemplateTheme> extends JPanel {
}
});
refresh();
fetchTheme();
}
private void initializePane() {
@ -148,6 +153,9 @@ public class TemplateThemeBlock<T extends TemplateTheme> extends JPanel {
}
private void openProfileDialog() {
if (theme == null) {
return;
}
Window parent = SwingUtilities.getWindowAncestor(TemplateThemeBlock.this);
TemplateThemeProfileDialog<T> profileDialog = new TemplateThemeProfileDialog<>(parent, profilePane);
try {
@ -158,16 +166,46 @@ public class TemplateThemeBlock<T extends TemplateTheme> extends JPanel {
profileDialog.setVisible(true);
}
public void refresh() {
theme = this.config.find(name);
ThemeThumbnail thumbnail = theme.getThumbnail();
if (thumbnail != null) {
BufferedImage image = thumbnail.getImage();
if (image != null) {
thumbnailLabel.setIcon(new ImageIcon(thumbnail.getImage()));
}
public void fetchTheme() {
if (asyncThemeFetcher.isShutdown()) {
return;
}
repaint();
asyncThemeFetcher.submit(new Runnable() {
@Override
public void run() {
if (asyncThemeFetcher.isShutdown()) {
return;
}
theme = null;
// 耗时的同步操作,如远程设计器场景
theme = config.cachedFetch(name, new TemplateThemeConfig.CacheCondition<T>() {
@Override
public boolean shouldCacheTheme(T theme) {
// 如果Fetcher已经关闭就不放进缓存里了
// 因为可切换工作目录,所以submit时的工作目录环境与最终获取到主题数据时的工作目录环境可能不是同一个,
// 如果仍然放进缓存中,会污染当前工作目录环境的主题缓存.
// TODO: 除了根据asyncThemeFetch的关闭情况来判断是否缓存主题,也可以更加精细的判断前后的工作目录环境是否时同一个
// TODO: 后续看情况再优化吧.
return !asyncThemeFetcher.isShutdown();
}
});
if (asyncThemeFetcher.isShutdown()) {
return;
}
if (theme != null) {
ThemeThumbnail thumbnail = theme.getThumbnail();
if (thumbnail != null) {
Image image = thumbnail.getImage();
if (image != null) {
thumbnailLabel.setIcon(new ImageIcon(image));
}
}
}
repaint();
}
});
}
@Override
@ -193,4 +231,4 @@ public class TemplateThemeBlock<T extends TemplateTheme> extends JPanel {
GraphHelper.draw(g, rectangle, Constants.LINE_MEDIUM);
}
}
}
}

26
designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeListPane.java

@ -2,12 +2,14 @@ package com.fr.design.mainframe.theme;
import com.fr.base.theme.TemplateTheme;
import com.fr.base.theme.TemplateThemeConfig;
import com.fr.concurrent.NamedThreadFactory;
import com.fr.design.designer.IntervalConstants;
import com.fr.design.dialog.BasicPane;
import com.fr.design.event.ChangeEvent;
import com.fr.design.event.ChangeListener;
import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.module.ModuleContext;
import com.fr.stable.StringUtils;
import javax.swing.BorderFactory;
@ -20,6 +22,7 @@ import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
/**
* @author Starryi
@ -31,6 +34,12 @@ public class TemplateThemeListPane<T extends TemplateTheme> extends BasicPane {
public static final int BLOCK_GAP = IntervalConstants.INTERVAL_L1;
public static final int CONTENT_WIDTH = 630;
private final ExecutorService asyncThemeFetcher =
ModuleContext.getExecutor().newFixedThreadPool(
10,
new NamedThreadFactory("TemplateThemeListPane")
);
protected final TemplateThemeConfig<T> config;
private final TemplateThemeProfilePane<T> profilePane;
private final JPanel contentListPane;
@ -72,8 +81,7 @@ public class TemplateThemeListPane<T extends TemplateTheme> extends BasicPane {
contentListPane.removeAll();
List<String> names = config.getThemeNames();
for (String name: names) {
T theme = config.find(name);
if (theme != null) {
if (config.contains(name)) {
TemplateThemeBlock<T> block = createCachedTemplateThemeBlock(name);
contentListPane.add(block);
if (StringUtils.equals(name, config.getTheme4NewTemplate().getName())) {
@ -92,7 +100,7 @@ public class TemplateThemeListPane<T extends TemplateTheme> extends BasicPane {
}
private TemplateThemeBlock<T> createNewTemplateThemeBlock(String name) {
final TemplateThemeBlock<T> block = new TemplateThemeBlock<>(name, config, profilePane);
final TemplateThemeBlock<T> block = new TemplateThemeBlock<>(name, config, profilePane, asyncThemeFetcher);
block.addClickListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
@ -138,17 +146,17 @@ public class TemplateThemeListPane<T extends TemplateTheme> extends BasicPane {
switch (event.action) {
case DEFAULT_THEME_4_NEW_TEMPLATE_UPDATE: {
if (block4newTemplate != null) {
block4newTemplate.refresh();
block4newTemplate.fetchTheme();
}
if (existingBlock != null) {
existingBlock.refresh();
existingBlock.fetchTheme();
}
block4newTemplate = existingBlock;
break;
}
case UPDATE: {
if (existingBlock != null) {
existingBlock.refresh();
existingBlock.fetchTheme();
}
break;
}
@ -186,4 +194,8 @@ public class TemplateThemeListPane<T extends TemplateTheme> extends BasicPane {
themeConfigChangeListener = null;
}
}
}
public void stopAsyncFetchTheme() {
asyncThemeFetcher.shutdown();
}
}

21
designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeManagePane.java

@ -22,6 +22,10 @@ import com.fr.design.menu.ToolBarDef;
import com.fr.general.IOUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.third.checkerframework.checker.nullness.qual.Nullable;
import com.fr.third.guava.util.concurrent.FutureCallback;
import com.fr.transaction.Configurations;
import com.fr.transaction.WorkerFacade;
import javax.swing.BorderFactory;
import javax.swing.JOptionPane;
@ -213,7 +217,12 @@ public class TemplateThemeManagePane<T extends TemplateTheme> extends BasicPane
Toolkit.i18nText("Fine-Design_Template_Theme_Manager_Pane_Delete_Tip", theme.getName()),
Toolkit.i18nText("Fine-Design_Basic_Delete"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.YES_OPTION) {
removeTheme(theme.getName());
Configurations.modify(new WorkerFacade(config.getClass()) {
@Override
public void run() {
config.removeTheme(theme.getName());
}
});
}
}
}
@ -244,10 +253,6 @@ public class TemplateThemeManagePane<T extends TemplateTheme> extends BasicPane
}
}
private void removeTheme(String name) {
config.removeTheme(name);
}
public static class BottomLineBorder extends LineBorder {
private BottomLineBorder(Color color, int thickness) {
@ -280,4 +285,8 @@ public class TemplateThemeManagePane<T extends TemplateTheme> extends BasicPane
public void stopListenThemeConfig() {
themeListPane.stopListenThemeConfig();
}
}
public void stopAsyncFetchTheme() {
themeListPane.stopAsyncFetchTheme();
}
}

61
designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemePreviewPane.java

@ -2,9 +2,17 @@ package com.fr.design.mainframe.theme;
import com.fr.base.chart.chartdata.CallbackEvent;
import com.fr.base.theme.TemplateTheme;
import com.fr.base.theme.settings.ThemeThumbnail;
import com.fr.design.mainframe.theme.preview.ThemePreviewed;
import com.fr.log.FineLoggerFactory;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
/**
* @author Starryi
@ -26,4 +34,55 @@ public abstract class TemplateThemePreviewPane<T extends TemplateTheme> extends
this.repaint();
}
}
}
protected Image createThumbnailImage() {
BufferedImage image = null;
int thumbnailWidth = ThemeThumbnail.WIDTH;
int thumbnailHeight = ThemeThumbnail.HEIGHT;
float thumbnailAspect = 1.0F * thumbnailWidth / thumbnailHeight;
int width = getWidth();
int height = getHeight();
float aspect = 1.0F * width / height;
if (thumbnailAspect > aspect) {
height = (int) (width / thumbnailAspect);
} else {
width = (int) (height * thumbnailAspect);
}
try {
// 使用TYPE_INT_RGB和new Color(255, 255, 255, 1)设置有透明背景buffer image,
// 使得创建出来的透明像素是(255, 255, 255, 1),而不是(0, 0, 0, 0)
// 这样不支持透明通道缩略图的旧设计器打开新设计器创建的模版时,就不会创建出拥有黑色背景的缩略图
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// 创建一个支持透明背景的buffer image
image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = image.createGraphics();
// 使用TYPE_INT_RGB和new Color(255, 255, 255, 1)设置有透明背景buffer image,
// 使得创建出来的透明像素是(255, 255, 255, 1),而不是(0, 0, 0, 0)
// 这样不支持透明通道缩略图的旧设计器打开新设计器创建的模版时,就不会创建出拥有黑色背景的缩略图
g2d.setColor(new Color(255, 255, 255, 1));
g2d.fillRect(0, 0, width, height);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.clipRect(0, 0, width, height);
paint(g2d);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
if (image != null) {
return image.getScaledInstance(thumbnailWidth, thumbnailHeight, BufferedImage.SCALE_SMOOTH);
}
return null;
}
}

93
designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeProfilePane.java

@ -25,8 +25,10 @@ import com.fr.design.mainframe.theme.edit.ui.ColorListPane;
import com.fr.design.mainframe.theme.edit.ui.LabelUtils;
import com.fr.design.mainframe.theme.ui.BorderUtils;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.transaction.CallBackAdaptor;
import com.fr.transaction.Configurations;
import com.fr.transaction.WorkerFacade;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
@ -36,19 +38,17 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.util.List;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Transparency;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.util.List;
/**
* @author Starryi
@ -316,7 +316,7 @@ public abstract class TemplateThemeProfilePane<T extends TemplateTheme> extends
String name = theme.getName();
currentIsNewTheme = config.find(name) == null;
currentIsNewTheme = config.cachedFetch(name) == null;
nameTextField.setText(name);
nameTextField.setEnabled(StringUtils.isEmpty(name));
@ -342,7 +342,12 @@ public abstract class TemplateThemeProfilePane<T extends TemplateTheme> extends
theme.setName(this.nameTextField.getText());
theme.setThumbnail(updateThumbnail());
Image thumbnailImage = themePreviewPane.createThumbnailImage();
if (thumbnailImage != null) {
ThemeThumbnail thumbnail = new ThemeThumbnail();
thumbnail.setImage(thumbnailImage);
theme.setThumbnail(thumbnail);
}
ThemedCellStyleList cellStyleConfig = this.cellStyleSettingPane.updateBean();
theme.setCellStyleList(cellStyleConfig);
@ -363,42 +368,6 @@ public abstract class TemplateThemeProfilePane<T extends TemplateTheme> extends
protected abstract void updateBean(T theme);
protected ThemeThumbnail updateThumbnail() {
BufferedImage image = null;
int imgWidth = ThemeThumbnail.WIDTH;
int imgHeight = ThemeThumbnail.HEIGHT;
int canvasWidth = themePreviewPane.getWidth();
int canvasHeight = themePreviewPane.getHeight();
try {
// 使用TYPE_INT_RGB和new Color(255, 255, 255, 1)设置有透明背景buffer image,
// 使得创建出来的透明像素是(255, 255, 255, 1),而不是(0, 0, 0, 0)
// 这样不支持透明通道缩略图的旧设计器打开新设计器创建的模版时,就不会创建出拥有黑色背景的缩略图
image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// 创建一个支持透明背景的buffer image
image = g2d.getDeviceConfiguration().createCompatibleImage(imgWidth, imgHeight, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = image.createGraphics();
// 使用TYPE_INT_RGB和new Color(255, 255, 255, 1)设置有透明背景buffer image,
// 使得创建出来的透明像素是(255, 255, 255, 1),而不是(0, 0, 0, 0)
// 这样不支持透明通道缩略图的旧设计器打开新设计器创建的模版时,就不会创建出拥有黑色背景的缩略图
g2d.setColor(new Color(255, 255, 255, 1));
g2d.fillRect(0, 0, imgWidth, imgHeight);
float scale = Math.max(1.0F * imgWidth / canvasWidth, 1.0F * imgHeight / canvasHeight);
g2d.scale(scale, scale);
themePreviewPane.paint(g2d);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
ThemeThumbnail thumbnail = new ThemeThumbnail();
thumbnail.setImage(image);
return thumbnail;
}
public UIButton createSaveButton() {
saveButton = new UIButton();
saveButton.setText(Toolkit.i18nText("Fine-Design_Basic_Save"));
@ -410,10 +379,20 @@ public abstract class TemplateThemeProfilePane<T extends TemplateTheme> extends
boolean canBeSaved = checkThemeCanBeSavedAndUpdateUI(currentIsNewTheme, nameTextField, nameErrorLabel, saveButton);
if (canBeSaved && theme != null) {
theme.setName(nameTextField.getText());
config.addTheme(theme, true);
currentIsNewTheme = false;
nameTextField.setEnabled(false);
saveAsButton.setEnabled(true);
Configurations.modify(new WorkerFacade(config.getClass()) {
@Override
public void run() {
config.addTheme(theme, true);
}
}.addCallBack(new CallBackAdaptor() {
@Override
public void afterCommit() {
super.afterCommit();
currentIsNewTheme = false;
nameTextField.setEnabled(false);
saveAsButton.setEnabled(true);
}
}));
}
}
});
@ -475,9 +454,19 @@ public abstract class TemplateThemeProfilePane<T extends TemplateTheme> extends
theme.setName(nameTextField.getText());
theme.setRemovable(true);
theme.setMutable(true);
config.addTheme(theme, true);
exit();
parent.exit();
Configurations.modify(new WorkerFacade(config.getClass()) {
@Override
public void run() {
config.addTheme(theme, true);
}
}.addCallBack(new CallBackAdaptor() {
@Override
public void afterCommit() {
super.afterCommit();
exit();
parent.exit();
}
}));
}
}
});
@ -560,7 +549,7 @@ public abstract class TemplateThemeProfilePane<T extends TemplateTheme> extends
return StringUtils.isEmpty(name);
}
private boolean isThemeNameDuplicated(String name) {
return config.find(name) != null;
return config.cachedFetch(name) != null;
}
private boolean checkThemeCanBeSavedAndUpdateUI(boolean checkDuplicated, UITextField textField, UILabel errorLabel, UIButton... actionButtons) {
String error = StringUtils.EMPTY;
@ -588,4 +577,4 @@ public abstract class TemplateThemeProfilePane<T extends TemplateTheme> extends
return valid;
}
}
}

4
designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeManageDialog.java

@ -85,7 +85,9 @@ public class TemplateThemeManageDialog extends TemplateThemeDialog {
public void exit() {
formThemesManagerPane.stopListenThemeConfig();
formThemesManagerPane.stopAsyncFetchTheme();
reportThemesManagerPane.stopListenThemeConfig();
reportThemesManagerPane.stopAsyncFetchTheme();
}
}
}
}

3
designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeUsingDialog.java

@ -146,6 +146,7 @@ public class TemplateThemeUsingDialog<T extends TemplateTheme> extends TemplateT
public void exit() {
themeListPane.stopListenThemeConfig();
themeListPane.stopAsyncFetchTheme();
super.exit();
}
@ -157,4 +158,4 @@ public class TemplateThemeUsingDialog<T extends TemplateTheme> extends TemplateT
usingCurrentThemeButton.setEnabled(false);
}
}
}
}
Loading…
Cancel
Save