How do you get JSplitPane to keep the same proportional location if the user has moved the location of the divider

折月煮酒 提交于 2020-01-01 18:54:13

问题


I am having trouble getting a JSplitPane to maintain the same relative position when its container is resized. If the split pane is assumed to have a fixed position everything works fine. I can get the divider location to stay close to the same relative position using the code shown below but the divider shifts to the left when the JFrame is resized. The shift is larger if the JFrame is resized slowly. The shift is to the left if the JFrame is made smaller or larger.

How do I get the divider to stay in the exact same proportional location?

This is what the GUI initially looks like.

This is what it looks like after several resizings.

And this is what the code looks like. This attempt is based on the information provided here:

JSplitPane splitting 50% precisely

JSplitPane SetDividerLocation Problem

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);
                }
            }
        }

    }

}

回答1:


  • Here's my attempt:
    • Override the doLayout method of the parent component of JSplitPane, after the resizing of JSplitPane has been completed, to adjust the position of the divider.
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);
      });
    }
  }
}



回答2:


This is what I ended up using. It seems to do exactly what I set out to do: create a split pane that proportionally resizes including proportionally resizing after the divider is moved.

This is what the example gui looks like before and after resize:

And here's what the code looks like. This is a complete example that includes proportional resizing of the split pane for horizontal and vertical split panes and the case where the user has moved the divider.

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);
                }
            }
        }

    }

}


来源:https://stackoverflow.com/questions/36067690/how-do-you-get-jsplitpane-to-keep-the-same-proportional-location-if-the-user-has

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!