JSplitPane PropertyChangeListener Wenn Benutzer property ändert

Guten tag,
ich wollte fragen, ob es eine Möglichkeit gibt, zu gucken weshalb eine property geändert wurde?
ich habe nämlich eine JSplitPane in der je nach Focus unterschiedlich große JPanels liegen.
Das heißt, das sich die Divider-Position ständig ändert. Gerne würde ich dieses property event aber nur abfangen, wenn der User den Divider ganz bewusst verschoben hat.
Gibt es da eine Möglichkeit?

Schönen Tag noch.

Das geht AFAIK nicht so ohne weiteres. Das würde auch dem Grundgedanken von MVC widersprechen: Wenn sich etwas ändert, kriegt man eine Nachricht darüber - aber NICHT über die “Ursache” der Änderung. Es könnte in jedem Fall etwas hakelig werden, aber … kann man erkennen, ob das passiert, was (keine Benutzerinteraktion ist aber) die Änderung der Divider-Position verursacht? Also GROB (!) sinngemäß sowas wie

boolean ignoreIt = false;
void focusChangesOrWhatever()
{
    ignoreIt = true;
    changePanelWhichWillCauseDividerChange();
    ignoreIt = false;
}

void dividerPropertyChanged()
{
    if (ignoreIt) return;
    ...
}

!? Was ist denn das eigentliche Ziel des ganzen?

Du könntest das Ganze ggf. erben und dieses zusätzliche verhalten mit einbauen

ich vermute die Divider feuern sich gegenseitig die Events zu und hängen in einer Endlosschleife

Ich habe eine liste mit verschiedenen Sachen. Je nachdem welche ausgewählt wird, wird eine Preview angezeigt. Diese ist unterschiedlich groß, weshalb eine Seite der Split pane die Größe ändert.
Code, der die Augen rausreist.

Beispiel

[spoiler]Java Code:

import java.awt.Dimension;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class BS {
    public static void main(String[] args) {
        final JFrame bs = new JFrame();
        bs.setTitle("Beispiel");
        bs.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        bs.setSize(300, 450);
        bs.setLocationRelativeTo(null);
        final Dimension[] d = new Dimension[200];
        final Random r = new Random();
        for (int i = 0; i < d.length; ++i)
            d** = new Dimension(r.nextInt(300), r.nextInt(300));
        final JList<Dimension> list = new JList<Dimension>(d);
        final JSplitPane split = new JSplitPane();
        split.setOrientation(JSplitPane.VERTICAL_SPLIT);
        split.setResizeWeight(0.8);
        split.setTopComponent(new JScrollPane(list));
        split.setBottomComponent(null);
        list.addListSelectionListener(new ListSelectionListener() {
            @SuppressWarnings("serial")
            @Override
            public void valueChanged(ListSelectionEvent e) {
                split.setBottomComponent(new JScrollPane(new JPanel() {
                    {
                        setPreferredSize(list.getSelectedValue());
                        add(new JLabel(list.getSelectedValue().toString()));
                    }
                }));
            }
        });
        bs.add(split);
        bs.setVisible(true);
    }
}

[/spoiler]

Die Previews sind sehr unterschiedlich groß aber man hat ja die Funktion [JAPI]JSplitPane#setResizeWeight(double)[/JAPI] weshalb ich dem User gern die Möglichkeit geben würde, den Divider mit der Hand zu versetzen, und die gesetzte Position danach als maximale Ausdehnung (mit setResizeWeight) zu setzen.

Unregistered

Hä… geht’s jetzt nur darum, dass die Größe sich nicht automatisch anpassen soll? Da gibt’s doch 1000 einfache Möglichkeiten…

import java.awt.*;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class BS {
    public static void main(String[] args) {
        final JFrame bs = new JFrame();
        bs.setTitle("Beispiel");
        bs.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        bs.setSize(300, 450);
        bs.setLocationRelativeTo(null);
        final Dimension[] d = new Dimension[200];
        final Random r = new Random();
        for (int i = 0; i < d.length; ++i)
            d** = new Dimension(r.nextInt(300), r.nextInt(300));
        final JList list = new JList(d);
        final JSplitPane split = new JSplitPane();
        split.setOrientation(JSplitPane.VERTICAL_SPLIT);
        split.setResizeWeight(0.8);
        split.setTopComponent(new JScrollPane(list));
        final JPanel container = new JPanel(new GridLayout(1,1));
        split.setBottomComponent(container);
        list.addListSelectionListener(new ListSelectionListener() {
            @SuppressWarnings("serial")
            @Override
            public void valueChanged(ListSelectionEvent e) {
                JPanel p = new JPanel();
                p.setPreferredSize((Dimension)list.getSelectedValue());
                p.add(new JLabel(list.getSelectedValue().toString()));
                container.removeAll();
                container.add(p);
                container.validate();
            }
        });
        bs.add(split);
        bs.setVisible(true);
    }
}

Geändert von Marco13 (Gestern um 20:30 Uhr) Grund: Code Formatierung

^^ Erstmal, vielen dank.

Nein, es geht nicht darum, dass die Größe nicht angepasst wird, sondern das sie nur bis zu einem bestimmten Maximum angepasst wird. Beispiel: (1 Dimensional, nur die Höhe)Die höhe ist 100px hoch.
Das Panel darf maximal 60px hoch sein.
Es wird ein Panel mit der Höhe 20 gesetzt.
Das Panel bekommt seine Höhe von 20.
Es wird ein Panel mit der Höhe 70 gesetzt.
Das Panel bekommt eine höhe von 60.
(Bis jetzt kein Problem)
Der User möchte das Panel ganz sehen, und verschiebt per Hand den Divider auf Höhe 80.
Scheinbar ist es dem User recht, wenn 80% vom Panel eingenommen werden.
Das heißt ab jetzt darf das Panel maximal 80px hoch sein, muss es aber nicht.
Das heißt, wenn der user den Divider verschiebt und loslässt, soll dies ab da die maximal mögliche Ausdehnung des Panels sein.

Unregistered

Vielen Dank, das du hier unbekannten Leuten hilfst, ihre Probleme zu lösen.
(oder ihnen zu sagen, das sie gar keine haben sondern sich nur welche machen,
oder die die sie haben nicht die Wichtigen sind, usw…)

Bin mir nicht sicher, ob ich es verstanden habe. Bei kleineren Komponenten soll die Höhe des SplitPane Bereich auf die Höhe der Komponente gesetzt werden und bei größeren auf einen Maximalwert?
Im Beispiel unten kann der Anwender den Maximalwert über den Divider verändern, allerdings nur nach oben. Soll er wieder reduziert werden müsste man sich etwas - z.B. eine separate “reset” Möglichkeit - einfallen lassen.

Code
[spoiler]```import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class BS {

public static void main(String[] args) {
	new BS().startDemo();
}

private double maxBottomHeigth = 60d;

public void startDemo() {
	final JFrame bs = new JFrame("Beispiel");
	bs.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	bs.setSize(300, 450);
	bs.setLocationRelativeTo(null);
	final Dimension[] d = new Dimension[20];
	final Random r = new Random();
	for (int i = 0; i < d.length; ++i)
		d** = new Dimension(300, r.nextInt(100) + 10);
	final JList<Dimension> list = new JList<Dimension>(d);
	final JSplitPane split = new JSplitPane();
	split.setOrientation(JSplitPane.VERTICAL_SPLIT);
	split.setTopComponent(new JScrollPane(list));
	final JLabel label = new JLabel("Test");
	split.setBottomComponent(label);

	list.addListSelectionListener(new ListSelectionListener() {
		public void valueChanged(ListSelectionEvent e) {
			Dimension dim = list.getSelectedValue();
			label.setText(dim.toString());
			label.setPreferredSize(dim);
			if (dim.height > maxBottomHeigth)
				split.setDividerLocation(1 - maxBottomHeigth / split.getMaximumDividerLocation());
			else
				split.setDividerLocation(1 - dim.getHeight() / split.getMaximumDividerLocation());
		}
	});
	split.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
		public void propertyChange(PropertyChangeEvent evt) {
			if ((Integer) evt.getOldValue() > 0) {
				double newValue = split.getMaximumDividerLocation() - (Integer) evt.getNewValue();
				if (newValue > maxBottomHeigth) {
					maxBottomHeigth = newValue;
				}
			}
		}
	});
	
	bs.add(split);

	SwingUtilities.invokeLater(new Runnable() {
		public void run() {
			bs.setVisible(true);
			split.setDividerLocation(1 - maxBottomHeigth / split.getMaximumDividerLocation());
		}
	});
}

}```
[/spoiler]

Ohne sich tiefer in den Listenermechanismus einzuhacken ist es vermutlich nicht erkennbar, wer die Änderung des Dividers auslöst.

Hab’ gerade nochmal geschaut… irgendwie ist das ganze dann IMHO ziemlich “hakelig” zu bedienen. Angefangen bei Usability-Killern, wie dass man einen Eintrag aus der Liste wählt, und dieser Eintrag dann (nach der Größenanpassung) ggf. nicht mehr sichtbar ist, scheinen da auch einige … “Spezifika” der SplitPane eine Rolle zu spielen: Wenn die BottomComponent größer ist, als die Component, in der die SplitPane selbst drin ist, wird die PreferredSize der BottomComponent quasi ignoriert (oder irgendein absurder Wert verwendet). Wie man das beschriebene Verhalten vernünftig mit einer bestimmten ResizeWeight kombinieren soll, wüßte ich jetzt auch nicht. Vielleicht könnte man da irgendwas dengeln, aber… ob das wirklich alles so gewünscht ist, oder nur ein Gedanke für eine “mögliche” Lösung war, ist gerade nicht ganz klar…