JavaFX学习之样例一

JavaFX学习之样例1
  代码老外的,学习学习

该代码主要功能是通过JavaFX的concurrent实现异步的数据库操作。
package h2app;

import java.sql.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.*;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.*;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class H2Tasks extends Application {
	private static final Logger logger = Logger.getLogger(H2Tasks.class
			.getName());
	private static final String[] SAMPLE_NAME_DATA = { "John", "Jill", "Jack",
			"Jerry" };

	public static void main(String[] args) {
		launch(args);
	}

	// executes database operations concurrent to JavaFX operations.
	private ExecutorService databaseExecutor;

	// the future's data will be available once the database setup has been
	// complete.
	private Future databaseSetupFuture;

	// initialize the program.
	// setting the database executor thread pool size to 1 ensures
	// only one database command is executed at any one time.
	@Override
	public void init() throws Exception {
		databaseExecutor = Executors.newFixedThreadPool(1,
				new DatabaseThreadFactory());

		// run the database setup in parallel to the JavaFX application setup.
		DBSetupTask setup = new DBSetupTask();
		databaseSetupFuture = databaseExecutor.submit(setup);
	}

	// shutdown the program.
	@Override
	public void stop() throws Exception {
		databaseExecutor.shutdown();
		if (!databaseExecutor.awaitTermination(3, TimeUnit.SECONDS)) {
			logger.info("Database execution thread timed out after 3 seconds rather than shutting down cleanly.");
		}
	}

	// start showing the UI.
	@Override
	public void start(Stage stage) throws InterruptedException,
			ExecutionException {
		// wait for the database setup to complete cleanly before showing any
		// UI.
		// a real app might use a preloader or show a splash screen if this
		// was to take a long time rather than just pausing the JavaFX
		// application thread.
		databaseSetupFuture.get();

		final ListView<String> nameView = new ListView();
		final ProgressIndicator databaseActivityIndicator = new ProgressIndicator();
		databaseActivityIndicator.setVisible(false);

		final Button fetchNames = new Button("Fetch names from the database");
		fetchNames.setOnAction(new EventHandler<ActionEvent>() {
			@Override
			public void handle(ActionEvent event) {
				fetchNamesFromDatabaseToListView(fetchNames,
						databaseActivityIndicator, nameView);
			}
		});

		final Button clearNameList = new Button("Clear the name list");
		clearNameList.setOnAction(new EventHandler<ActionEvent>() {
			@Override
			public void handle(ActionEvent event) {
				nameView.getItems().clear();
			}
		});

		VBox layout = new VBox(10);
		layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 15;");
		layout.getChildren().setAll(
				HBoxBuilder
						.create()
						.spacing(10)
						.children(fetchNames, clearNameList,
								databaseActivityIndicator).build(), nameView);
		layout.setPrefHeight(200);

		stage.setScene(new Scene(layout));
		stage.show();
	}

	private void fetchNamesFromDatabaseToListView(final Button triggerButton,
			final ProgressIndicator databaseActivityIndicator,
			final ListView listView) {
		final FetchNamesTask fetchNamesTask = new FetchNamesTask();
		triggerButton.setDisable(true);
		databaseActivityIndicator.setVisible(true);
		databaseActivityIndicator.progressProperty().bind(
				fetchNamesTask.progressProperty());  //进度条绑定任务
		fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {   //task的成功执行事件
			@Override
			public void handle(WorkerStateEvent t) {
				listView.setItems(fetchNamesTask.getValue());            //task.getValue()获取task.call()方法返回的值
			}
		});
		fetchNamesTask.runningProperty().addListener(
				new ChangeListener<Boolean>() {
					@Override
					public void changed(
							ObservableValue<? extends Boolean> observable,
							Boolean wasRunning, Boolean isRunning) {
						if (!isRunning) {
							triggerButton.setDisable(false);
							databaseActivityIndicator.setVisible(false);
						}
					};
				});
		databaseExecutor.submit(fetchNamesTask);
	}

	abstract class DBTask<T> extends Task<T> {
		DBTask() {
			setOnFailed(new EventHandler<WorkerStateEvent>() {
				@Override
				public void handle(WorkerStateEvent t) {
					logger.log(Level.SEVERE, null, getException());
				}
			});
		}
	}

	class FetchNamesTask extends DBTask<ObservableList<String>> {
		@Override
		protected ObservableList<String> call() throws Exception {
			// artificially pause for a while to simulate a long running
			// database connection.
			Thread.sleep(1000);

			Connection con = getConnection();
			return fetchNames(con);

		}

		private ObservableList<String> fetchNames(Connection con)
				throws SQLException {
			logger.info("Fetching names from database");
			ObservableList<String> names = FXCollections.observableArrayList();

			Statement st = con.createStatement();
			ResultSet rs = st.executeQuery("select name from employee");
			while (rs.next()) {
				names.add(rs.getString("name"));
			}

			logger.info("Found " + names.size() + " names");

			return names;
		}
	}

	class DBSetupTask<Void> extends DBTask {
		@Override
		protected Void call() throws Exception {

			Connection con = getConnection();
			if (!schemaExists(con)) {
				createSchema(con);
				populateDatabase(con);
			}

			return null;
		}

		private boolean schemaExists(Connection con) {
			logger.info("Checking for Schema existence");
			try {
				Statement st = con.createStatement();
				st.executeQuery("select count(*) from employee");
				logger.info("Schema exists");
			} catch (SQLException ex) {
				logger.info("Existing DB not found will create a new one");
				return false;
			}

			return true;
		}

		private void createSchema(Connection con) throws SQLException {
			logger.info("Creating schema");
			Statement st = con.createStatement();
			String table = "create table employee(id integer, name varchar(64))";
			st.executeUpdate(table);
			logger.info("Created schema");
		}

		private void populateDatabase(Connection con) throws SQLException {
			logger.info("Populating database");
			Statement st = con.createStatement();
			for (String name : SAMPLE_NAME_DATA) {
				st.executeUpdate("insert into employee values(1,'" + name
						+ "')");
			}
			logger.info("Populated database");
		}
	}

	private Connection getConnection() throws ClassNotFoundException,
			SQLException {
		logger.info("Getting a database connection");
		Class.forName("com.mysql.jdbc.Driver");
		return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&noAccessToProcedureBodies=true", "root", "root");
	}

	static class DatabaseThreadFactory implements ThreadFactory {
		static final AtomicInteger poolNumber = new AtomicInteger(1);

		@Override
		public Thread newThread(Runnable runnable) {
			Thread thread = new Thread(runnable, "Database-Connection-"
					+ poolNumber.getAndIncrement() + "-thread");
			thread.setDaemon(true);

			return thread;
		}
	}
}


代码里是通过java的多线程执行task,并不是通过javafx的service。代码很简单通过二个task,一个初始化数据库,一个查询数据库。结果返回显示。


		
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {   //task的成功执行事件
			@Override
			public void handle(WorkerStateEvent t) {
				listView.setItems(fetchNamesTask.getValue());            //task.getValue()获取task.call()方法返回的值
			}
		});

没想到过task.getValue()既是call的返回值,学习到了。