Search code examples
javafx-2radial

javafx layout nodes in circle


i'm trying to do a volume knob, i've got the knob, but i would like to place volume indicator around the knob represented by enlighten big spot. I gave it try with Circle node. I declared a array of Circle add all of them to a Group so that i can place the group where i want in the frame. I place each circle using the Circle method setTranslateX and setTranslateY. Each Circle has a radial gradient for filling and are not visible. I map the value of the slider (volume knob) to each Circle in my array, and call valueChanged method to get all the Circle below or equal to the value and set each of them visible. I have problem with the position of the Circle inside my Group. When they all are visible, they're perfectly placed, but when some get their property visible to false, the remaining visible one are not staying at their position. So i'm looking for a better design approach of doing this. Thank you.

Code example : Circle creation

audioSelectionValueToCircle = new HashMap<Double, Circle>(audioSelectionCircle.length);
    for (int i = 0; i < 6; i++) {
        final int count = i;
        audioSelectionCircle[count] = new Circle(15.0);
        RadialGradient rgrad = RadialGradientBuilder.create().centerX(audioSelectionCircle[count].getCenterX() - audioSelectionCircle[count].getRadius() / 5).centerY(audioSelectionCircle[count].getCenterY() - audioSelectionCircle[count].getRadius() / 5).radius(audioSelectionCircle[count].getRadius()).proportional(false).stops(new Stop(0.0, Color.WHITE), new Stop(0.3, Color.ORANGE), new Stop(1.0, Color.TRANSPARENT)).build();
        audioSelectionCircle[count].setFill(rgrad);
        audioSelectionValueToCircle.put(audioSelectionValue[count], audioSelectionCircle[count]);
        audioSelectionCircle[count].setVisible(false);
    }

Group creation

    gAudioSelectionCircle = new Group();
gAudioSelectionCircle.resizeRelocate(1225, 520, 280, 280);
        gAudioSelectionCircle.setRotate(90);

Circle placement

    int n = 6;
        for (int i = 0; i < n; i++) {
            final int count = i;
            double t = 2 * Math.PI * i / n;
            double x = 60 * Math.cos(t);
            double y = 60 * Math.sin(t);
            audioSelectionCircle[count].setTranslateX(x);
            audioSelectionCircle[count].setTranslateY(y);

            gAudioSelectionCircle.getChildren().add(audioSelectionCircle[count]);
//            audioSelectionCircle[count].resizeRelocate(x, y, 41, 41);
        }

Knob behavior

audioSelectionKnob.valueProperty().addListener(new ChangeListener<Number>() {

            @Override
            public void changed(ObservableValue<? extends Number> ov, Number oldVal, Number newVal) {
//                if (!audioSelectionKnob.isValueChanging()) {
//                }
                Iterator<Map.Entry<Double, Circle>> it = audioSelectionValueToCircle.entrySet().iterator();
                while(it.hasNext()) {
                    Map.Entry<Double, Circle> pairs = it.next();
                        Circle c = pairs.getValue();
                    if(pairs.getKey() >= newVal.doubleValue()) {
                        c.setVisible(true);
                    } else {
                        c.setVisible(false);
                    }
                }
            }
        });

Maybe one thing, if i add the group above my knob is that a problem ? I add the knob to the root Group, and i add the circle group to the root group above the knob.


I partly fixed the problem applying the rotation method of the sample clock to my circles. But, there is still a problem when i rotate my group of circle using group.setRotate(rotation);. When all my circles are visible the position of the circle is good, but when i start the change the visible property, by sliding the knob, the group behave like if i did group.setRotation(0);. It looks like it doesn't keep the rotation value that i set firstly.


Solution

  • the remaining visible one are not staying at their position

    This is probably a bug in your placement algorithm which is causing this. To diagnose, you will need provide a short, simplified, compilable sample app.

    Modifying only the visibility property of a node should not affect the layout of the application in any way.

    For laying nodes out around a circle you could take a look at this clock sample. What the clock sample does is position 12 nodes at the 12 o'clock position and then apply rotation to the nodes to get them to the appropriate location. The layout seems a bit similar to what you describe, though it's hard to be sure without a reference picture.

    Once the nodes are correctly placed, and you have your volume knob "slider" in the middle, then you can adjust the css styleclass of the nodes based on the slider value (similar to the way you are currently doing the visibility adjustment) to style them on or off. You should never need to add or remove further nodes from your group and the layout shouldn't change just the visibility or effect applied to the different styled nodes.

    You might want to look at this audio player example which features knob based sliders based on replacing the slider behaviour and skin (though you would need to tweak the implementation a bit to get the kind of effect you are describing).