I want to create a final class, having only static methods- an instance of this class will not be needed- it should be a static container. This class is supposed to have a map field containing created scenes. Now the problem is- method getClass() is not static and I cannot contain it in my static initializer block. Is there a way of creating scenes from FXML files without the use of non-static methods?
Here is the code:
package gui;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import java.util.Map;
import java.util.TreeMap;
public class ViewManager {
/**
* Class containing constant height and width of the window.
*/
public static final class Bounds{
public static final int HEIGHT = 800;
public static final int WIDTH = 800;
}
/**
* Enum class containing paths to following scenes.
*/
public enum SceneName{
LOGIN_VIEW("/login_view.fxml"),
MAIN_VIEW("/main_view.fxml");
private String path;
SceneName(String path) {
this.path = path;
}
#Override
public String toString() {
return path;
}
}
private static Map<SceneName, Scene> sceneContainer;
static{
sceneContainer = new TreeMap<>();
for(SceneName sceneName : SceneName.values()) {
//here is the non-static reference
Parent root = FXMLLoader.load(getClass().getResource(SceneName.LOGIN_VIEW.toString()));
sceneContainer.put(SceneName.LOGIN_VIEW, new Scene(root, Bounds.HEIGHT, Bounds.WIDTH));
}
}
public static Map<SceneName, Scene> getSceneContainer() {
return sceneContainer;
}
}
If you only need access to a certain Class instance, simply use ClassName.class:
// also changed this to use the loop variable instead of loading the same scene twice
Parent root = FXMLLoader.load(ViewManager.class.getResource(sceneName.toString()));
sceneContainer.put(sceneName, new Scene(root, Bounds.HEIGHT, Bounds.WIDTH));
In general using static too often should be avoided though. A singleton could be the better option. Even better if you're able to pass a ViewManager instance to all the classes that need it... (Taking a look at dependency injection may be a good idea.)
Related
i have a simple javafx FXML app
that has a button and a textArea
I am trying to write to the textArea from another
class (not the controller)
without sending the textArea
to that class ,
i added a getter on my controller class,
and on the writingClass i created an object of the ControllerClass
and then trying to write to the textArea ,
i am getting a java.lang.NullPointerException and java.lang.reflect.InvocationTargetException
what am i doing wrong ???
//Controller.java
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
public class Controller {
#FXML
Button myButton;
#FXML
TextArea myTextArea;
WriteToTextArea writeToTextArea;
public TextArea getMyTextArea() {
return myTextArea;
}
public void buttonPressed() {
writeToTextArea = new WriteToTextArea();
writeToTextArea.writeThis("trying To Write To TextArea");
}
}
//WriteToTextArea.java
package sample;
import javafx.scene.control.TextArea;
public class WriteToTextArea {
private Controller objectController;
private TextArea textArea;
public WriteToTextArea() {
objectController = new Controller();
textArea = new TextArea();
textArea = objectController.getMyTextArea();
}
public void writeThis(String whatToWrite) {
textArea.setText(whatToWrite);
}
}
The textArea is initialized in the controller by the FXMLLoader when the FXML file is loaded. It is only initialized in the controller, and won't be initialized in other instances of the same class (what would it be initialized to?). So when you create a new Controller instance with
objectController = new Controller();
the textArea in that instance is null, so when you call
textArea.setText(whatToWrite);
you get a null pointer exception.
You need the WriteToTextArea instance to have a reference to the controller itself, not some arbitrary instance of the same class. You can do this by passing a reference to the controller to the WriteToTextArea constructor:
package sample;
import javafx.scene.control.TextArea;
public class WriteToTextArea {
private Controller objectController;
private TextArea textArea;
public WriteToTextArea(Controller objectController) {
this.objectController = objectController ;
textArea = objectController.getMyTextArea();
}
public void writeThis(String whatToWrite) {
textArea.setText(whatToWrite);
}
}
and then in the controller code
public void buttonPressed() {
writeToTextArea = new WriteToTextArea(this);
writeToTextArea.writeThis("trying To Write To TextArea");
}
Sorry, but I must have a mental lapsus right now, because I don't see where the problem is, and should be trivial. I've prepared a simple scenario where I bind a field to a bean property using the BeanFieldGroup, and when I click the Change and Reset buttons, the model is set with the correct values, but the textfield in the UI is not being updated.
I'm using Vaadin4Spring, but should not be the issue.
import com.vaadin.data.fieldgroup.BeanFieldGroup;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.ui.Button;
import com.vaadin.ui.Notification;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
import java.io.Serializable;
#SpringView(name = "test")
public class TestView extends VerticalLayout implements View {
private TextField txtTest = new TextField("Test");
private Button btnChange = new Button("Click!");
private Button btnReset = new Button("Reset");
private TestBean testBean = new TestBean();
public TestView() {
txtTest.setImmediate(true);
addComponent(txtTest);
addComponent(btnChange);
addComponent(btnReset);
BeanFieldGroup<TestBean> binder = new BeanFieldGroup<>(TestBean.class);
binder.setItemDataSource(testBean);
binder.setBuffered(false);
binder.bind(txtTest, "text");
initComponents();
}
private void initComponents() {
btnChange.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
testBean.setText("Hello world!");
}
});
btnReset.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
testBean.setText("");
}
});
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
Notification.show("Test");
}
public class TestBean implements Serializable {
private String text;
public TestBean() {
text = "";
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}
The closest thing I have found is binder.discard(), which forces all bound fields to re-read its value from the bean. Yes, it still has to be called manually, but is still far less painful than getItemDataSource().getItemProperty(...).setValue(...). If there are any concerns with this brute-force approach then of course one can call Field.discard() directly on the fields that should be affected.
You are calling a bean setter directly and because Java doesn't provide any way to listen that kind of changes, the Vaadin property (or a TextField) doesn't know that the value has been changed. If you change the value through a Vaadin property by saying
binder.getItemDataSource().getItemProperty("text").setValue("new value");
then you see "new value" on the TextField, and because buffering is disabled, testBean.getText() also returns "new value".
I have an FXML file that has a Pane as one of it's entries, used for the output of our program. I would like to have this Pane contain an HTMLEditor. I'm a little bit confused at what to do to accomplish this. The class uses the Singleton pattern as recommended, and I can call into the Controller to get the Pane.
Then I find myself having to create an inner class, since HTMLEditor is not a Node. So I extended rectangle to do this, and use getChildren.add(htmlEditorWrapper) to try and add it as a Node. Of course, the HTMLEditor does not show up when I run the program.
The gist of my question: How do I add an HTMLEditor to a Pane (which is in the fxml file)?
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
import javafx.scene.web.HTMLEditor;
/**
* Gets the controller's outputPane (the console in the gui)
* #author Matt
*
*/
public class OutputPanel{
private static Pane pane;
private static HtmlEditorWrap htmlEditor = new HtmlEditorWrap();
private static final OutputPanel outputPanel = new OutputPanel();
private OutputPanel(){}
public static OutputPanel getInstance(){
pane = Controller.getOutputPane();
pane.getChildren().add(htmlEditor);
return outputPanel;
}
public void clear(){
//htmlEditor.setHtmlText();
}
public static void write(String text){
htmlEditor.setHtmlText(text + "\n");
}
}
class HtmlEditorWrap extends Rectangle{
HTMLEditor htmlEditor = new HTMLEditor();
public HtmlEditorWrap(){
htmlEditor.setLayoutX(200);
htmlEditor.setLayoutY(200);
htmlEditor.setHtmlText("TESTING");
}
public void setHtmlText(String text){
htmlEditor.setHtmlText(text);
}
}
Actually HtmlEditor is a Node. Try adding it directly.
And how did you obtain an editor by extending Rectangle?
here is what i have so far:
A working Webdriver based Java class, which logs-in to the application and goes to a Home page:
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.testng.AssertJUnit;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class MLoginFFTest {
private WebDriver driver;
private String baseUrl;
private String fileName = "screenshot.png";
#BeforeMethod
public void setUp() throws Exception {
FirefoxProfile profile = new FirefoxProfile();
profile.setPreference("network.http.phishy-userpass-length", 255);
profile.setAssumeUntrustedCertificateIssuer(false);
driver = new FirefoxDriver(profile);
baseUrl = "https://a.b.c.d/";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void testAccountLogin() throws Exception {
driver.get(baseUrl + "web/certLogon.jsp");
driver.findElement(By.name("logonName")).clear();
AssertJUnit.assertEquals(driver.findElement(By.name("logonName"))
.getTagName(), "input");
AssertJUnit.assertEquals(driver.getTitle(), "DA Logon");
driver.findElement(By.name("logonName")).sendKeys("username");
driver.findElement(By.name("password")).clear();
driver.findElement(By.name("password")).sendKeys("password");
driver.findElement(By.name("submit")).click();
driver.findElement(By.linkText("Account")).click();
AssertJUnit.assertEquals(driver.getTitle(), "View Account");
}
#AfterMethod
public void tearDown() throws Exception {
File screenshot = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(screenshot, new File(fileName));
} catch (IOException e) {
e.printStackTrace();
}
driver.quit();
}
}
Now as we see there are 2 pages:
1. Login page, where i have to enter username and password, and homepage, where i would be taken, once the authentication succeeds.
Now i want to implement this as PageObjects using Pagefactory: so i have :
package com.example.pageobjects;
import static com.example.setup.SeleniumDriver.getDriver;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
public abstract class MPage<T> {
private static final String BASE_URL = "https://a.b.c.d/";
private static final int LOAD_TIMEOUT = 30;
private static final int REFRESH_RATE = 2;
public T openPage(Class<T> clazz) {
T page = PageFactory.initElements(getDriver(), clazz);
getDriver().get(BASE_URL + getPageUrl());
ExpectedCondition pageLoadCondition = ((MPage) page).getPageLoadCondition();
waitForPageToLoad(pageLoadCondition);
return page;
}
private void waitForPageToLoad(ExpectedCondition pageLoadCondition) {
Wait wait = new FluentWait(getDriver())
.withTimeout(LOAD_TIMEOUT, TimeUnit.SECONDS)
.pollingEvery(REFRESH_RATE, TimeUnit.SECONDS);
wait.until(pageLoadCondition);
}
/**
* Provides condition when page can be considered as fully loaded.
*
* #return
*/
protected abstract ExpectedCondition getPageLoadCondition();
/**
* Provides page relative URL/
*
* #return
*/
public abstract String getPageUrl();
}
And for login Page not sure how i would implement that, as well as the Test, which would call these pages.
I hope these links will be helpful:
page objects in webdriver
page object using jBehave
I would recommend having one class responsible for before/after methods and they should be than called before and after whole scenario. Right now you would close webdriver just after logging in to your page and I guess this is not desired beahaviour. You can extract one level of abstraction simply for Pages where all the clicking happens (right now your MLoginFFTest class does logging in) for both login page and main page.
Now the other class would simply run methods from your Pages calsses like this:
#Test
public void shouldOpenMainPage(){
LoginPage loginPage = new LoginPage();
MainPage mainPage = loginPage.loginCorrectly();
mainPage.verifyOnMainPage();
mainPage.doSthElse();
verifySth(....);
}
So now your file structure could be sth like
++pages/LoginPage.class
++pages/MainPage.class
++steps/SomeTest.class
Hope this helps.
I want to make a reusable Alert Box Class which will be instantiated on various screens of my Flex Project.
Can some tell me whats next in the code below, because am sort of lost regarding how to set the message and title and how to call the Class in my project?
Any help.
Thanks
package components
{
import mx.controls.Alert;
import mx.core.mx_internal;
public class myAlertBox extends Alert
{
public function AlertBoza()
{
super();
var a:Alert;
}
override public static function show():void{
}
}
}
You do not need to extend Alert since the Alert.show() function is static. But you can set it as follows inserting a constructor for a message string and a class member. With that cou can just call the class with the constructor and show the alertbox.
package components
{
import mx.controls.Alert;
import mx.core.mx_internal;
public class myAlertBox
{
private var message:String;
public function myAlertBox(message:String = "")
{
super();
this.message = message;
}
public function show():void{
Alert.show(message);
}
}
}
In another class you can call:
var box:myAlertBox = new myAlertBox("Error");
myAlertBox.show();
If you just want to show a simple alert box, just use mx.controls.Alert directly as you can specify the title and the message show then:
import mx.controls.Alert;
Alert.show("the message", "the title");