#java #button #javafx #colors #javafx-11
Вопрос:
Когда я нажимаю кнопку «изменить цвет», она должна добавить пользовательский цвет(сделанный с помощью ползунков RGB) в первый квадрат(левый), затем, после того как я выберу второй пользовательский цвет и нажму кнопку «изменить цвет», следует переместить первый цвет во второй квадрат(правый) и поместить новый пользовательский цвет в первый квадрат(левый). Я должен быть в состоянии сделать это несколько раз, таким образом, повторяя цикл. Ему нужно только показать и сохранить последние два выбранных мной пользовательских цвета (два последних цвета). Я пробовал разные способы, но не мог сделать это правильно. Я предоставил чистый рабочий код ниже.
package com.example.tester;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.text.DecimalFormat;
public class colormade extends Application {
@Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setVgap(10);
grid.setHgap(70);
//Circle creation
Circle circle = new Circle(150, Color.rgb(0,0,0));
circle.setStrokeType(StrokeType.CENTERED);
circle.setOpacity(0.5);
circle.minHeight(150);
circle.minWidth(170);
circle.setStroke(Color.web("black"));
circle.setStrokeWidth(5);
//rectangle 1
Rectangle rectangle = new Rectangle();
rectangle.setWidth(50);
rectangle.setHeight(50);
rectangle.setStroke(Color.web("blue"));
rectangle.setFill(Color.GRAY);
grid.add(rectangle,4,0);
//rectangle 2
Rectangle rectangle1 = new Rectangle();
rectangle1.setWidth(50);
rectangle1.setHeight(50);
rectangle1.setStroke(Color.web("blue"));
rectangle1.setFill(Color.GRAY);
grid.add(rectangle1,5,0);
root.getChildren().add(rectangle);
root.getChildren().add(rectangle1);
//adding circle to root group
root.getChildren().add(circle);
//Opacity Slider
Slider opacity = new Slider();
opacity.setMin(0);
opacity.setValue(0.5);
opacity.setMax(1);
Text opacitylevel = new Text("0.5");
javafx.scene.control.Label opacityCaption = new Label("Opacity Level:");
grid.add(opacityCaption,0,1);
grid.add(opacity,1,1);
grid.add(opacitylevel,2,1);
Button clbtn = new Button("Change Colour");
clbtn.setPrefSize(200,10);
grid.add(clbtn,1,3);
//formatting
DecimalFormat df = new DecimalFormat("#.##");
opacity.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
circle.setOpacity(new_val.doubleValue());
opacitylevel.setText(df.format((double)new_val));
}
});
//R G B
Slider R = new Slider();
R.setMin(0);
R.setValue(0);
R.setMax(255);
Text red = new Text("0");
javafx.scene.control.Label Rt = new Label("Red:");
grid.add(Rt,0,4);
grid.add(R,1,4);
grid.add(red,2,4);
Slider G = new Slider();
G.setMin(0);
G.setValue(0);
G.setMax(255);
Text green = new Text("0");
javafx.scene.control.Label Gt = new Label("Green:");
grid.add(Gt,0,5);
grid.add(G,1,5);
grid.add(green,2,5);
Slider B = new Slider();
B.setMin(0);
B.setValue(0);
B.setMax(255);
Text blue = new Text("0");
javafx.scene.control.Label Bt = new Label("Blue:");
grid.add(Bt,0,6);
grid.add(B,1,6);
grid.add(blue,2,6);
root.add(grid,1,0);
//creating scene
Scene scene = new Scene(root,800,600,Color.web("white"));
//centering circle
circle.setCenterX(170);
circle.setCenterY(150);
//Change Colour event
//clbtn.setOnAction(event -> {
//});
//RGB events
//red
R.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
red.setText("" new_val.intValue());
circle.setFill(Color.rgb(new_val.intValue(),Integer.parseInt(green.getText()),Integer.parseInt(blue.getText())));
}
});
//green
G.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
green.setText("" new_val.intValue());
circle.setFill(Color.rgb(Integer.parseInt(red.getText()),new_val.intValue(),Integer.parseInt(blue.getText())));
}
});
//blue
B.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
blue.setText("" new_val.intValue());
circle.setFill(Color.rgb(Integer.parseInt(red.getText()),Integer.parseInt(green.getText()),new_val.intValue()));
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Комментарии:
1. Итак, на самом деле у вас есть две концепции. У вас есть понятие «цвета» и понятие «отображаемые цвета». Это в значительной степени основа концепции «модель/представление/контроллер», на которой основан JavaFX. Итак, вы начинаете с того, что выясняете, как «моделировать» цвета каким-то осмысленным образом, а затем выясняете, как представление может визуализировать эту модель. Это также должно привести вас к использованию «наблюдателя» для отслеживания изменений в модели, чтобы представление могло обновляться при изменении модели
2.stick to java naming conventions please
Ответ №1:
Solution strategy
This solution uses the strategy suggested by MadProgrammer in comments:
So what you have is actually two concepts. You have a concept of «colors» and a concept of «displayed colors». This is pretty much the bases for «model/view/controller» concept, which JavaFX is based on. So, you start by figuring out how to «model» the colors in some meaningful way and then you figure out how a view might visualise that model. This should also lead you down the path of using an «observer» to monitor changes to the model so the view can be updated when the model changes
How the solution works
The solution provides:
- A ColorModel class with two
ObjectProperty<Color>
objects:
primaryColor
andsecondaryColor
.
- Two color pickers, that allow the user to select colors to update in the model.
- Two rectangular display swatches that depend on the color model and, when it changes, change their color in response.
- Enforcement of the color change rules outlined in your question:
- If the secondary color changes, it is the only color that changes.
- If the primary color changes, the secondary color is set to the old value of the primary color.
- Bi-direction bindings between the color picker values and the model update the model when new colors are picked and update the picked colors when the model changes.
- Uni-directional bindings to the color model colors update the fills of the swatches when the model changes.
- A change listener defined in the color model for the primary color property enforces the rule to update the secondary color when the primary color changes.
Example code
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ColorPicker;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.*;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ColorSelector extends Application {
private static final Color INIT_COLOR = Color.GRAY;
@Override
public void start(Stage stage) {
ColorModel colorModel = new ColorModel(INIT_COLOR, INIT_COLOR);
ColorPicker[] colorPickers = {
new ColorPicker(INIT_COLOR),
new ColorPicker(INIT_COLOR)
};
Rectangle[] swatches = {
new Rectangle(50, 50, INIT_COLOR),
new Rectangle(50, 50, INIT_COLOR)
};
bindColorModel(colorModel, colorPickers, swatches);
GridPane layout = layoutScene(colorPickers, swatches);
stage.setScene(new Scene(layout));
stage.show();
}
private GridPane layoutScene(
ColorPicker[] colorPickers,
Rectangle[] swatches
) {
GridPane layout = new GridPane();
layout.setHgap(10);
layout.setVgap(10);
layout.setPadding(new Insets(10));
layout.addRow(0, colorPickers);
layout.addRow(1, swatches);
layout.getChildren().forEach(child ->
GridPane.setHalignment(
child,
HPos.CENTER)
);
return layout;
}
private void bindColorModel(
ColorModel colorModel,
ColorPicker[] colorPickers,
Rectangle[] swatches
) {
// because the pickers can change the colors in the model,
// we bidirectionally bind the colors in the model with the picker values.
colorModel.primaryColorProperty().bindBidirectional(
colorPickers[0].valueProperty()
);
colorModel.secondaryColorProperty().bindBidirectional(
colorPickers[1].valueProperty()
);
// the swatches cannot change the colors in the model,
// so we only need a standard unidirectional bind.
swatches[0].fillProperty().bind(
colorModel.primaryColorProperty()
);
swatches[1].fillProperty().bind(
colorModel.secondaryColorProperty()
);
}
public static void main(String[] args) {
launch(args);
}
}
class ColorModel {
private final ObjectProperty<Color> primaryColor;
private final ObjectProperty<Color> secondaryColor;
public ColorModel(Color initPrimaryColor, Color initSecondaryColor) {
primaryColor = new SimpleObjectProperty<>(initPrimaryColor);
secondaryColor = new SimpleObjectProperty<>(initSecondaryColor);
primaryColor.addListener(
(observable, oldValue, newValue) ->
secondaryColor.set(oldValue)
);
}
public Color getPrimaryColor() {
return primaryColor.get();
}
public ObjectProperty<Color> primaryColorProperty() {
return primaryColor;
}
public void setPrimaryColor(Color primaryColor) {
this.primaryColor.set(primaryColor);
}
public Color getSecondaryColor() {
return secondaryColor.get();
}
public ObjectProperty<Color> secondaryColorProperty() {
return secondaryColor;
}
public void setSecondaryColor(Color secondaryColor) {
this.secondaryColor.set(secondaryColor);
}
}
Why use a separate model for this
This could have been done without the abstraction of a separate model (by using and binding and listeners of the picker value properties and swatch fill properties).
However, the addition of the color model provides a bit more flexibility and separation of concerns:
- It is also more scalable as you could bind anything you want to the values of the color model, so it could be used not just to update the swatches, but other items as well.
- The model values could be independently saved and persisted to store and restore state.
- You change the model and have the values automatically reflected in all relevant places in the UI, without having to directly refer to any UI components.
- There are likely other benefits to this approach.
Integrating a custom color chooser
In your example code, you don’t use the standard JavaFX color pickers to allow the user to choose colors. That is fine if that is best for your user experience.
To integrate a custom color chooser into the solution outlined in this answer:
- Create a class to represent your custom color chooser widgets.
- Предоставьте свойство значения для текущего выбранного цвета для виджета (аналогично тому, как стандартный инструмент выбора цвета предоставляет свойство значения цвета с возможностью привязки).
- Соответствующим образом привязать к новому свойству значения таким же образом, как это решение привязывается к свойству значения стандартного средства выбора цвета.
Ваши виджеты пользовательского выбора цвета могут расширить стандартный класс управления JavaFX, но это сложнее реализовать, чем не расширять управление, и не является строго необходимым для решения этой проблемы.
Я не буду приводить здесь дополнительный код, чтобы продемонстрировать интеграцию пользовательского решения для выбора цвета.