Обдумывая и изучая, как работает управляющий объект "HTMLEditor" в JavaFX, я узнал о том, как он, по сути, является основой для более сложной "кожи", которая сама по себе подобна комбинации большего количества узлов.Все кнопки, поля выбора и сам редактор и т. Д. Объединены в больший класс «HTMLEditor».У меня есть некоторый рабочий код, который сокращает усилия примерно на 80%, но у меня также есть история незнания нормального пути и создания слишком сложных обходных путей.Короче говоря: я могу делать то, что хочу прямо сейчас, но я, вероятно, просто делаю свою работу тяжелой и снова глючной.
Это очень полезно и полезно знать.Есть ли способ, которым я могу создать пользовательский класс, похожий на этот (где я создаю пользовательский класс, определяющий его роль и функции, и пользовательский скин, определяющий его функциональность), а затем получаю подтверждение FXML API и анализирую его автоматически?Мне не нравится иметь файл FXML и его содержимое, но приходится вручную добавлять узлы в коде.Это кажется сбивающим с толку (по крайней мере, мне).
Например, создание комбинации TableView (с N столбцами каждого определенного типа) и автоматическое создание TextFields, ChoiceBoxes и т. Д., Соответствующих каждому столбцу, иКнопки для добавления, копирования, изменения и удаления элементов этого TableView.Мне приходилось делать это несколько раз, и я сейчас нахожусь в процессе создания утилит для него (именно так я узнал о скинах более крупных управляющих объектов).
Лучшее, что у меня есть на данный момент, этосоздание TableView, его объектов столбцов, элементов управления вводом и кнопок в файле FXML и последующее их связывание методом.
Лучшая идея, о которой я сейчас думаю, - это иметь специальный узел, как вв случае выше "EditableTable" и до того, как FXML действительно пойдет и попытается проанализировать его (и потерпит неудачу, потому что он не знает Класс), замените его расширенными базовыми элементами управления.Это, однако, все еще потребует большой работы, предоставляя ему собственные идентификаторы и ретроспективно соединяя все объекты FXML.
Я вставлю код, как в настоящее время, но это довольно долго, что может бытьсделано намного проще, но в настоящее время это лучшее, что я получил, и пока работает.Консультации приветствуются, но не обязательны, я просто хотел бы знать, программирую ли я сложные обходные пути, потому что я не знаю простого способа, которым все снова пользуются.
package utils.fxmlUtils;
//T is a reference to the Class extending Bindeable. Required for functions. Probably a bit of a dirty trick but no idea how else
public abstract class Bindeable<T extends Bindeable<T>> implements Cloneable
{
//Creates the T. Required to call from Bindeable
public abstract T createFrom(String[] elements);
//Gets all the Column data as String[]
public abstract String[] getData();
//Sets all the Column data as String[]
public abstract void setData(String[] elements);
//Clones itself
public T clone()
{
return createFrom(getData());
}
}
package application;
import utils.fxmlUtils.Bindeable;
//Example for Bindeable. All this is is a large collection of Strings
public class Property extends Bindeable<Property>
{
private String tag;
private String propertyName;
private String propertyModifier;
private String modifierType;
private String numberType;
public Property(String tag, String propertyName, String propertyModifier, String modifierType, String numberType)
{
this.tag = tag;
this.propertyName = propertyName;
this.propertyModifier = propertyModifier;
this.modifierType = modifierType;
this.numberType = numberType;
}
//MUST HAVE a constructor using only String[] in order to allow the TableEditBinder to create the objects by invoking this constructor. Probably a wonky way to do it but no idea how else to do
public Property(String[] elements)
{
this(elements[0], elements[1], elements[2], elements[3], elements[4]);
}
public String getTag()
{
return tag;
}
public void setTag(String tags)
{
this.tag = tags;
}
public String getPropertyName()
{
return propertyName;
}
public void setPropertyName(String propertyName)
{
this.propertyName = propertyName;
}
public String getPropertyModifier()
{
return propertyModifier;
}
public void setPropertyModifier(String propertyModifier)
{
this.propertyModifier = propertyModifier;
}
public String getModifierType()
{
return modifierType;
}
public void setModifierType(String modifierType)
{
this.modifierType = modifierType;
}
public String getNumberType()
{
if(modifierType.equals("Add Base Value") || modifierType.equals("Multiply Value"))
{
return numberType;
} else
{
return "";
}
}
public void setNumberType(String numberType)
{
this.numberType = numberType;
}
public Property clone()
{
return new Property(tag, propertyName, propertyModifier, modifierType, numberType);
}
@Override
public Property createFrom(String[] elements)
{
return new Property(elements);
}
@Override
public String[] getData()
{
return new String[] {tag, propertyName, propertyModifier, modifierType, numberType};
}
@Override
public void setData(String[] elements)
{
tag = elements[0];
propertyName = elements[1];
propertyModifier = elements[2];
modifierType = elements[3];
numberType = elements[4];
}
}
package utils.fxmlUtils;
import javafx.scene.effect.Light;
import javafx.scene.effect.Lighting;
import javafx.scene.paint.Color;
//"Library" of Lighting effects for nodes. Identifies them as (in)valid, potentially invalid and currently being tested, as well as neutral and inactive (off)
public interface LightingIdentifiers
{
public static Lighting correctOn = new Lighting();
public static Lighting incorrectOn = new Lighting();
public static Lighting inProgressOn = new Lighting();
public static Lighting warningOn = new Lighting();
public static Lighting correctOff = new Lighting();
public static Lighting incorrectOff = new Lighting();
public static Lighting inProgressOff = new Lighting();
public static Lighting warningOff = new Lighting();
public static Lighting off = new Lighting();
public static Lighting neutral = new Lighting();
//Initializes and creates all the various lightings. Must be executed before using them to work.
public static void initialize()
{
correctOn.setDiffuseConstant(2);
correctOn.setSpecularConstant(2);
correctOn.setSpecularExponent(40);
correctOn.setSurfaceScale(0);
Light l = new Light.Distant();
l.setColor(new Color(0.5, 1, 0.5, 1));
correctOn.setLight(l);
incorrectOn.setDiffuseConstant(2);
incorrectOn.setSpecularConstant(2);
incorrectOn.setSpecularExponent(40);
incorrectOn.setSurfaceScale(0);
l = new Light.Distant();
l.setColor(new Color(1, 0.5, 0.5, 1));
incorrectOn.setLight(l);
inProgressOn.setDiffuseConstant(2);
inProgressOn.setSpecularConstant(2);
inProgressOn.setSpecularExponent(40);
inProgressOn.setSurfaceScale(0);
l = new Light.Distant();
l.setColor(new Color(0.5, 0.5, 1, 1));
inProgressOn.setLight(l);
warningOn.setDiffuseConstant(2);
warningOn.setSpecularConstant(2);
warningOn.setSpecularExponent(40);
warningOn.setSurfaceScale(0);
l = new Light.Distant();
l.setColor(new Color(1, 1, 0.5, 1));
warningOn.setLight(l);
correctOff.setDiffuseConstant(2);
correctOff.setSpecularConstant(2);
correctOff.setSpecularExponent(40);
correctOff.setSurfaceScale(0);
l = new Light.Distant();
l.setColor(new Color(0.25, 0.5, 0.25, 1));
correctOff.setLight(l);
incorrectOff.setDiffuseConstant(2);
incorrectOff.setSpecularConstant(2);
incorrectOff.setSpecularExponent(40);
incorrectOff.setSurfaceScale(0);
l = new Light.Distant();
l.setColor(new Color(0.5, 0.25, 0.25, 1));
incorrectOff.setLight(l);
inProgressOff.setDiffuseConstant(2);
inProgressOff.setSpecularConstant(2);
inProgressOff.setSpecularExponent(40);
inProgressOff.setSurfaceScale(0);
l = new Light.Distant();
l.setColor(new Color(0.25, 0.25, 0.25, 1));
inProgressOff.setLight(l);
warningOff.setDiffuseConstant(2);
warningOff.setSpecularConstant(2);
warningOff.setSpecularExponent(40);
warningOff.setSurfaceScale(0);
l = new Light.Distant();
l.setColor(new Color(0.25, 0.25, 0.5, 1));
warningOff.setLight(l);
off.setDiffuseConstant(2);
off.setSpecularConstant(2);
off.setSpecularExponent(40);
off.setSurfaceScale(0);
l = new Light.Distant();
l.setColor(new Color(0.25, 0.25, 0.25, 1));
off.setLight(l);
neutral.setLight(null);
}
}
package utils.fxmlUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Control;
import javafx.scene.control.PasswordField;
import javafx.scene.control.Spinner;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import utils.ArrayUtils;
public interface TableEditBinder
{
/*
* Initiates the process of binding a TableView and its Bindeable row objects to the create, copy, modify and delete Buttons as well as Controls representing the input fields.
* Edit fields must correspond to the various TableRows. Number of TableRows and Controls must be equal. Column N must respond to input field N (== they must be in the right order).
* Currently implemented: TextField
* TextArea
* PasswordField
* ChoiceBox<String>
* Spinner<Integer>
* Spinner<Double>
* Spinner<String>
* TODO: Implement more
*/
public static void bindTableView(TableView<? extends Bindeable<?>> table, Class<? extends Bindeable<?>> tableType, Button create, Button copy, Button modify, Button delete, Control... editFields)
{
//Create TableView ChangeListener
ChangeListener<Bindeable<?>> bl = new ChangeListener<Bindeable<?>>()
{
//Sub-Listeners for the various Controls
private ChangeListener<?>[] subListeners;
//changed method. If no sublisteners exist creates them. After that if sublisteners exist and a non-null new value exists fills the input Controls
@SuppressWarnings("unchecked")
@Override
public void changed(ObservableValue<? extends Bindeable<?>> observable, Bindeable<?> oldValue, Bindeable<?> newValue)
{
if (subListeners == null)
{
try
{
connect(newValue);
} catch (Exception e)
{
e.printStackTrace();
}
}
if (subListeners != null)
{
if (newValue == null)
{
newValue = table.getSelectionModel().getSelectedItem();
}
if (newValue != null)
{
for (int i = 0; i < subListeners.length; i++)
{
((ChangeListener<Bindeable<?>>) subListeners[i]).changed(observable, oldValue, newValue);
}
}
}
}
//Connection method. Requires a non-null object of the class. Only executed if the connector is non-null
@SuppressWarnings({ "unchecked", "rawtypes" })
public void connect(Bindeable<?> connector) throws Exception
{
//checks if all conditions work out. Throws an execption if TableView and edit fields are of different numbers
if (connector == null)
{
return;
}
String[] methods = new String[table.getColumns().size()];
for (int i = 0; i < methods.length; i++)
{
methods[i] = ((PropertyValueFactory) table.getColumns().get(i).getCellValueFactory()).getProperty();
}
if (methods.length != editFields.length || editFields.length != table.getColumns().size())
{
throw new Exception("Error: Method length != Editable length");
}
//creates the sub-listeners and binds them to the respective Controls
subListeners = new ChangeListener<?>[methods.length];
for (int i = 0; i < editFields.length; i++)
{
if (editFields[i].getClass().equals(TextField.class))
{
subListeners[i] = bind((TextField) editFields[i], connector, methods[i]);
} else if (editFields[i].getClass().equals(TextArea.class))
{
subListeners[i] = bind((TextArea) editFields[i], connector, methods[i]);
} else if (editFields[i].getClass().equals(PasswordField.class))
{
subListeners[i] = bind((PasswordField) editFields[i], connector, methods[i]);
} else if (editFields[i].getClass().equals(ChoiceBox.class))
{
subListeners[i] = bind((ChoiceBox<String>) editFields[i], connector, methods[i]);
} else if (editFields[i].getClass().equals(Spinner.class))
{
try
{
subListeners[i] = bindIntSpinner((Spinner<Integer>) editFields[i], connector, methods[i]);
} catch (Exception e)
{
try
{
subListeners[i] = bindDoubleSpinner((Spinner<Double>) editFields[i], connector, methods[i]);
} catch (Exception f)
{
subListeners[i] = bindStringSpinner((Spinner<String>) editFields[i], connector, methods[i]);
}
}
}
}
}
};
//Adding listeners to TableView and Buttons
table.getSelectionModel().selectedItemProperty().addListener(bl);
create.setOnAction(new EventHandler<ActionEvent>()
{
@SuppressWarnings("unchecked")
@Override
public void handle(ActionEvent event)
{
try
{
create((TableView<Bindeable<?>>) table, editFields, tableType);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
copy.setOnAction(new EventHandler<ActionEvent>()
{
@SuppressWarnings("unchecked")
@Override
public void handle(ActionEvent event)
{
try
{
copy((TableView<Bindeable<?>>) table, getAllSelected((TableView<Bindeable<?>>) table));
} catch (Exception e)
{
e.printStackTrace();
}
}
});
modify.setOnAction(new EventHandler<ActionEvent>()
{
@SuppressWarnings("unchecked")
@Override
public void handle(ActionEvent event)
{
try
{
modify((TableView<Bindeable<?>>) table, getAllSelected((TableView<Bindeable<?>>) table), editFields);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
delete.setOnAction(new EventHandler<ActionEvent>()
{
@SuppressWarnings("unchecked")
@Override
public void handle(ActionEvent event)
{
try
{
remove((TableView<Bindeable<?>>) table, getAllSelected((TableView<Bindeable<?>>) table));
} catch (Exception e)
{
e.printStackTrace();
}
}
});
}
//Copy functionality
public static void copy(TableView<Bindeable<?>> table, Bindeable<?>[] items)
{
table.getSelectionModel().clearSelection();
Bindeable<?> next = null;
for (int i = 0; i < items.length; i++)
{
next = items[i].clone();
table.getItems().add(next);
table.getSelectionModel().select(next);
}
table.refresh();
}
//Create functionality
public static void create(TableView<Bindeable<?>> table, Control[] inputs, Class<? extends Bindeable<?>> tableObjects)
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException
{
String[] base = itemize(inputs);
if(base == null)
{
return;
}
Bindeable<?> next = null;
table.getSelectionModel().clearSelection();
next = tableObjects.getConstructor(String[].class).newInstance(new Object[] {base});
table.getItems().add(next);
table.getSelectionModel().select(next);
table.refresh();
}
//Takes all the input data from the controls as String values and returns them
@SuppressWarnings("unchecked")
public static String[] itemize(Control[] inputs)
{
String[] base = new String[inputs.length];
for (int i = 0; i < inputs.length; i++)
{
if (inputs[i].getEffect().equals(LightingIdentifiers.incorrectOn))
{
return null;
}
if (inputs[i].getClass().equals(TextField.class))
{
base[i] = ((TextField) inputs[i]).getText();
} else if (inputs[i].getClass().equals(TextArea.class))
{
base[i] = ((TextArea) inputs[i]).getText();
} else if (inputs[i].getClass().equals(PasswordField.class))
{
base[i] = ((PasswordField) inputs[i]).getText();
} else if (inputs[i].getClass().equals(ChoiceBox.class))
{
base[i] = ((ChoiceBox<String>) inputs[i]).getSelectionModel().getSelectedItem();
} else if (inputs[i].getClass().equals(Spinner.class))
{
try
{
base[i] = ((Spinner<Integer>) inputs[i]).getValue() + "";
} catch (Exception e)
{
try
{
base[i] = ((Spinner<Double>) inputs[i]).getValue() + "";
} catch (Exception f)
{
base[i] = ((Spinner<String>) inputs[i]).getValue() + "";
}
}
}
}
return base;
}
//Modify functionality
public static void modify(TableView<Bindeable<?>> table, Bindeable<?>[] items, Control[] inputs)
{
String[] itemized = itemize(inputs);
if(itemized == null)
{
return;
}
table.getSelectionModel().clearSelection();
for (int i = 0; i < items.length; i++)
{
items[i].setData(itemized);
table.getSelectionModel().select((Bindeable<?>) items[i]);
}
table.refresh();
}
//Remove functionality
public static void remove(TableView<Bindeable<?>> table, Bindeable<?>[] items)
{
table.getSelectionModel().clearSelection();
for (int i = 0; i < items.length; i++)
{
table.getItems().remove(items[i]);
}
table.refresh();
}
//Returns all selected items in the TableView
public static Bindeable<?>[] getAllSelected(TableView<Bindeable<?>> table)
{
return table.getSelectionModel().getSelectedItems().toArray(new Bindeable<?>[table.getSelectionModel().getSelectedItems().size()]);
}
//Binds a TextField
public static ChangeListener<Bindeable<?>> bind(TextField textField, Bindeable<?> connector, String propertyName)
{
return new ChangeListener<Bindeable<?>>()
{
Method m = null;
@Override
public void changed(ObservableValue<? extends Bindeable<?>> observable, Bindeable<?> oldValue, Bindeable<?> newValue)
{
try
{
if (m == null)
{
m = connector.getClass().getMethod("get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1, propertyName.length()));
}
textField.setText((String) m.invoke(newValue));
} catch (Exception e)
{
e.printStackTrace();
System.err.println();
System.err.println(ArrayUtils.printObject(connector.getClass().getMethods()));
}
}
};
}
//Binds a TextArea
public static ChangeListener<Bindeable<?>> bind(TextArea textArea, Bindeable<?> connector, String propertyName)
{
return new ChangeListener<Bindeable<?>>()
{
Method m = null;
@Override
public void changed(ObservableValue<? extends Bindeable<?>> observable, Bindeable<?> oldValue, Bindeable<?> newValue)
{
try
{
if (m == null)
{
m = connector.getClass().getMethod("get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1, propertyName.length()));
}
textArea.setText((String) m.invoke(newValue));
} catch (Exception e)
{
e.printStackTrace();
System.err.println();
System.err.println(ArrayUtils.printObject(connector.getClass().getMethods()));
}
}
};
}
//Binds a PasswordField
public static ChangeListener<Bindeable<?>> bind(PasswordField passwordField, Bindeable<?> connector, String propertyName)
{
return new ChangeListener<Bindeable<?>>()
{
Method m = null;
@Override
public void changed(ObservableValue<? extends Bindeable<?>> observable, Bindeable<?> oldValue, Bindeable<?> newValue)
{
try
{
if (m == null)
{
m = connector.getClass().getMethod("get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1, propertyName.length()));
}
passwordField.setText((String) m.invoke(newValue));
} catch (Exception e)
{
e.printStackTrace();
System.err.println();
System.err.println(ArrayUtils.printObject(connector.getClass().getMethods()));
}
}
};
}
//Binds a ChoiceBox<String>
public static ChangeListener<Bindeable<?>> bind(ChoiceBox<String> choiceBox, Bindeable<?> connector, String propertyName)
{
return new ChangeListener<Bindeable<?>>()
{
Method m = null;
@Override
public void changed(ObservableValue<? extends Bindeable<?>> observable, Bindeable<?> oldValue, Bindeable<?> newValue)
{
try
{
if (m == null)
{
m = connector.getClass().getMethod("get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1, propertyName.length()));
}
choiceBox.getSelectionModel().select((String) m.invoke(newValue));
} catch (Exception e)
{
e.printStackTrace();
System.err.println();
System.err.println(ArrayUtils.printObject(connector.getClass().getMethods()));
}
}
};
}
//Binds a Spinner<Integer>
public static ChangeListener<Bindeable<?>> bindIntSpinner(Spinner<Integer> integerSpinner, Bindeable<?> connector, String propertyName)
{
return new ChangeListener<Bindeable<?>>()
{
Method m = null;
@Override
public void changed(ObservableValue<? extends Bindeable<?>> observable, Bindeable<?> oldValue, Bindeable<?> newValue)
{
try
{
if (m == null)
{
m = connector.getClass().getMethod("get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1, propertyName.length()));
}
integerSpinner.getValueFactory().setValue(Integer.parseInt((String) m.invoke(newValue)));
} catch (Exception e)
{
e.printStackTrace();
System.err.println();
System.err.println(ArrayUtils.printObject(connector.getClass().getMethods()));
}
}
};
}
//Binds a Spinner<Double>
public static ChangeListener<Bindeable<?>> bindDoubleSpinner(Spinner<Double> doubleSpinner, Bindeable<?> connector, String propertyName)
{
return new ChangeListener<Bindeable<?>>()
{
Method m = null;
@Override
public void changed(ObservableValue<? extends Bindeable<?>> observable, Bindeable<?> oldValue, Bindeable<?> newValue)
{
try
{
if (m == null)
{
m = connector.getClass().getMethod("get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1, propertyName.length()));
}
try
{
doubleSpinner.getValueFactory().setValue((double) Integer.parseInt((String) m.invoke(newValue)));
} catch (Exception e)
{
doubleSpinner.getValueFactory().setValue(Double.parseDouble((String) m.invoke(newValue)));
}
} catch (Exception e)
{
e.printStackTrace();
System.err.println();
System.err.println(ArrayUtils.printObject(connector.getClass().getMethods()));
}
}
};
}
//Binds a Spinner<String>
public static ChangeListener<Bindeable<?>> bindStringSpinner(Spinner<String> stringSpinner, Bindeable<?> connector, String propertyName)
{
return new ChangeListener<Bindeable<?>>()
{
Method m = null;
@Override
public void changed(ObservableValue<? extends Bindeable<?>> observable, Bindeable<?> oldValue, Bindeable<?> newValue)
{
try
{
if (m == null)
{
m = connector.getClass().getMethod("get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1, propertyName.length()));
}
stringSpinner.getValueFactory().setValue((String) m.invoke(newValue));
} catch (Exception e)
{
e.printStackTrace();
System.err.println();
System.err.println(ArrayUtils.printObject(connector.getClass().getMethods()));
}
}
};
}
}