vito
7 years ago
11 changed files with 960 additions and 14 deletions
@ -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); |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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<FastGifImage> imageRef; |
||||||
|
final Timeline timeline; |
||||||
|
final ImageLoader loader; |
||||||
|
|
||||||
|
public Animation(final FastGifImage image, final ImageLoader loader) { |
||||||
|
this.loader = loader; |
||||||
|
imageRef = new WeakReference<FastGifImage>(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())); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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.
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Binary file not shown.
Loading…
Reference in new issue