Main 변경 없이 바로 Scene Builder로




Boder Center 위에 Pane(400, 400)을 하나 올려줍니다.


그 위에


PieChart : Code : fx:id - pieChart


Button : Code : OnAction - javaSel


Button : Code : OnAction - csSel


Button : Code : OnAction - pySel


Button : Code : OnAction - jsSel


Controller Class 설정



package app;

import java.net.URL;
import java.util.ResourceBundle;

import javax.swing.JScrollBar;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.PieChart.Data;

public class MainController implements Initializable{
	@FXML private PieChart pieChart;
	// 개별데이터 -> 리스트 -> 파이차트에 set
	int java = 10;
	int cs = 10;
	int py = 10;
	int js = 10;
	
	ObservableList<Data> list = null;
	
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		System.out.println("FXML 완");
		list = FXCollections.observableArrayList();
		list.add(new PieChart.Data("Java",  java));
		list.add(new PieChart.Data("C#",  cs));
		list.add(new PieChart.Data("Python",  py));
		list.add(new PieChart.Data("Java Script",  js));
		pieChart.setTitle("선호 언어"); // 타이틀
		pieChart.setLabelsVisible(true); // 라벨 여부
		pieChart.setData(list); // 데이터 적용
	}
	
	public void javaSel() { // 버튼을 누를 때 파이 값 증가
		java++;
		list.set(0, new Data("Java", java));
	}
	
	public void csSel() {
		cs++;
		list.set(1, new Data("C#", cs));
	}
	
	public void pySel() {
		py++;
		list.set(2, new Data("Python", py));
	}
	
	public void jsSel() {
		js++;
		list.set(3, new Data("Java Script", js));
	}
	
}



이번에는 Main을 Parent로 바꾼다.


Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));


primaryStage.setTitle("Progress 예제");




Border를 지워주고 Pane을 추가

Pane(400, 400) 위에

Label : Code : fx:id - result

ProgressBar : Code : fx:id - bar

ProgressIndicator : Code : fx:id - pi

Button(+) : Code : OnAction - plus

Button(auto) : Code : OnAction - auto

Button(-) : Code : OnAction - minus

Controller Class 설정을 마치고 코딩

다음 코드는 버튼을 누를 경우 +, - 가 되고 그 값을 Label에 띄우고 Progress bar, Indicator에도 띄운다.

또한 auto에 Thread를 적용하여 자동으로 증가하게끔 코딩





package app;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;

public class MainController implements Initializable{

	@FXML private Label result;
	@FXML private ProgressBar bar;
	@FXML private ProgressIndicator pi;
	private int percent = 0;
	
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		System.out.println("FXML 완");
	}
	
	public void plus() {
		if(percent<100) {
			percent++;
		}else {
			percent = 0;
		}
		print(percent);
	}
	
	public void minus() {
		if(percent > 0) {
			percent--;
		}else {
			percent = 100;
		}
		print(percent);
	}
	
	public void print(int num) {
		result.setText(num + "%");
		bar.setProgress(num*0.01); // 1을 100%로 잡으므로 0.01을 곱한다.
		pi.setProgress(num*0.01);
	}
	
	public void auto() {
		// 스레드로 pi의 % 채우기
		Thread th = new Thread() {
			@Override
			public void run() {
				// 방법 1 (충돌 발생)
//				while(true) { // 스레드를 임의로 만들면 작동은 되지만 예외를 발생
//								// FX등록 스레드만 쓰라는것
//					try { // 코드 안의 값들은 상관없는데 UI도 하나의 스레드로 서로 간섭이 일어나 충돌
//						plus(); // 코드 안의 값들은 상관없는데 UI도 하나의 스레드로 서로 간섭이 일어나 충돌
//						Thread.sleep(100);
//					} catch (InterruptedException e) {
//						e.printStackTrace();
//					}
//				}
				
				// 방법 2, 1의 해결법
				while(true) {
					Platform.runLater(()->{ // 자바 8버젼부터 사용가능, UI 충돌이 예상되는 지점에 사용
						plus();
					});
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		};
		th.start();
		
	}
}


'JavaFX > 01.Node 추가' 카테고리의 다른 글

[JAVA FX]01-12.Line Chart, Random  (0) 2018.09.11
[JAVA FX]01-11.PieChart  (0) 2018.09.11
[JAVA FX]01-09.속성 감시, 속성 바인딩  (0) 2018.09.10
[JAVA FX]01-08.Menu 바 추가  (0) 2018.09.10
[JAVA FX]01-07.TreeView  (0) 2018.09.10

- 1 -


Main은 따로 변경할 사항이 없다.



BorderPane - Center에 Pane 추가


Pane위에 위와같이


Label (fx:id - label)


Slider (fx:id - slider)


TextArea (fx:id - area1)


TextArea (fx:id - area2)



Controller class 설정, 저장 후 코드


슬라이더의 콩을 움직일 때 글자의 크기도 바로바로 늘어나고


텍스트 area 1, 2의 글자가 동시에 입력되게끔



package app;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextArea;
import javafx.scene.text.Font;

public class MainController implements Initializable{

	@FXML private Label label;
	@FXML private Slider slider;
	@FXML private TextArea area1;
	@FXML private TextArea area2;
	
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		System.out.println("FXML Load Complete");
		// 실시간 감시, 값을 확인
		// 슬라이더 변경시 값을 확인
		// 아래는 그대로 쓰되 대부분 자동생성
		slider.setValue(12); // 글자 기본값 12랑 맞춰주기
		slider.valueProperty().addListener(new ChangeListener<Number>() {
		// 스스로를 감시하여 값이 변경 될 때마다 특정 이벤트 발생
			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				label.setFont(new Font(newValue.doubleValue()));
			}
		});
		/*
		 * 단방향 바인딩
		 * 대상1에 변경된 내용을 대상 2에 적용
		 * 대상2.xxxProperty().bind(대상1.XXXProperty))
		 * 양방향 바인딩
		 * 대상2. xxxProperty().bindBidrictional(대상1.xxxProperty)
		 */
		Bindings.bindBidirectional(area1.textProperty(), area2.textProperty());
	}
	
}





















- 2 -


Main의 Scene scene = new Scene(root,200,200); 부분에 숫자를 200, 200로 하여 창 크기를 줄이고





Border의 Center에 Pane을 올리고


Pane 위에 Slider와 Text Field를 올린다.


Slider - fx:id : slider


TextField - fx:id : field


그리고 Controller class를 설정 후 코딩


이 프로그램은 슬라이더를 움직이면 그에 알맞은 텍스트가 출력되는 프로그램


Bindings를 통해서 하나로 묶는것

package app;

import java.net.URL;
import java.text.NumberFormat;
import java.util.ResourceBundle;

import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;

public class MainController implements Initializable {

	@FXML private Slider slider;
	@FXML private TextField field;
	
	@Override
	public void initialize(URL arg0, ResourceBundle arg1) {
		System.out.println("FXML Load Complete");
		slider.setValue(50);
//		field.setText("50");
		field.setText(new Double(50).toString());
		Bindings.bindBidirectional(field.textProperty(), slider.valueProperty(), NumberFormat.getNumberInstance());
		// field, slider를 인자로 받음
	}
	
}








Menu바는 정말 간단한 작업


Main에는 건드릴게 따로 없고 Scene Builder에서 디자인과 Controller만 하면 된다.





BorderPane Top - MenuBar 추가 - Menu 포함 - MenuItem 포함


   - Menu 추가 - MenuItem 포함


    - MenuItem 추가


   - MenuItem 추가


   - SeparatorMenuItem 추가


   - MenuItem 추가 (Close로 명명)


   - Menu 포함 - MenuItem 포함


   - Menu 포함 - MenuItem 포함


Center - Pane 추가


추가는 Library에서 검색하여 넣은것. 포함은 기본값


처음 MenuBar을 추가하면 3가지 Menu가 들어있고


Library에서 Menu 항목 안에 Menu를 추가하면 위의 실행 이미지와 같이 하위 항목이 하나 더 생긴다.


이미지에서 Menu 옆에 흐른 글씨로 File과 같이 써있는데 더블 클릭하여 내용을 변경할 수 있다.


그 중 Close로 명명한 MenuItem의 Code 부분의 On Action을 closeApp으로  설정


Controller 클래스 등록 후 저장


그리고 등록한 클래스를 작성



package app; import javafx.application.Platform; public class MainController { // initialize는 따로 초기화할 것이 없으므로 안씀 public void closeApp() { Platform.exit(); // 앱 종료(main Thread) System.exit(0); // Jvm을 완전 종료 } }



위와같이 코드를 작성하고 실행하면 아까 디자인한 창이 나오는데


상단 메뉴바에서 Close를 선택하면 프로그램이 종료된다.

이번에는 BorderPane을 그대로 사용합니다.


BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("main.fxml"));


primaryStage.setTitle("Directories");




BorderPane의 Center 부분에 Pane을 추가하고 TreeView를 사진과 같이 배치


TreeView : Code, fx:id - treeView


Mouse - On Mouse Clicked - selectTree


Controller class 등록


이제 폴더 구조를 만들기 위한 폴더 아이콘 이미지를 올려 줍니다.


위 이미지를 우클릭 다른 이름으로 저장


그리고 img 패키지를 새로 만든 후 드래그 앤 드롭으로 복사하고





Controller class 등록 후 클래스를 작성합니다.



package app;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;

public class MainController implements Initializable{
	// 폴더 명을 받기 때문에 String
	@FXML private TreeView<String> treeView;
	// 프로젝트의 src폴더 절대경로
	// "img/icon.png" 맨 앞에 슬래시가 없으면 app.img 패키지, 없으면 app 아래까지 절대경로를 지정
	// "/img/icon.png" 맨 앞에 슬래시가 있으면 img 패키지
	Image img = new Image(getClass().getResourceAsStream("/img/icon.png"));
	
	@SuppressWarnings("unchecked")
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		System.out.println("FXML Load Complete");
		// 트리뷰의 구조를 먼저 설정해야함
		/*
		 * - 영화
		 * 		- 코메디
		 * 			- 한국
		 * 			- 미국
		 * 			- 영국
		 * 		- 로맨스
		 * 		- 액션
		*/
		// 폴더 : 7개 - 이미지 7개
		ImageView[] iv = new ImageView[7];
		for(int i = 0; i < 7; i++) {
			iv[i] = new ImageView(img);
			iv[i].setFitHeight(25); // 픽셀을 의미
			iv[i].setFitWidth(25);
		}
		
		// 1단계 트리 아이템 추가
		TreeItem<String> movie = new TreeItem<String>("영화", iv[0]);
		
		// 2단계 트리뷰에 추가
		TreeItem<String> comedy = new TreeItem<String>("comedy", iv[1]);
		TreeItem<String> romance = new TreeItem<String>("romance", iv[2]);
		TreeItem<String> action = new TreeItem<String>("action", iv[3]);
		// 1단계 밑에 추가
		movie.getChildren().addAll(comedy, romance, action);
		
		// 3단계 아이템 생성
		TreeItem<String> korea = new TreeItem<String>("한국", iv[4]);
		TreeItem<String> america = new TreeItem<String>("미국", iv[5]);
		TreeItem<String> english = new TreeItem<String>("영국", iv[6]);
		// 2단계 밑에 추가
		comedy.getChildren().addAll(korea, america, english);
		
		//한국 밑에 코메디 영화 2가지 추가(이미지 X)
		TreeItem<String> movie1 = new TreeItem<String>("영화1");
		TreeItem<String> movie2 = new TreeItem<String>("영화2");
		korea.getChildren().addAll(movie1, movie2);
		
		// 트리뷰에 추가
		treeView.setRoot(movie);
	}
	public void selectTree(MouseEvent evt) {
		// 더블클릭하면 그 값을 가져옴
		// get Click Count는 연속 클릭 횟수를 세는 메소드
		System.out.println(evt.getClickCount());
		
		if(evt.getClickCount() == 2) {
			TreeItem<String> item = treeView.getSelectionModel().getSelectedItem();
			System.out.println(item.getValue());
		}
	}
	
}



이번에는 BorderPane을 그대로 사용합니다.


BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("main.fxml"));


primaryStage.setTitle("주소록 작성");


실행 시 창 크기의 가로를 조금 늘려주겠습니다.


primaryStage.setWidth(615);





BorderPane을 지우지 않고 그대로 사용하겠습니다.


Library에 Pane을 검색하여 좌측 아래의 Hierarchy Border의 Top부분에 끌어다 놓고 안에 TextField 4가지, Button 2가지


TableView는 Center에 끌어다가 배치합니다.


TableColumn은 아래와 같이 추가합니다.




컬럼 이름에 오른쪽 클릭해서 Copy & Paste, Duplicate 둘 중 마음에 드는걸 사용.





Pane : Layout : Pref Width : 600, Pref Wifth : 80


TextField : Properties : Prompt Text - 이름


   Code : fx:id - nameField


TextField : Properties : Prompt Text - 주소


   Code : fx:id - addrField


TextField : Properties : Prompt Text - 폰번호


   Code : fx:id - phoneField


TextField : Properties : Prompt Text - 이메일


   Code : fx:id - mailField


Button : Properties : Prompt Text - 등록


Code : fx:id - regist


Button : Properties : Prompt Text - 초기화


Code : fx:id - init


TableView : Code : fx:id - table


TableColumn : Properties : Prompt Text - Name


   Code : fx:id - name


TableColumn : Properties : Prompt Text - Address


   Code : fx:id - address


TableColumn : Properties : Prompt Text - Phone


   Code : fx:id - phone


TableColumn : Properties : Prompt Text - E-mail


   Code : fx:id - email



package app;

import java.net.URL;
import java.util.ResourceBundle;

import app.model.TableRowModel;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;

public class MainController implements Initializable {
	// 필드
	@FXML private TextField nameField;
	@FXML private TextField addrField;
	@FXML private TextField phoneField;
	@FXML private TextField mailField;

	// 테이블&컬럼
	@FXML private TableView<TableRowModel> table;
	@FXML private TableColumn<TableRowModel, String> name;
	@FXML private TableColumn<TableRowModel, String> address;
	@FXML private TableColumn<TableRowModel, String> phone;
	@FXML private TableColumn<TableRowModel, String> email;


	ObservableList<TableRowModel> list = null;
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		System.out.println("FXML Load Complete");
		list = FXCollections.observableArrayList();
		// Model 객체가 list에 추가
		// lee, seoul, 010-1234-5678, email
		list.add(new TableRowModel("lee", "seoul", "no phone", "email"));

		// 각 필드를 CellData 인터페이스에
		name.setCellValueFactory(cellData -> cellData.getValue().getName());
		address.setCellValueFactory(cellData -> cellData.getValue().getAddress());
		phone.setCellValueFactory(cellData -> cellData.getValue().getPhone());
		email.setCellValueFactory(cellData -> cellData.getValue().getEmail());

		table.setItems(list);
	}
	public void regist() {
		list.add(new TableRowModel(nameField.getText(), addrField.getText(), phoneField.getText(), mailField.getText()));
		name.setCellValueFactory(cellData -> cellData.getValue().getName());
		address.setCellValueFactory(cellData -> cellData.getValue().getAddress());
		phone.setCellValueFactory(cellData -> cellData.getValue().getPhone());
		email.setCellValueFactory(cellData -> cellData.getValue().getEmail());
		// setItems는 자동으로 감지됨

		// 사용자 편의성을 위해 초기화
		nameField.setText("");
		addrField.setText("");
		phoneField.setText("");
		mailField.setText("");
		
		// 리스트의 모든 값 출력 하기
		// TableRowModel을 새로 list를 넣어 향상된 for문으로 출력
		for(TableRowModel model : list) {
			System.out.println(
					model.getName().get()+" | "+
					model.getAddress().get()+" | "+
					model.getPhone().get()+" | "+
					model.getEmail().get()
			);
		}
	}
	public void init() {
		list.clear();
		// 리스트를 직접 핸들링 한 경우는 이 부분이 필요
		table.setItems(list);
	}

}
package app.model;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class TableRowModel {
	private StringProperty name;
	private StringProperty address;
	private StringProperty phone;
	private StringProperty email;
	// 생성자 (Alt + Shift + S 로 쉽게 등록(Constructor))
	public TableRowModel(String name, String address, String phone, String email) {
		this.name = new SimpleStringProperty(name);
		this.address = new SimpleStringProperty(address);
		this.phone = new SimpleStringProperty(phone);
		this.email = new SimpleStringProperty(email);
	}
	// 게터세터 (Alt + Shift + S 로 쉽게 등록)
	public StringProperty getName() {
		return name;
	}
	public void setName(StringProperty name) {
		this.name = name;
	}
	public StringProperty getAddress() {
		return address;
	}
	public void setAddress(StringProperty address) {
		this.address = address;
	}
	public StringProperty getPhone() {
		return phone;
	}
	public void setPhone(StringProperty phone) {
		this.phone = phone;
	}
	public StringProperty getEmail() {
		return email;
	}
	public void setEmail(StringProperty email) {
		this.email = email;
	}
	
}





Main.java 설정


Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));


primaryStage.setTitle("image view");


fxml파일의 Border pane 삭제





AnchorPane위에


ImageView : Layout은 원하시는대로 사이즈를 잘 맞춰보세요.


Code : fx:id - imgView


Button : Code : On Action - fileChoose


먼저 Controller로 설정한 java파일이 시작(initialize)될 때 이미지를 웹에서 끌어와 기본 이미지로 띄울 것이고


그리고 이미지 선택 버튼을 누르면 윈도우 파일 선택창이 나오고 그 파일을 InputStream으로 받아서 ImageView에 띄우는것.



package app;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;

public class MainController implements Initializable {
	@FXML private ImageView imgView;
	
	@Override
	public void initialize(URL arg0, ResourceBundle arg1) {
		System.out.println("FXML Load Complete");
		// 기본 이미지(웹 이미지)
		// http://img.hani.co.kr/imgdb/resize/2017/0623/00502643_20170623.JPG , 치킨주의!!
		String url = "http://img.hani.co.kr/imgdb/resize/2017/0623/00502643_20170623.JPG";
		Image img = new Image(url);
		// 선택한 이미지의 사이즈 조절이 가능하다.
		imgView.setFitHeight(372);
//		imgView.setFitHeight(img.getHeight()/2);
		imgView.setFitWidth(372);
		imgView.setImage(img);
	}
	
	public void fileChoose() {
		// 사진 선택 창
		FileChooser fc = new FileChooser();
		fc.setTitle("이미지 선택");
		fc.setInitialDirectory(new File("C:/")); // default 디렉토리 설정
		// 선택한 파일 정보 추출
		// 확장자 제한
		ExtensionFilter imgType = new ExtensionFilter("image file", "*.jpg", "*.gif", "*.png");
//		fc.getExtensionFilters().add(imgType);
		ExtensionFilter txtType = new ExtensionFilter("text file", "*.txt", "*.doc");
		fc.getExtensionFilters().addAll(imgType, txtType);
		
		File selectedFile =  fc.showOpenDialog(null); // showOpenDialog는 창을 띄우는데 어느 위치에 띄울건지 인자를 받고
																// 그리고 선택한 파일의 경로값을 반환한다.
		System.out.println(selectedFile); 				// 선택한 경로가 출력된다.
		
		// 파일을 InputStream으로 읽어옴
		try {
			// 파일 읽어오기
			FileInputStream fis = new FileInputStream(selectedFile);
			BufferedInputStream bis = new BufferedInputStream(fis);
			// 이미지 생성하기
			Image img = new Image(bis);
			// 이미지 띄우기
			imgView.setImage(img);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
	}
	
}














전과 같이 먼저 Main.java


Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));


primaryStage.setTitle("ListView Example");


추가 수정하고 


fxml파일을 열자.




이번에는 BorderPane을 사용하는데 기본 BorderPane을 좌측 아래에서 선택해 delete를 눌러 지워주고 다시 리스트에서


BorderPane을 검색하여 추가하자


BorderPane은 영역을 Top Left Right Center Bottom으로 나뉜다.


탑에는 Label, 센터에는 ListView, 바텀에는 ComboBox를 추가하자


Label : Properties : Text - SELECT, Font - 34px, Node Alignment - CENTER(Text Alignment가 아니고 아래의 Node)


          Code fx:id - msg


ListView : fx:id - listView


Prompt Text : Properties : Text - --선택하세요--


                   Code : fx:id - combo, On Action : choose

이번에는 리스트를 초기화 하고 콤보박스에서 선택한 것을 리스트에 추가해본다.


그리고 Controller class 설정을 잊지말자


Observable이란 리스트를 사용한다. 거의 ArrayList랑 비슷한 기능을 가지고 있다.


package app;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;

public class MainController implements Initializable {
	@FXML private Label msg;
	@FXML private ListView listView;
	@FXML private ComboBox combo;
	
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		System.out.println("FXML Load Complete");
		// 콤보 박스
		ObservableListitem = FXCollections.observableArrayList("a", "b", "c");
		combo.setItems(item);
		// 리스트 뷰
		item = FXCollections.observableArrayList();
		item.add("Java FX 8");
		item.add("Java FX 9");
		item.add("AWT");
		item.add("SWING");
		item.add("GWT");
		item.add("FRONT-END");
		listView.setItems(item);
		// 리스트 여러개 선택할 수 있게 활성화
		listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
//		listView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
	}
	
	// 콤보박스에 선택한 내용을 msg Label에 표현
	ObservableList list = FXCollections.observableArrayList(); // 전역 변수로 선언한다면
																					  // 기존의 리스트 자리를 유지하고 값이 추가됨
	public void choose() {
//		msg.setText(combo.getValue());
		// 콤보박스에 선택한 내용을 listView에 추가
		list.add(combo.getValue());
		listView.setItems(list);
	}
	
	public void selected() {
//		String item = listView.getSelectionModel().getSelectedItem();
//		System.out.println(item);
		
		// 복수, multi select 구현
		ObservableList list = listView.getSelectionModel().getSelectedItems();
		for(String selecteditem : list) {
			System.out.println(selecteditem);
		}
		// 원래는 복수 선택시 별도 버튼 필요
	}
}
//







저번과 똑같이 Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));로 변경하고


primaryStage.setTitle("Input Controls");과 같이 임의의 이름을 설정하고 난 뒤


gridpane은 행과 Pane을 행과 열로 구분하여 영역을 배분하는 판이다.


ComboBox는 아래의 공개여부 항목의 단추를 눌러 선택하는 노드이다.





이번에는 Label은 바꿀 필요가 없으므로 따로 설정은 이름말고는 없다.


그리고 좌측 하단에 보면 (X, X)식으로 행과 열의 위치가 알맞게 쓰여있음을 확인 할 수 있다.


margin 바깥 여백, padding 안 여백 두가지를 사용하면 TextField같이 우측에 조금 튀어나온 부분을 수정할 수 있다.


TextField(한 줄), fx:id : title


PasswordField, fx:id : pass


ComboBox, fx:id : comboList


DatePicker, fx:id : endDate


TextArea(여러 줄), fx:id : note


Button(저장), OnAction : save


마지막으로 Controller의 Controller class를 잊지말자


다음은 코드이다.



package app;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;

// FXML 파일(main.fxml)이 대문자가 아닌것은 class파일이 아니기때문
public class MainController implements Initializable{
	@FXML private ComboBox comboList;
	@FXML private TextField title;
	@FXML private PasswordField pass;
	@FXML private DatePicker endDate;
	@FXML private TextArea note;
	
	@Override // 거의 생성자와 비슷하다고 생각하면 된다.
	public void initialize(URL location, ResourceBundle resources) {
		System.out.println("FXML Load Complete");
		// 콤보 박스에 값 추가, 콤보박스는 처음 생성 시 따로 값이 없기 때문에
		// 파일 로드할 때 값을 추가해준다.
		ObservableList combo = FXCollections.observableArrayList("공개", "회원 공개", "비공개");
		comboList.setItems(combo);
	}
	
	public void save() { // 선택한 값과 입력한 값을 모두 출력
		System.out.println("제목 : " + title.getText());
		System.out.println("비밀번호 : " + pass.getText());
		System.out.println("공개 여부 : " + comboList.getValue()); // value는 시스템에서 임의로 부여한 값을 가져올 때 사용
		System.out.println("게시 종료 : " + endDate.getValue());
		System.out.println("내용 : " + note.getText());
	}
}
//



먼저 main의 pane 부분을


Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));


위와 같이 바꾸고 primaryStage.setTitle("buttons"); 추가해서 마음에 드는 제목을 설정한다.





위와 같이 디자인 하고 한가지 다른점은 Radio 버튼을 group으로 묶어서 다중 선택을 막는것이다.


ToggleButton : Button, Code - OnAction : toggle, Properties : group


선택 : Button, Code - OnAction : genderSel, hobbySel


남자, 여자 : RadioButton, Code - fx:id : male, female


영화, 게임, 낚시하기 : CheckBox, Code - fx:id : movie, game, fishing


성별, 취미 : Label, Code - fx:id : gender, hobby


위의 공통점을 본다면 보통 값을 읽거나 쓰기 위해선 fx:id라는 이름을 주는것과


함수에 어떠한 기능을 할 지를 설정하기 위한 노드들은 Code에 OnAction으로 설정한다는것.


마지막으로 좌측 하단의 Controller 의 Controller class 설정도 잊지말자


본인이 만든 class 파일을 설정하는 것으로 이름은 각각 다르니 알아서 찾아야함


다음은 Controller에 기능을 구현한 예제이다.


Java에 작성할 때와 같으며 사용하던 클래스 함수 등 역시 사용 가능하다.



package app;

import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;

public class MainController implements Initializable{
	@FXML private Label toggle; // @fxml 어노테이션을 기입하여 FXML의 노드임을 명시
	@FXML private Label gender;// FXML에 지정한 fx:id와 철자 하나 틀리지 않고 같이 명시를 해야함
	@FXML private Label hobby; // 대소문자도 구분
	@FXML private RadioButton male;
	@FXML private RadioButton female;
	@FXML private CheckBox movie;
	@FXML private CheckBox game;
	@FXML private CheckBox fishing;

	boolean settxt = false;
	
	@Override
	public void initialize(URL location, ResourceBundle resourceBundle) {
		System.out.println("FXML Load Complete"); // 이 문구를 꼭 입력하여 오류가 어디서 났는지 판단 기준을 잡자.
															   // FXML을 로딩하고 java를 읽는다.
	}
	
	// 라벨 아래의 버튼, 역시 OnAction에 기입한 글자와 철자하나 틀리지 않고 같이 써야함
	public void toggle() {
		String flag = toggle.getText();
		System.out.println("눌렀을 때 상태 : " + flag);
		// 누를 때 마다 ON|OFF 바꾸기
		if(settxt) {
			toggle.setText("Off");
			settxt = false;
		}else {
			toggle.setText("On");
			settxt = true;
		}
	}
	
	// 라디오 아래의 버튼
	public void genderSel() {
		if(male.isSelected()) {
			gender.setText(male.getText());
		}
		if(female.isSelected()) {
			gender.setText(female.getText());
		}
	}
	
	ArrayList list = new ArrayList();
	// 체크박스 아래의 버튼
	public void hobbySel() {
		if(movie.isSelected()) {
			list.add(movie.getText());
		}
		if(game.isSelected()) {
			list.add(game.getText());
		}
		if(fishing.isSelected()) {
			list.add(fishing.getText());
		}
		System.out.println("취미 갯수 : " + list.size() + "개");
		for(String selected : list) {
			System.out.println("선택 항목 : " + selected);
		}
		list.clear();
	}
}
//




그리고 Swing이나 awt를 사용하는게 아니기 때문에 자동완성 사용시 주의하여야 한다.


FXLM을 사용하기 때문에 꼭 JavaFx를 사용해야함




JavaFX에서 오류가 나온다면 일단 javaFX를 지정하였는지 Code에 지정한 이름들의 철자를 틀리지 않았는지부터 확인하자.










  ※ 실행 테스트를 여러번 하거나 여러개의 프로젝트를 테스트 할 때 주의점.


테스트를 하다보면 자꾸 전에 했던 프로젝트가 실행되거나 FXML의 변경사항이 저장이 안되고 실행이 될 때가 있다.


아래와 같이 해결해보자




Eclipse와 Scene Builder는 별개의 프로그램이기 때문에 수정을 했을때 서로 바로바로 적용이 되지않는다.


따로 fxml파일을 한번 열어서 저장(Ctrl + S)를 눌러주면 된다.







이클립스 좌측에서 실행하고자 하는 프로젝트를 우클릭 - Run As - Java Application을 누르면 정상적으로 실행이 된다.




+ Recent posts