From db4860bda5dc4281ec81ff10a62ffd4da5e8fa9a Mon Sep 17 00:00:00 2001 From: vito Date: Wed, 6 Jun 2018 11:34:42 +0800 Subject: [PATCH] =?UTF-8?q?REPORT-8393=2010.0=E5=90=AF=E5=8A=A8=E5=8A=A8?= =?UTF-8?q?=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/com/fr/start/SplashStrategy.java | 34 +++ .../src/com/fr/start/Designer.java | 17 +- .../src/com/fr/start/SplashContext.java | 142 ++++++++++++ .../src/com/fr/start/fx/FastGifImage.java | 210 +++++++++++++++++ .../com/fr/start/fx/PrismImageLoader2.java | 211 ++++++++++++++++++ .../src/com/fr/start/fx/SplashFx.java | 49 ++++ .../src/com/fr/start/fx/SplashFxWindow.java | 133 +++++++++++ .../src/com/fr/start/jni/SplashJNI.java | 69 ++++++ .../src/com/fr/start/jni/SplashMac.java | 87 ++++++++ .../src/com/fr/start/jni/splash.dylib | Bin 0 -> 32320 bytes .../com/fr/start/module/DesignerStartup.java | 22 +- 11 files changed, 960 insertions(+), 14 deletions(-) create mode 100644 designer-base/src/com/fr/start/SplashStrategy.java create mode 100644 designer-realize/src/com/fr/start/SplashContext.java create mode 100644 designer-realize/src/com/fr/start/fx/FastGifImage.java create mode 100644 designer-realize/src/com/fr/start/fx/PrismImageLoader2.java create mode 100644 designer-realize/src/com/fr/start/fx/SplashFx.java create mode 100644 designer-realize/src/com/fr/start/fx/SplashFxWindow.java create mode 100644 designer-realize/src/com/fr/start/jni/SplashJNI.java create mode 100644 designer-realize/src/com/fr/start/jni/SplashMac.java create mode 100755 designer-realize/src/com/fr/start/jni/splash.dylib diff --git a/designer-base/src/com/fr/start/SplashStrategy.java b/designer-base/src/com/fr/start/SplashStrategy.java new file mode 100644 index 0000000000..8f7673d5c9 --- /dev/null +++ b/designer-base/src/com/fr/start/SplashStrategy.java @@ -0,0 +1,34 @@ +package com.fr.start; + +/** + * 启动动画策略接口 + * + * @author vito + * @date 2018/6/1 + */ +public interface SplashStrategy { + + /** + * 显示启动动画窗口 + */ + void show(); + + /** + * 隐藏启动动画窗口 + */ + void hide(); + + /** + * 设置模块加载信息 + * + * @param text 更新的文字 + */ + void updateModuleLog(String text); + + /** + * 设置感谢文字 + * + * @param text 更新的文字 + */ + void updateThanksLog(String text); +} diff --git a/designer-realize/src/com/fr/start/Designer.java b/designer-realize/src/com/fr/start/Designer.java index ae382d2fa3..770810127a 100644 --- a/designer-realize/src/com/fr/start/Designer.java +++ b/designer-realize/src/com/fr/start/Designer.java @@ -43,10 +43,13 @@ import com.fr.general.ComparatorUtils; import com.fr.general.Inter; import com.fr.module.Module; import com.fr.module.ModuleContext; +import com.fr.stable.OperatingSystem; import com.fr.stable.ProductConstants; import com.fr.stable.StableUtils; import com.fr.stable.StringUtils; import com.fr.stable.xml.XMLTools; +import com.fr.start.fx.SplashFx; +import com.fr.start.jni.SplashMac; import com.fr.start.module.StartupArgs; import javax.swing.JComponent; @@ -85,7 +88,8 @@ public class Designer extends BaseDesigner { * @param args 参数 */ public static void main(String[] args) { - + SplashContext.getInstance().registerSplash(createSplash()); + SplashContext.getInstance().show(); Module designerRoot = ModuleContext.parseRoot("designer-startup.xml"); //传递启动参数 designerRoot.setSingleton(StartupArgs.class, new StartupArgs(args)); @@ -94,6 +98,17 @@ public class Designer extends BaseDesigner { // 预启动一下 StartServer.start(); } + + } + + private static SplashStrategy createSplash() { + // 这里可以开接口加载自定义启动画面 + if (OperatingSystem.isWindows()) { + return new SplashFx(); + } else if (OperatingSystem.isMacOS()) { + return new SplashMac(); + } + return new SplashFx(); } public Designer(String[] args) { diff --git a/designer-realize/src/com/fr/start/SplashContext.java b/designer-realize/src/com/fr/start/SplashContext.java new file mode 100644 index 0000000000..f8a1a3f673 --- /dev/null +++ b/designer-realize/src/com/fr/start/SplashContext.java @@ -0,0 +1,142 @@ +package com.fr.start; + +import com.fr.base.FRContext; +import com.fr.design.mainframe.bbs.BBSConstants; +import com.fr.general.Inter; +import com.fr.stable.StringUtils; +import com.fr.stable.module.ModuleAdapter; +import com.fr.stable.module.ModuleListener; + +import java.util.Locale; +import java.util.Random; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 启动动画策略 + * + * @author vito + * @date 2018/6/5 + */ +public class SplashContext { + + private static final SplashContext SPLASH_CONTEXT = new SplashContext(); + + private SplashStrategy splashStrategy; + + private String moduleID = ""; + private int loadingIndex = 0; + private String[] loading = new String[]{"..", "....", "......"}; + + private static final String GUEST = getRandomUser(); + + private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + + private ModuleListener listener; + + + public static SplashContext getInstance() { + return SPLASH_CONTEXT; + } + + private SplashContext() { + } + + /** + * 注册具体的启动动画 + */ + public void registerSplash(SplashStrategy splashStrategy) { + this.splashStrategy = splashStrategy; + } + + /** + * 注册监听 + */ + public ModuleListener getModuleListener() { + initListener(); + return listener; + } + + /** + * 展示启动动画 + */ + public void show() { + splashStrategy.show(); + } + + /** + * 隐藏启动动画 + */ + public void hide() { + splashStrategy.hide(); + // 窗口关闭后取消定时获取模块信息的timer + scheduler.shutdown(); + // 一次性 + splashStrategy = null; + } + + private void initListener() { + scheduler.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + loadingIndex++; + updateModuleLog(moduleID.isEmpty() ? StringUtils.EMPTY : moduleID + loading[loadingIndex % 3]); + } + }, 0, 300, TimeUnit.MILLISECONDS); + + listener = new ModuleAdapter() { + @Override + public void onStartBefore(String moduleName, String moduleI18nName) { + moduleID = moduleI18nName; + loadingIndex++; + updateModuleLog(moduleID.isEmpty() ? StringUtils.EMPTY : moduleID + loading[loadingIndex % 3]); + + } + }; + showThanks(); + } + + private void updateModuleLog(String text) { + splashStrategy.updateModuleLog(text); + } + + private void updateThanksLog(String text) { + splashStrategy.updateThanksLog(text); + } + + /** + * 获取随机感谢人员 + */ + private static String getRandomUser() { + String[] allGuest = BBSConstants.getAllGuest(); + if (allGuest.length == 0) { + return StringUtils.EMPTY; + } + int num = new Random().nextInt(allGuest.length); + return StringUtils.BLANK + allGuest[num]; + } + + /** + * 展示感谢信息 + */ + private void showThanks() { + if (shouldShowThanks()) { + updateThanksLog(Inter.getLocText("FR-Designer_Thanks-To") + GUEST); + } + } + + /** + * 是否显示鸣谢面板 + */ + private boolean shouldShowThanks() { + Locale[] hideLocales = {Locale.CHINA, Locale.TAIWAN}; + for (Locale loc : hideLocales) { + if (FRContext.getLocale().equals(loc)) { + return true; + } + } + return false; + } + +} diff --git a/designer-realize/src/com/fr/start/fx/FastGifImage.java b/designer-realize/src/com/fr/start/fx/FastGifImage.java new file mode 100644 index 0000000000..9049850c36 --- /dev/null +++ b/designer-realize/src/com/fr/start/fx/FastGifImage.java @@ -0,0 +1,210 @@ +package com.fr.start.fx; + +import com.sun.imageio.plugins.gif.GIFImageReader; +import com.sun.imageio.plugins.gif.GIFImageReaderSpi; +import com.sun.javafx.tk.ImageLoader; +import com.sun.javafx.tk.PlatformImage; +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.scene.image.WritableImage; +import javafx.util.Duration; + +import javax.imageio.stream.FileImageInputStream; +import java.io.File; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.regex.Pattern; + +/** + * 边加载边播放的gif加载器 + * + * @author daniel + */ +public class FastGifImage extends WritableImage { + private String url; + private int gifCount; + + public FastGifImage(String url, int w, int h) { + super(w, h); + this.url = validateUrl(url); + seekCount(); + initialize(); + } + + /** + * 给出gif帧数,加快加载速度 + * + * @param url gif url + * @param gifCount gif帧数 + * @param w 宽 + * @param h 高 + */ + public FastGifImage(String url, int gifCount, int w, int h) { + super(w, h); + this.url = validateUrl(url); + this.gifCount = gifCount; + initialize(); + } + + private void seekCount() { + try { + GIFImageReaderSpi spi = new GIFImageReaderSpi(); + GIFImageReader gifReader = (GIFImageReader) spi.createReaderInstance(); + gifReader.setInput(new FileImageInputStream(new File(new URI(url)))); + gifCount = gifReader.getNumImages(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static final Pattern URL_QUICKMATCH = Pattern.compile("^\\p{Alpha}[\\p{Alnum}+.-]*:.*$"); + + private static String validateUrl(final String url) { + if (url == null) { + throw new NullPointerException("URL must not be null"); + } + + if (url.trim().isEmpty()) { + throw new IllegalArgumentException("URL must not be empty"); + } + + try { + if (!URL_QUICKMATCH.matcher(url).matches()) { + final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + URL resource; + if (url.charAt(0) == '/') { + resource = contextClassLoader.getResource(url.substring(1)); + } else { + resource = contextClassLoader.getResource(url); + } + if (resource == null) { + throw new IllegalArgumentException("Invalid URL or resource not found"); + } + return resource.toString(); + } + // Use URL constructor for validation + return new URL(url).toString(); + } catch (final IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid URL" + e.getMessage()); + } catch (final MalformedURLException e) { + throw new IllegalArgumentException("Invalid URL" + e.getMessage()); + } + } + + + private void finishImage(Exception e) { + e.printStackTrace(); + } + + private void finishImage(ImageLoader loader) { + final Exception loadingException = loader.getException(); + if (loadingException != null) { + finishImage(loadingException); + return; + } + initializeAnimatedImage(loader); + } + + // Generates the animation Timeline for multiframe images. + private void initializeAnimatedImage(ImageLoader loader) { + + animation = new Animation(this, loader); + animation.start(); + } + + // Support for animated images. + private Animation animation; + + private static final class Animation { + final WeakReference imageRef; + final Timeline timeline; + final ImageLoader loader; + + public Animation(final FastGifImage image, final ImageLoader loader) { + this.loader = loader; + imageRef = new WeakReference(image); + timeline = new Timeline(); + timeline.setCycleCount(Timeline.INDEFINITE); + + final int frameCount = loader.getFrameCount(); + int duration = 0; + + for (int i = 0; i < frameCount; ++i) { + addKeyFrame(i, duration); + duration = duration + loader.getFrameDelay(i); + } + + // Note: we need one extra frame in the timeline to define how long + // the last frame is shown, the wrap around is "instantaneous" + addKeyFrame(0, duration); + } + + public void start() { + timeline.play(); + } + + public void stop() { + timeline.stop(); + } + + private void updateImage(final int frameIndex) { + final FastGifImage image = imageRef.get(); + if (image != null) { + image.setPlatformImagePropertyImpl( + loader.getFrame(frameIndex)); + } else { + timeline.stop(); + } + } + + private void addKeyFrame(final int index, final double duration) { + timeline.getKeyFrames().add( + new KeyFrame(Duration.millis(duration), + new EventHandler() { + @Override + public void handle(Event event) { + updateImage(index); + } + } + )); + } + } + + private static Method method; + + static { + try { + method = FastGifImage.class.getSuperclass().getSuperclass().getDeclaredMethod("platformImagePropertyImpl"); + method.setAccessible(true); + } catch (Exception e) { + + } + } + + private void setPlatformImagePropertyImpl(PlatformImage image) { + try { + Object o = method.invoke(this); + Method method = o.getClass().getDeclaredMethod("set", Object.class); + method.setAccessible(true); + method.invoke(o, image); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + + private void initialize() { + finishImage(new PrismImageLoader2(url, gifCount, (int) getRequestedWidth(), (int) getRequestedHeight(), isPreserveRatio(), isSmooth())); + } + +} diff --git a/designer-realize/src/com/fr/start/fx/PrismImageLoader2.java b/designer-realize/src/com/fr/start/fx/PrismImageLoader2.java new file mode 100644 index 0000000000..e943df011c --- /dev/null +++ b/designer-realize/src/com/fr/start/fx/PrismImageLoader2.java @@ -0,0 +1,211 @@ +package com.fr.start.fx; + +import com.sun.javafx.iio.ImageFrame; +import com.sun.javafx.iio.ImageLoadListener; +import com.sun.javafx.iio.ImageLoader; +import com.sun.javafx.iio.ImageMetadata; +import com.sun.javafx.iio.ImageStorageException; +import com.sun.javafx.iio.common.ImageTools; +import com.sun.javafx.iio.gif.GIFImageLoaderFactory; +import com.sun.javafx.tk.PlatformImage; +import com.sun.prism.Image; +import com.sun.prism.impl.PrismSettings; +import sun.util.logging.PlatformLogger; + +import java.io.IOException; +import java.io.InputStream; + +/** + * 边加载边播放的gif加载器 + * + * @author daniel + */ +class PrismImageLoader2 implements com.sun.javafx.tk.ImageLoader { + + private static PlatformLogger imageioLogger = null; + + private Image[] images; + private int[] delayTimes; + private int width; + private int height; + private int gifCount = 1; + private Exception exception; + + public PrismImageLoader2(final String url, int gifCount, final int width, final int height, + final boolean preserveRatio, final boolean smooth) { + this.gifCount = gifCount; + images = new Image[gifCount]; + delayTimes = new int[gifCount]; + this.width = width; + this.height = height; + new Thread() { + @Override + public void run() { + InputStream inputStream = null; + try { + inputStream = ImageTools.createInputStream(url); + loadAll(inputStream, width, height, preserveRatio, smooth); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + }.start(); + + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getFrameCount() { + return gifCount; + } + + @Override + public PlatformImage getFrame(int index) { + while (images[index] == null) { + synchronized (this) { + if (images[index] == null) { + try { + this.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + return images[index]; + } + + + @Override + public int getFrameDelay(int index) { +// while (images[0] == null) { +// synchronized (this) { +// if(images[0] == null) { +// try { +// this.wait(); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// } +// } +// return 0; +// } +// return delayTimes[0]; + // 直接使用第一帧的时间 + return 40; + } + + @Override + public int getLoopCount() { + return 0; + } + + @Override + public Exception getException() { + return exception; + } + + + private void loadAll(InputStream stream, int w, int h, + boolean preserveRatio, boolean smooth) { + ImageLoadListener listener = new PrismLoadListener(); + + try { + ImageLoader loader = null; + loader = GIFImageLoaderFactory.getInstance().createImageLoader(stream); + loader.addListener(listener); + + for (int i = 0; i < gifCount; i++) { + ImageFrame imageFrame = loader.load(i, w, h, preserveRatio, smooth); + images[i] = convert(imageFrame); + synchronized (this) { + this.notify(); + } + } + } catch (ImageStorageException e) { + handleException(e); + } catch (Exception e) { + handleException(e); + } + } + + private void handleException(final ImageStorageException isException) { + // unwrap ImageStorageException if possible + final Throwable exceptionCause = isException.getCause(); + if (exceptionCause instanceof Exception) { + handleException((Exception) exceptionCause); + } else { + handleException((Exception) isException); + } + } + + private void handleException(final Exception exception) { + if (PrismSettings.verbose) { + exception.printStackTrace(System.err); + } + this.exception = exception; + } + + private Image convert(ImageFrame imgFrames) { + ImageFrame frame = imgFrames; + Image image = Image.convertImageFrame(frame); + ImageMetadata metadata = frame.getMetadata(); + if (metadata != null) { + Integer delay = metadata.delayTime; + if (delay != null) { + delayTimes[0] = delay.intValue(); + } + } + return image; + } + + /** + * Returns the PlatformLogger for logging imageio-related activities. + */ + private static synchronized PlatformLogger getImageioLogger() { + if (imageioLogger == null) { + imageioLogger = PlatformLogger.getLogger("imageio"); + } + + return imageioLogger; + } + + private class PrismLoadListener implements ImageLoadListener { + @Override + public void imageLoadWarning(ImageLoader loader, String message) { + getImageioLogger().warning(message); + } + + @Override + public void imageLoadProgress(ImageLoader loader, + float percentageComplete) { + // progress only matters when backgroundLoading=true, but + // currently we are relying on AbstractRemoteResource for tracking + // progress of the InputStream, so there's no need to implement + // this for now; eventually though we might want to consider + // moving away from AbstractRemoteResource and instead use + // the built-in support for progress in the javafx-iio library... + } + + @Override + public void imageLoadMetaData(ImageLoader loader, ImageMetadata metadata) { + // We currently have no need to listen for ImageMetadata ready. + } + } + +} diff --git a/designer-realize/src/com/fr/start/fx/SplashFx.java b/designer-realize/src/com/fr/start/fx/SplashFx.java new file mode 100644 index 0000000000..34990509d0 --- /dev/null +++ b/designer-realize/src/com/fr/start/fx/SplashFx.java @@ -0,0 +1,49 @@ +package com.fr.start.fx; + +import com.fr.start.SplashStrategy; +import javafx.application.Application; +import javafx.application.Platform; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * JavaFx方式启动启动动画。这种方式在mac下与 + * swing一起启动会会出现线程死锁,jvm等问题, + * 所以这个方式仅用于windows上。 + * + * @author vito + * @date 2018/6/4 + * @see com.fr.start.jni.SplashMac + */ +public class SplashFx implements SplashStrategy { + + private SplashFxWindow test; + private static final ExecutorService SERVICE = Executors.newSingleThreadExecutor(); + + @Override + public void show() { + SERVICE.execute(new Runnable() { + @Override + public void run() { + Application.launch(SplashFxWindow.class); + } + }); + test = SplashFxWindow.waitForStartUpTest(); + } + + @Override + public void hide() { + Platform.exit(); + } + + @Override + public void updateModuleLog(String text) { + test.updateModuleInfo(text); + } + + @Override + public void updateThanksLog(String text) { + test.updateThanks(text); + } +} diff --git a/designer-realize/src/com/fr/start/fx/SplashFxWindow.java b/designer-realize/src/com/fr/start/fx/SplashFxWindow.java new file mode 100644 index 0000000000..0685943d94 --- /dev/null +++ b/designer-realize/src/com/fr/start/fx/SplashFxWindow.java @@ -0,0 +1,133 @@ +package com.fr.start.fx; + +import com.bulenkov.iconloader.util.JBUI; +import com.fr.base.FRContext; +import com.fr.stable.OperatingSystem; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.AnchorPane; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import javafx.stage.Stage; +import javafx.stage.StageStyle; + +import java.util.concurrent.CountDownLatch; + + +/** + * JavaFx启动动画窗口 + * + * @author vito + */ +public class SplashFxWindow extends Application { + + private static float JBUI_INIT_SCALE = JBUI.scale(1f); + + private static final String ARIAL_FONT_NAME = "Arial"; + private static final String PF_FONT_NAME = "PingFang"; + private static final String YAHEI_FONT_NAME = "Microsoft YaHei"; + private static final int MODULE_INFO_LEFT_MARGIN = 36; + private static final int MODULE_INFO_BOTTOM_MARGIN = 28; + private static final int THINKS_BOTTOM_RIGHT = 35; + private static final int THINKS_BOTTOM_MARGIN = 27; + private static final int WINDOW_WIDTH = 640; + private static final int WINDOW_HEIGHT = 360; + private static final int FONT = 12; + private static final String THINKS_COLOR = "#82b1ce"; + + private static final CountDownLatch LATCH = new CountDownLatch(1); + private static SplashFxWindow app = null; + + private Text moduleInfo; + private Text thanks; + + private static int uiScale(int i) { + return (int) (i * JBUI_INIT_SCALE); + } + + /** + * 获取当前运行实例。黑科技 + * + * @return 运行实例 + */ + public static SplashFxWindow waitForStartUpTest() { + try { + LATCH.await(); + } catch (InterruptedException e) { + FRContext.getLogger().error(e.getMessage(), e); + } + return app; + } + + private static void setApp(SplashFxWindow window) { + app = window; + LATCH.countDown(); + } + + public SplashFxWindow() { + setApp(this); + } + + @Override + public void start(Stage primaryStage) { + AnchorPane root = new AnchorPane(); + primaryStage.initStyle(StageStyle.TRANSPARENT); + long t = System.currentTimeMillis(); + Image image = new FastGifImage("com/fr/base/images/oem/splash_10.gif", 254, WINDOW_WIDTH, WINDOW_HEIGHT); + + ImageView gif = new ImageView(image); + + AnchorPane.setBottomAnchor(gif, 0d); + AnchorPane.setTopAnchor(gif, 0d); + AnchorPane.setLeftAnchor(gif, 0d); + AnchorPane.setRightAnchor(gif, 0d); + Font font; + if (OperatingSystem.isWindows()) { + font = new Font(YAHEI_FONT_NAME, uiScale(FONT)); + } else if (OperatingSystem.isMacOS()) { + font = new Font(PF_FONT_NAME, uiScale(FONT)); + } else { + font = new Font(ARIAL_FONT_NAME, uiScale(FONT)); + } + + moduleInfo = new Text(); + moduleInfo.setFont(font); + moduleInfo.setFill(Color.WHITE); + AnchorPane.setLeftAnchor(moduleInfo, (double) uiScale(MODULE_INFO_LEFT_MARGIN)); + AnchorPane.setBottomAnchor(moduleInfo, (double) uiScale(MODULE_INFO_BOTTOM_MARGIN)); + thanks = new Text(); + thanks.setFont(font); + thanks.setFill(Color.valueOf(THINKS_COLOR)); + AnchorPane.setRightAnchor(thanks, (double) uiScale(THINKS_BOTTOM_RIGHT)); + AnchorPane.setBottomAnchor(thanks, (double) uiScale(THINKS_BOTTOM_MARGIN)); + + root.getChildren().add(gif); + root.getChildren().add(moduleInfo); + root.getChildren().add(thanks); + + Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT, null); + primaryStage.setScene(scene); + primaryStage.show(); + } + + /** + * 更新模块信息 + * + * @param s 文字 + */ + public void updateModuleInfo(String s) { + moduleInfo.setText(s); + } + + /** + * 更新欢迎信息 + * + * @param s 文字 + */ + public void updateThanks(String s) { + thanks.setText(s); + } +} diff --git a/designer-realize/src/com/fr/start/jni/SplashJNI.java b/designer-realize/src/com/fr/start/jni/SplashJNI.java new file mode 100644 index 0000000000..e71a44ca21 --- /dev/null +++ b/designer-realize/src/com/fr/start/jni/SplashJNI.java @@ -0,0 +1,69 @@ +package com.fr.start.jni; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + +/** + * Splash JNI调用。jni类改名或者移包之后 + * 必须重新编译动态库 + * + * @author vito + * @date 2018/6/4 + */ +public class SplashJNI { + + static { + try { + System.setProperty("java.library.path", "."); + System.loadLibrary("splash"); + } catch (UnsatisfiedLinkError e) { + loadLibraryFromJar("/com/fr/start/jni/splash.dylib"); + } + } + + /** + * 显示启动动画窗口 + */ + public native void show(String path); + + /** + * 隐藏启动动画窗口 + */ + public native void hide(); + + /** + * 设置模块加载信息 + */ + public native void updateModuleLog(String text); + + /** + * 设置感谢文字 + */ + public native void updateThanksLog(String text); + + /** + * 从jar中加载动态库 + * + * @param path 路径,如/com/a/b + * @throws UnsatisfiedLinkError 没有找到合适的动态库 + */ + private static void loadLibraryFromJar(String path) throws UnsatisfiedLinkError { + try (InputStream inputStream = SplashJNI.class.getResourceAsStream(path)) { + File tempLib = File.createTempFile(path, ""); + + byte[] buffer = new byte[1024]; + int read = -1; + + try (FileOutputStream fileOutputStream = new FileOutputStream(tempLib)) { + while ((read = inputStream.read(buffer)) != -1) { + fileOutputStream.write(buffer, 0, read); + } + } + + System.load(tempLib.getAbsolutePath()); + } catch (Exception e) { + throw new UnsatisfiedLinkError("Unable to open " + path + " from jar file."); + } + } +} diff --git a/designer-realize/src/com/fr/start/jni/SplashMac.java b/designer-realize/src/com/fr/start/jni/SplashMac.java new file mode 100644 index 0000000000..16ff8fc502 --- /dev/null +++ b/designer-realize/src/com/fr/start/jni/SplashMac.java @@ -0,0 +1,87 @@ +package com.fr.start.jni; + +import com.fr.stable.ProductConstants; +import com.fr.stable.StableUtils; +import com.fr.start.SplashContext; +import com.fr.start.SplashStrategy; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * mac上使用jni方式绘制gif。不使用javafx有两个原因: + * 1.mac上javafx和swing同时启动会导致卡死; + * 2.platform.exit会导致设计器崩溃 + * + * @author vito + * @see com.fr.start.fx.SplashFx + */ +public class SplashMac implements SplashStrategy { + + private static final String SPLASH_CACHE_NAME = "splash_10.gif"; + private static final String SPLASH_PATH = "/com/fr/base/images/oem/splash_10.gif"; + + private SplashJNI jni; + + public SplashMac() { + jni = new SplashJNI(); + } + + /** + * 将jar中的资源拷贝到缓存文件夹 + * + * @return 路径 + * @throws IOException 拷贝失败 + */ + private static String loadResFromJar() throws UnsatisfiedLinkError { + File tempLib = null; + try (InputStream inputStream = SplashContext.class.getResourceAsStream(SplashMac.SPLASH_PATH)) { + tempLib = new File(StableUtils.pathJoin(ProductConstants.getEnvHome(), SPLASH_CACHE_NAME)); + byte[] buffer = new byte[1024]; + int read = -1; + try (FileOutputStream fileOutputStream = new FileOutputStream(tempLib)) { + while ((read = inputStream.read(buffer)) != -1) { + fileOutputStream.write(buffer, 0, read); + } + } + return tempLib.getAbsolutePath(); + } catch (IOException e) { + tempLib.deleteOnExit(); + throw new UnsatisfiedLinkError("Unable to open " + SplashMac.SPLASH_PATH + " from jar file."); + } + } + + @Override + public void show() { + if (jni != null) { + File splash = new File(StableUtils.pathJoin(ProductConstants.getEnvHome(), SPLASH_CACHE_NAME)); + String path = splash.exists() ? splash.getAbsolutePath() : loadResFromJar(); + jni.show(path); + } + } + + @Override + public void hide() { + if (jni != null) { + jni.hide(); + jni = null; + } + } + + @Override + public void updateModuleLog(String text) { + if (jni != null) { + jni.updateModuleLog(text); + } + + } + + @Override + public void updateThanksLog(String text) { + if (jni != null) { + jni.updateThanksLog(text); + } + } +} diff --git a/designer-realize/src/com/fr/start/jni/splash.dylib b/designer-realize/src/com/fr/start/jni/splash.dylib new file mode 100755 index 0000000000000000000000000000000000000000..3e19cd20e55488220c6ea4559390f4cc112921b9 GIT binary patch literal 32320 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 aE&{m-{+W!Kk_o_(% literal 0 HcmV?d00001 diff --git a/designer-realize/src/com/fr/start/module/DesignerStartup.java b/designer-realize/src/com/fr/start/module/DesignerStartup.java index dd2b09c538..3f150e043d 100644 --- a/designer-realize/src/com/fr/start/module/DesignerStartup.java +++ b/designer-realize/src/com/fr/start/module/DesignerStartup.java @@ -5,8 +5,7 @@ import com.fr.stable.CoreActivator; import com.fr.stable.module.ModuleListener; import com.fr.start.Designer; import com.fr.start.EnvSwitcher; -import com.fr.start.ReportSplashPane; -import com.fr.start.SplashWindow; +import com.fr.start.SplashContext; import com.fr.startup.activators.BasicActivator; /** @@ -16,12 +15,11 @@ public class DesignerStartup extends Activator { @Override public void start() { - startSub(PreStartActivator.class); //启动基础部分 startSub(BasicActivator.class); - //启动画面 - SplashWindow splashWindow = createSplashWindow(); + //启动画面注册监听,必须在初始化国际化之后注册监听 + registerSplashListener(); String[] args = getModule().upFindSingleton(StartupArgs.class).get(); Designer designer = new Designer(args); //启动env @@ -33,17 +31,15 @@ public class DesignerStartup extends Activator { //启动设计器界面 designer.show(args); //启动画面结束 - splashWindow.setVisible(false); - splashWindow.dispose(); + SplashContext.getInstance().hide(); startSub(StartFinishActivator.class); } - private SplashWindow createSplashWindow() { - - ReportSplashPane reportSplashPane = new ReportSplashPane(); - SplashWindow splashWindow = new SplashWindow(reportSplashPane); - getModule().setSingleton(ModuleListener.class, reportSplashPane.getModuleListener()); - return splashWindow; + /** + * 注册启动动画监听器 + */ + private void registerSplashListener() { + getModule().setSingleton(ModuleListener.class, SplashContext.getInstance().getModuleListener()); } @Override