JavaFX:从FXML文件和外观对象创建/解析自定义组合元素

时间:2018-11-23 22:07:14

标签: java javafx tableview bind fxml

在四处浏览并研究JavaFX的“ HTMLEditor”控制对象如何工作时,我发现它实际上是更复杂的“皮肤”的基础,“皮肤”本身就像是多个节点的组合。所有按钮,选择框和编辑器本身等都组合在较大的“ HTMLEditor”类中。我有一些可以减少80%的工作量的工作代码,但是我也有一个不了解正常方法并创建过于复杂的变通方法的历史。简而言之:我现在可以做我想做的事,但是我可能只是让工作变得更加艰辛和困难。

这是一件非常有用的好事。有没有一种方法可以创建类似于此的自定义类(在此,我创建一个自定义类,定义其角色和功能,并使用一个自定义外观定义其功能),然后让FXML API知道并自动解析它?我不喜欢FXML文件及其内容,但随后必须在代码中手动添加Node。 (至少对我来说)似乎令人困惑。

例如,创建一个TableView的组合(每个N类型的特定类型都有N列),并使其自动创建与每个列相对应的TextField,ChoiceBoxes等以及向该TableView添加,复制,修改和删除元素的按钮。我不得不做多次,目前正在为其创建实用程序(这是我发现较大控件对象的外观的方式)。

我目前最好的方法是在FXML文件中创建TableView,其列对象,输入控件和按钮,然后通过方法将它们链接起来。

我目前正在考虑的最好的主意是拥有一个特殊的Node,例如上面的“ EditableTable”中的情况,并且在FXML实际尝试解析它(并因为它不知道Class而失败)之前替换它与扩展的基础控件。但是,这仍然需要大量工作才能为其提供自定义ID,并回想起来连接所有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()));
                }
            }
        };
    }
}

0 个答案:

没有答案