JavaFX: создание / анализ пользовательских комбинированных элементов из файлов FXML и объектов обложки - PullRequest
0 голосов
/ 24 ноября 2018

Обдумывая и изучая, как работает управляющий объект "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()));
                }
            }
        };
    }
}
...