JavaFX+Jfoenix 学习笔记(七)--多线程、延迟加载

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_26486347/article/details/96620969

前言

JavaFX是单线程编程,怎么个意思呢?意思就是所有对界面的操作都会交给唯一的线程Application Thread去处理,比如你要修改按钮一的名称,同时又要添加个按钮二,然后还要干点别的,那么不好意思,操作界面的线程只有一个,大家排队一个一个来。

1、我为什么要在JavaFX中使用多线程编程?

答:其实我主要是为了解决很多时候的卡顿问题(这个本应该说是用延迟加载来解决,但延迟加载离不开多线程思想)。

比如:打开一个页面同时加载数据会很卡(因为加载数据紧随页面初始化的动作)这样给人的体验很不友好,怎么解决呢,网上很多人都会用javafx.concurrent包下的Task来实现,这是一种解决方案。

但开始时我说了我主要是为了解决卡顿问题,那么既然卡顿是因为某两个动作连续执行造成的,那么我们可以将后面的动作延迟一下,同样可以解决卡顿的问题,比如我们先让页面初始化,接着延迟1秒来加载数据,所以时间轴动画也可以用来实现某些场合下的多线程编程效果。

当然多线程还能解决很多问题,比如一个很耗时的网络请求等等。

2、为什么把延迟加载跟多线程放一起说

答:因为延迟加载离不开多线程的支持。

3、实例-1:使用Platform.runLater(...)实现多线程

javafx.application包下的亲儿子,原理是把你要执行的动作添加到类似队列中,等Application Thread线程空闲时处理,说到底还是主线程(Application Thread)处理的,你的那个线程只负责给等待队列扔了一下。

下面用两个按钮来演示多线程(其实下面这种情况没必要用多线程,只是为了举例),一个演示错误用法,一个演示正确用法。

package zkh.javafx.learn.concurrent;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 * 多线程
 */
//继承javafx.application.Application是JavaFX的开始
public class Concurrent1 extends Application {
	
	/**
	 * Stage:就是你能看到的整个软件界面(窗口)
	 * Scene:就是除了窗口最上面有最大、最小化及关闭按钮那一行及窗口边框外其它的区域(场景)
	 * 场景(Scene)是一个窗口(Stage)必不可少的
	 */
	@Override
	public void start(Stage stage) throws Exception {
		VBox vBox = new VBox(); vBox.setAlignment(Pos.CENTER);

		final Button button1 = new Button("我是按钮1");
		button1.setOnAction(new EventHandler<ActionEvent>() {
			public void handle(ActionEvent event) {
				Thread button1thread = new Thread() {
                    @Override
                    public void run() {
                    	// 当单击这个按钮时会报异常,但如果你切换下窗口按钮名称可能已经改变,但当时直接就会报错啊,所以是错误用法
                    	// Exception in thread "button1thread" java.lang.IllegalStateException: Not on FX application thread; currentThread = button1thread
                        button1.setText("我现在是按钮100");
                    }
                };
                button1thread.setName("button1thread");
                button1thread.start();
			}
		});
		
		final Button button2 = new Button("我是按钮2");
		button2.setOnAction(new EventHandler<ActionEvent>() {
			public void handle(ActionEvent event) {
				Thread button2thread = new Thread() {
                    @Override
                    public void run() {
                    	// 这个是正确的用法
                    	Platform.runLater(new Runnable() {
							public void run() {
								button2.setText("我现在是按钮200");
							}
						});
                    }
                };
                button2thread.setName("button2thread");
                button2thread.start();
			}
		});
		vBox.getChildren().addAll(button1, button2);
		
		
		// 1、初始化一个场景
		Scene scene = new Scene(vBox, 800, 600);
		// 2、将场景放入窗口
		stage.setScene(scene);
		// 3、打开窗口
		stage.show();
	}
	
	public static void main( String[] args ){
		// 启动软件
		Application.launch(args);
    }

}

效果动图:

4、实例-2:使用javafx.concurrent包下的Task实现多线程

网上都说Task能实现多线程编程,按我的想法就是直接在Task的call方法里写进修改按钮名称的代码(button1.setText("我的新名字是100");)然而报错了,所以曲线救国在Task的call方法里更新Task的属性message(用这个存放我按钮的新名字),然后将它绑定到按钮名称text属性,就这么实现了”多线程“(可能是吧,这操作不是我想象)。

package zkh.javafx.learn.concurrent;

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 * 多线程
 */
//继承javafx.application.Application是JavaFX的开始
public class Concurrent2 extends Application {
	
	/**
	 * Stage:就是你能看到的整个软件界面(窗口)
	 * Scene:就是除了窗口最上面有最大、最小化及关闭按钮那一行及窗口边框外其它的区域(场景)
	 * 场景(Scene)是一个窗口(Stage)必不可少的
	 */
	@Override
	public void start(Stage stage) throws Exception {
		VBox vBox = new VBox(); vBox.setAlignment(Pos.CENTER);
		final Button button1 = new Button("我是按钮1");
		button1.setOnAction(new EventHandler<ActionEvent>() {
			public void handle(ActionEvent event) {
				 Task<Void> task = new Task<Void>(){
			            @Override
			            protected Void call() throws Exception {
			            	// 我这样用的时候报错了
//			            	button1.setText("我的新名字是100");
			            	
			            	// (1)正确用法,更新message属性值
			            	updateMessage("我的新名字是100");  
			                return null;
			            }
			     };
			     // (2)将task的message属性值绑定到button1的text(名称)属性值
			     button1.textProperty().bind(task.messageProperty());
				 // 创建线程并开启
				 new Thread(task).start();
			}
		});
		vBox.getChildren().addAll(button1);
		
		
		// 1、初始化一个场景
		Scene scene = new Scene(vBox, 800, 600);
		// 2、将场景放入窗口
		stage.setScene(scene);
		// 3、打开窗口
		stage.show();
	}

	public static void main( String[] args ){
		// 启动软件
		Application.launch(args);
    }

}

效果动图:

5、实例-3 利用Timer+Platform.runLater(...)来实现延迟加载

延迟2000毫秒将button1的名字改为"我的新名字是100"

package zkh.javafx.learn.concurrent;

import java.util.Timer;
import java.util.TimerTask;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 * 多线程
 */
//继承javafx.application.Application是JavaFX的开始
public class Concurrent3 extends Application {
	
	/**
	 * Stage:就是你能看到的整个软件界面(窗口)
	 * Scene:就是除了窗口最上面有最大、最小化及关闭按钮那一行及窗口边框外其它的区域(场景)
	 * 场景(Scene)是一个窗口(Stage)必不可少的
	 */
	@Override
	public void start(Stage stage) throws Exception {
		VBox vBox = new VBox(); vBox.setAlignment(Pos.CENTER);
		final Button button1 = new Button("我是按钮1");
		button1.setOnAction(new EventHandler<ActionEvent>() {
			public void handle(ActionEvent event) {
				// 延迟2000毫秒将button1的名字改为"我的新名字是100"
				new Timer().schedule(new TimerTask() {
			        public void run() {
			        	Platform.runLater(new Runnable() {
							public void run() {
								button1.setText("我的新名字是100");
							}			        		
			        	});
			        }
				}, 2000);
			}
		});
		vBox.getChildren().addAll(button1);
		
		
		// 1、初始化一个场景
		Scene scene = new Scene(vBox, 800, 600);
		// 2、将场景放入窗口
		stage.setScene(scene);
		// 3、打开窗口
		stage.show();
	}

	public static void main( String[] args ){
		// 启动软件
		Application.launch(args);
    }

}

效果动图:

6、实例-4 利用时间轴动画(Timeline)来实现延迟加载

package zkh.javafx.learn.concurrent;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * 多线程
 */
//继承javafx.application.Application是JavaFX的开始
public class Concurrent4 extends Application {
	
	/**
	 * Stage:就是你能看到的整个软件界面(窗口)
	 * Scene:就是除了窗口最上面有最大、最小化及关闭按钮那一行及窗口边框外其它的区域(场景)
	 * 场景(Scene)是一个窗口(Stage)必不可少的
	 */
	@Override
	public void start(Stage stage) throws Exception {
		VBox vBox = new VBox(); vBox.setAlignment(Pos.CENTER);
		final Button button1 = new Button("我是按钮1");
		button1.setOnAction(new EventHandler<ActionEvent>() {
			public void handle(ActionEvent event) {
				// (我喜欢用这个方式来延迟)延迟2000毫秒将button1的名字改为"我的新名字是100"
				Timeline timeline = new Timeline();
				timeline.setCycleCount(1);
				timeline.setAutoReverse(false);
		    	KeyFrame keyFrame = new KeyFrame(Duration.millis(2000), new EventHandler<ActionEvent>() {
		            public void handle(ActionEvent t) {	                        
		            	button1.setText("我的新名字是100");
		            }
		        });
		    	timeline.getKeyFrames().clear();
		    	timeline.getKeyFrames().add(keyFrame);
		    	timeline.play();
			}
		});
		vBox.getChildren().addAll(button1);
		
		
		// 1、初始化一个场景
		Scene scene = new Scene(vBox, 800, 600);
		// 2、将场景放入窗口
		stage.setScene(scene);
		// 3、打开窗口
		stage.show();
	}

	public static void main( String[] args ){
		// 启动软件
		Application.launch(args);
    }

}

效果动图(跟实例-3效果一致):

7、源代码(不推荐,文章基本都有了)

JavaFX+Jfoenix 学习笔记(七)--多线程、延迟加载源码

好了,多看注释!

猜你喜欢

转载自blog.csdn.net/qq_26486347/article/details/96620969