Как заставить JSplitPane сохранять то же пропорциональное расположение, если пользователь переместил расположение разделителя
У меня возникли проблемы с получением JSplitPane для сохранения той же относительной позиции, когда его контейнер изменяется. Если предполагается, что разделенная панель имеет фиксированное положение, все работает нормально. Я могу получить расположение разделителя, чтобы оно оставалось близко к той же относительной позиции, используя код, показанный ниже, но разделитель сдвигается влево, когда размер JFrame изменяется. Сдвиг больше, если размер JFrame изменяется медленно. Сдвиг влево, если JFrame сделан меньше или больше.
Как заставить разделитель оставаться в точно таком же пропорциональном месте?
Вот как изначально выглядит GUI.
Вот как это выглядит после нескольких изменений.
И вот как выглядит код. Эта попытка основана на информации, представленной здесь:
JSplitPane расщепление 50% точно
JSplitPane SetDividerLocation Проблема
https://docs.oracle.com/javase/tutorial/uiswing/components/splitpane.html
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
public class Example {
public static void main(String[] args) {
new Example().showGui();
}
private void showGui() {
// create the jframe
JFrame jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(400, 200);
// create the left and right panels
JPanel left = new JPanel();
left.setBackground(Color.yellow);
JPanel right = new JPanel();
right.setBackground(Color.orange);
ResizableSplitPane split = new ResizableSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right, jFrame);
jFrame.getContentPane().add(split);
// show the gui
jFrame.setVisible(true);
}
public class ResizableSplitPane extends JSplitPane {
//
// instance variables
//
private boolean painted;
private double defaultDividerLocation;
private ResizableSplitPane resizableSplitPane = this;
private double currentDividerLocation;
private Component first;
private Component second;
private boolean dividerPositionCaptured = false;
//
// constructors
//
public ResizableSplitPane(int splitType, Component first, Component second, Component parent) {
this(splitType, first, second, parent, 0.5);
}
public ResizableSplitPane(int splitType, Component first, Component second, Component parent, double defaultDividerLocation) {
super(splitType, first, second);
this.defaultDividerLocation = defaultDividerLocation;
this.currentDividerLocation = defaultDividerLocation;
this.setResizeWeight(defaultDividerLocation);
this.first = first;
this.second = second;
parent.addComponentListener(new DividerLocator());
first.addComponentListener(new DividerMovedByUserComponentAdapter());
}
//
// trivial getters and setters
//
public double getDefaultDividerLocation() {
return defaultDividerLocation;
}
public void setDefaultDividerLocation(double defaultDividerLocation) {
this.defaultDividerLocation = defaultDividerLocation;
}
//
// implementation
//
@Override
public void paint(Graphics g) {
super.paint(g);
if (!painted) {
painted = true;
this.setDividerLocation(currentDividerLocation);
}
dividerPositionCaptured = false;
}
private class DividerLocator extends ComponentAdapter {
@Override
public void componentResized(ComponentEvent e) {
setDividerLocation(currentDividerLocation);
}
}
private class DividerMovedByUserComponentAdapter extends ComponentAdapter {
@Override
public void componentResized(ComponentEvent e) {
if (dividerPositionCaptured == false) {
dividerPositionCaptured = true;
currentDividerLocation = (double) first.getWidth() / (double) (first.getWidth() + second.getWidth());
System.out.println(currentDividerLocation);
}
}
}
}
}
2 ответа
- Вот моя попытка:
- Переопределить
doLayout
метод родительского компонентаJSplitPane
после изменения размераJSplitPane
было завершено, чтобы отрегулировать положение делителя.
- Переопределить
import java.awt.*;
import java.awt.event.*;
import java.math.BigDecimal;
import javax.swing.*;
public class Example2 {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new Example2().makeUI());
f.setSize(400, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
private JComponent makeUI() {
JPanel p = new JPanel(new GridLayout(0, 1, 0, 0));
JPanel left1 = new JPanel();
left1.setBackground(Color.YELLOW);
JPanel right1 = new JPanel();
right1.setBackground(Color.ORANGE);
JSplitPane split1 = new ResizableSplitPane(
JSplitPane.HORIZONTAL_SPLIT, left1, right1, p);
p.add(split1);
JPanel left2 = new JPanel();
left2.setBackground(Color.YELLOW);
JPanel right2 = new JPanel();
right2.setBackground(Color.ORANGE);
JSplitPane split2 = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT, left2, right2);
p.add(new SplitPaneWrapper(split2));
return p;
}
}
class ResizableSplitPane extends JSplitPane {
//
// instance variables
//
private boolean painted;
private double defaultDividerLocation;
private ResizableSplitPane resizableSplitPane = this;
private double currentDividerLocation;
private Component first;
private Component second;
private boolean dividerPositionCaptured = false;
//
// constructors
//
public ResizableSplitPane(int splitType, Component first, Component second, Component parent) {
this(splitType, first, second, parent, 0.5);
}
public ResizableSplitPane(int splitType, Component first, Component second, Component parent, double defaultDividerLocation) {
super(splitType, first, second);
this.defaultDividerLocation = defaultDividerLocation;
this.currentDividerLocation = defaultDividerLocation;
this.setResizeWeight(defaultDividerLocation);
this.first = first;
this.second = second;
parent.addComponentListener(new DividerLocator());
first.addComponentListener(new DividerMovedByUserComponentAdapter());
}
//
// trivial getters and setters
//
public double getDefaultDividerLocation() {
return defaultDividerLocation;
}
public void setDefaultDividerLocation(double defaultDividerLocation) {
this.defaultDividerLocation = defaultDividerLocation;
}
//
// implementation
//
@Override
public void paint(Graphics g) {
super.paint(g);
if (!painted) {
painted = true;
this.setDividerLocation(currentDividerLocation);
}
dividerPositionCaptured = false;
}
private class DividerLocator extends ComponentAdapter {
@Override
public void componentResized(ComponentEvent e) {
setDividerLocation(currentDividerLocation);
}
}
private class DividerMovedByUserComponentAdapter extends ComponentAdapter {
@Override
public void componentResized(ComponentEvent e) {
if (dividerPositionCaptured == false) {
dividerPositionCaptured = true;
currentDividerLocation = (double) first.getWidth() / (double)(first.getWidth() + second.getWidth());
System.out.println(currentDividerLocation);
}
}
}
}
class SplitPaneWrapper extends JPanel {
private final JSplitPane splitPane;
protected SplitPaneWrapper(JSplitPane splitPane) {
super(new BorderLayout());
this.splitPane = splitPane;
splitPane.setResizeWeight(.5);
add(splitPane);
}
private static int getOrientedSize(JSplitPane sp) {
return sp.getOrientation() == JSplitPane.VERTICAL_SPLIT
? sp.getHeight() - sp.getDividerSize()
: sp.getWidth() - sp.getDividerSize();
}
@Override public void doLayout() {
int size = getOrientedSize(splitPane);
double d = splitPane.getDividerLocation() / (double) size;
BigDecimal bd = new BigDecimal(d).setScale(2, BigDecimal.ROUND_HALF_UP);
super.doLayout();
if (splitPane.isShowing()) {
EventQueue.invokeLater(() -> {
int s = getOrientedSize(splitPane);
int iv = (int)(.5 + s * bd.doubleValue());
splitPane.setDividerLocation(iv);
});
}
}
}
Это то, что я в конечном итоге использовал. Кажется, он делает именно то, что я намеревался сделать: создать разделенную панель, которая пропорционально изменяет размеры, в том числе пропорционально изменяет размеры после перемещения разделителя.
Вот как выглядит пример GUI до и после изменения размера:
А вот как выглядит код. Это полный пример, который включает пропорциональное изменение размера разделенной панели для горизонтальных и вертикальных разделенных панелей и случай, когда пользователь переместил разделитель.
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
public class Example {
public static void main(String[] args) {
new Example().showGui();
}
private void showGui() {
// create the jframe
JFrame jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(400, 200);
// create the left panel
JPanel left = new JPanel();
left.setBackground(Color.yellow);
// create the right panel
JPanel right = new JPanel();
right.setBackground(Color.orange);
// create the bottom panel
JPanel bottom = new JPanel();
bottom.setBackground(Color.green);
// create the split panes
ResizableSplitPane horizontalSplit = new ResizableSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right, jFrame);
ResizableSplitPane verticalSplit = new ResizableSplitPane(JSplitPane.VERTICAL_SPLIT, horizontalSplit, bottom, jFrame);
jFrame.getContentPane().add(verticalSplit);
// show the gui
jFrame.setVisible(true);
}
public class ResizableSplitPane extends JSplitPane {
//
// instance variables
//
private boolean painted;
private double defaultDividerLocation;
private double dividerProportionalLocation;
private int currentDividerLocation;
private Component first;
private Component second;
private boolean dividerPositionCaptured = false;
//
// constructors
//
public ResizableSplitPane(int splitType, Component first, Component second, Component parent) {
this(splitType, first, second, parent, 0.5);
}
public ResizableSplitPane(int splitType, Component first, Component second, Component parent, double defaultDividerLocation) {
super(splitType, first, second);
this.defaultDividerLocation = defaultDividerLocation;
this.dividerProportionalLocation = defaultDividerLocation;
this.setResizeWeight(defaultDividerLocation);
this.first = first;
this.second = second;
parent.addComponentListener(new DividerLocator());
second.addComponentListener(new DividerMovedByUserComponentAdapter());
}
//
// trivial getters and setters
//
public double getDefaultDividerLocation() {
return defaultDividerLocation;
}
public void setDefaultDividerLocation(double defaultDividerLocation) {
this.defaultDividerLocation = defaultDividerLocation;
}
//
// implementation
//
@Override
public void paint(Graphics g) {
super.paint(g);
if (painted == false) {
painted = true;
this.setDividerLocation(dividerProportionalLocation);
this.currentDividerLocation = this.getDividerLocation();
}
}
private class DividerLocator extends ComponentAdapter {
@Override
public void componentResized(ComponentEvent e) {
setDividerLocation(dividerProportionalLocation);
currentDividerLocation = getDividerLocation();
}
}
private class DividerMovedByUserComponentAdapter extends ComponentAdapter {
@Override
public void componentResized(ComponentEvent e) {
System.out.println("RESIZED: " + dividerPositionCaptured);
int newDividerLocation = getDividerLocation();
boolean dividerWasMovedByUser = newDividerLocation != currentDividerLocation;
System.out.println(currentDividerLocation + "\t" + newDividerLocation + "\t" + dividerProportionalLocation);
if (dividerPositionCaptured == false || dividerWasMovedByUser == true) {
dividerPositionCaptured = true;
painted = false;
if(getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
dividerProportionalLocation = (double) first.getWidth() / (double) (first.getWidth() + second.getWidth());
} else {
dividerProportionalLocation = (double) first.getHeight() / (double) (first.getHeight() + second.getHeight());
}
System.out.println(dividerProportionalLocation);
}
}
}
}
}