JavaFX StackedBar Implementieren?

lam_tr

Top Contributor
Hallo zusammen,

habt ihr eine Idee wie ich die StackedBar von dem unten angehängten Foto in Javafx nachbilden kann? Ist auf der rechten Seite bei Sprint 8 oder im zweiten Bild bei Projects gemeint. An sich ist es eine Komponente von Stacked Bar CHart. Kann man da es entnehmen? Oder muss man da selber was basteln? Wenn das zweite zutrifft, wie geht man da am besten vor?

Image1_JIRA-Insiders-April16-Blog_600x-retina2x-pretty_dash.png

jira-4_1-jira-dashboard-example.png


Grüße
lam
 
Also du kannst sicher so ziemlich alles ausschalten und umgestalten, so dass eine StackedBarChart so aussehen könnte, wie du möchtest. Aber ich würde es, ehrlich gesagt, eher selbst machen.
Das sollte IMHO recht leicht sein:
HBox als container (oder VBox, wenn es vertikal sein soll), kein Spacing und dann Child-Elemenente deren Breite (min+max) per Property-Binding vom Parent abgeleitet werden.
Wenn ich mal Langeweile habe, muss ich das mal glatt probieren. 😀
 
Hallo MaxG,
ich meinte schon die Komponente alleine.

@dzim
Ja an sich hast du recht, jetzt wo du es kurz ausschlüsselst, ist mir auch klar, wie einfach das ist. Du brauchst dir keine Mühe machen, ich komme zurecht, das wichtigste hast du ja schon gesagt. Vielen Dank!
 
Zu spät. Ein Prototyp ist schon fertig. 😀

Hier die Komponente selbst (unten ist eine Klasse für Einträge integriert)
Java:
package eu.dzim.tests.fx;

import java.util.ArrayList;
import java.util.List;

import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;

public class StackedHBox extends HBox {
   
    private static final Color[] DEFAULT_COLORS = new Color[] { Color.AQUA, Color.AZURE, Color.YELLOW, Color.LIGHTYELLOW, Color.YELLOWGREEN,
            Color.GREENYELLOW, Color.GREEN, Color.DARKGREEN, Color.ORANGERED, Color.RED };
   
    private ObservableList<Entry> entries = FXCollections.observableArrayList();
    private ObservableList<HBox> panes = FXCollections.observableArrayList();
   
    private ListChangeListener<Entry> entriesChangeListener = this::entriesChanged;
    private ListChangeListener<HBox> panesChangeListener = this::panesChanged;
   
    public StackedHBox() {
        entries.addListener(entriesChangeListener);
        panes.addListener(panesChangeListener);
    }
   
    public ObservableList<Entry> getEntries() {
        return entries;
    }
   
    private void entriesChanged(ListChangeListener.Change<? extends Entry> change) {
        while (change.next()) {
            for (Entry entry : change.getRemoved())
                removeEntry(entry);
            for (Entry entry : change.getAddedSubList())
                addEntry(entry);
        }
    }
   
    private void removeEntry(Entry entry) {
        for (HBox pane : panes) {
            if (pane.getUserData() != entry)
                continue;
            if (entry.customListener != null) {
                entries.removeListener(entry.customListener);
                entry.customListener = null;
            }
            panes.remove(pane);
        }
    }
   
    private void addEntry(final Entry entry) {
        final HBox box = new HBox();
        box.setUserData(entry);
        ListChangeListener<Entry> listener = (ListChangeListener<Entry>) change -> {
            handleListChange(entry, box);
        };
        entry.customListener = listener;
        entries.addListener(listener);
        handleListChange(entry, box);
       
        ChangeListener<Color> colorListener = (obs, o, n) -> {
            BackgroundFill fill = new BackgroundFill(getColor(entry), CornerRadii.EMPTY, new Insets(0.0, 0.0, 0.0, 0.0));
            Background bg = new Background(fill);
            box.setBackground(bg);
        };
        entry.colorListener = colorListener;
        colorListener.changed(entry.color, null, entry.getColor());
       
        Label l = new Label();
        l.textProperty().bind(entry.text);
        box.getChildren().add(l);
       
        panes.add(box);
    }
   
    private void handleListChange(final Entry entry, final HBox box) {
        List<DoubleProperty> values = new ArrayList<>();
        for (Entry e : entries) {
            values.add(e.value);
        }
        List<Observable> bindings = new ArrayList<>();
        bindings.add(widthProperty());
        bindings.addAll(values);
        DoubleBinding widthBinding = Bindings.createDoubleBinding(() -> {
            int sum = 0;
            for (Entry e : entries)
                sum += e.value.get();
            final double percentage;
            if (sum == 0)
                percentage = 0.0;
            else
                percentage = entry.value.get() / sum;
            return getWidth() * percentage;
        }, bindings.toArray(new Observable[values.size()]));
        box.minWidthProperty().bind(widthBinding);
        box.maxWidthProperty().bind(widthBinding);
    }
   
    private Color getColor(Entry entry) {
        int index = entries.indexOf(entry);
        Color color = entry.color.get() == null ? DEFAULT_COLORS[index % entries.size()] : entry.color.get();
        return color;
    }
   
    private void panesChanged(ListChangeListener.Change<? extends HBox> change) {
        while (change.next()) {
            for (HBox pane : change.getRemoved())
                getChildren().remove(pane);
            for (HBox pane : change.getAddedSubList())
                getChildren().add(pane);
        }
    }
   
    public static class Entry {
       
        private DoubleProperty value = new SimpleDoubleProperty(0.0);
        private ObjectProperty<Color> color = new SimpleObjectProperty<>(null);
        private StringProperty text = new SimpleStringProperty(null);
       
        private ListChangeListener<Entry> customListener = null;
        private ChangeListener<Color> colorListener = null;
       
        public Entry(double value) {
            this.value.set(value);
        }
       
        public Entry(double value, Color color) {
            this(value);
            this.color.set(color);
        }
       
        public Entry(double value, Color color, String text) {
            this(value, color);
            this.text.set(text);
        }
       
        public final DoubleProperty valueProperty() {
            return this.value;
        }
       
        public final double getValue() {
            return this.valueProperty().get();
        }
       
        public final void setValue(final double value) {
            this.valueProperty().set(value);
        }
       
        public final ObjectProperty<Color> colorProperty() {
            return this.color;
        }
       
        public final Color getColor() {
            return this.colorProperty().get();
        }
       
        public final void setColor(final Color color) {
            this.colorProperty().set(color);
        }
       
        public final StringProperty textProperty() {
            return this.text;
        }
       
        public final String getText() {
            return this.textProperty().get();
        }
       
        public final void setText(final String text) {
            this.textProperty().set(text);
        }
       
        public ListChangeListener<Entry> getCustomListener() {
            return customListener;
        }
       
        public void setCustomListener(ListChangeListener<Entry> customListener) {
            this.customListener = customListener;
        }
       
        public ChangeListener<Color> getColorListener() {
            return colorListener;
        }
       
        public void setColorListener(ChangeListener<Color> colorListener) {
            this.colorListener = colorListener;
        }
    }
}

Eine Kleine Main-Klasse zum Testen:
Java:
package eu.dzim.tests.fx;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class MainStack extends Application {
    @Override
    public void start(Stage stage) throws Exception {
       
        stage.setTitle("Window");
       
        StackedHBox root = new StackedHBox();
        root.getEntries().add(new StackedHBox.Entry(5.0, Color.RED, "1"));
        root.getEntries().add(new StackedHBox.Entry(20.0, Color.GREEN, "2"));
        root.getEntries().add(new StackedHBox.Entry(75.0, Color.BLUE, "3"));
       
        Scene scene = new Scene(root, 640, 100);
       
        stage.setScene(scene);
        stage.show();
    }
   
    public static void main(String[] args) {
        launch(args);
    }
}

Das Ganze sieht dann etwas so aus:
 
Kann's nicht mehr editeren, daher hier ein Nachtrag: Es gibt einen Fehler in der #getColor-Methode. Hier die Angepasste Variante:
Java:
    private Color getColor(Entry entry) {
        int index = entries.indexOf(entry);
        Color color = entry.color.get() == null ? DEFAULT_COLORS[index % DEFAULT_COLORS.length] : entry.color.get();
        return color;
    }
 

Neue Themen


Zurück
Oben