JScrollPane “jumping” when scrollbars start being used

醉酒当歌 提交于 2019-12-11 05:05:01

问题


Sorry, this is a long piece of sample code below, but here's the issue:

I have a background that I'm actively drawing (I could probably be smart and draw it once and just scale it, but this shows the problem just as well).

You can use the mouse wheel to zoom in and out on the image.

The idea is to do a "google map" zoom where it zooms under the mouse pointer. What I've noticed is that it doesn't seem to actuall behave until the image is big enough to use both scroll bars. Until then, you get the image simply getting bigger, but locked to the origin.

The "correct" behavior should be that the viewposition is moved, even though the scrollbars aren't yet being utilized by an oversized image.

I'm not sure how to get around this (or if this is correct and expected) without drawing a much larger background behind the image so it more than fills the viewport.

It "jumps" after one or the other scroll bars engages due to (I think) the same issue.

Thoughts?

package com.hostigr.raw.io.client.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;

public class TestFrame extends JFrame {

    public static void main(String[] arg) {
            new TestFrame();
    }

    public TestFrame() {
        initComponents();
        setVisible(true);
    }

    private void initComponents() {
        setLayout(new BorderLayout());

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(600, 600);
        setPreferredSize(new Dimension(600, 600));

        add(new TopPanel());

    }

    private class TopPanel extends JPanel {
        JScrollPane scrollPane;

        public TopPanel() {
            setPreferredSize(new Dimension(500,500));
            scrollPane = new JScrollPane(new InteriorPanel());
            scrollPane.setPreferredSize(new Dimension(500,500));
            scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(10,490));
            scrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(490,10));
            scrollPane.setWheelScrollingEnabled(false);
            scrollPane.setHorizontalScrollBarPolicy(
                    JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
            scrollPane.setVerticalScrollBarPolicy(
                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            add(scrollPane);

        }
    }

    private class InteriorPanel extends JPanel {
        private double scale = 10.0;
        private final double scaleModifier = 0.1;
        private final int width = 10;
        private Point loc = new Point(0,0);
        private final int SIZE = 10;

        public InteriorPanel() {
            super(true);
            setPreferredSize(new Dimension((int)(scale * width * SIZE),
                    (int)(scale * width * SIZE)));
            this.addMouseWheelListener(new MapMouseWheelListener());
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2D = (Graphics2D) g;
            g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            g2D.scale(scale,scale);

            for (int row = 0; row <= SIZE; row++) {
                for (int col = 0; col < SIZE; col++) {
                    if ((col + row) % 2 == 0) {
                        g2D.setColor(Color.white);
                    } else {
                        g2D.setColor(Color.black);
                    }
                    g2D.fillRect(col * width, row * width, width, width);
                }
            }

        }

        private void incrementScale(int notches) {
            double modifier = 0;
            double prevScale = scale;
            if (notches != 0) {
                modifier = 1.0 + -notches / Math.abs(notches) * scaleModifier;
            }
            scale = scale * Math.pow(modifier, Math.abs(notches));
            /*if (scale * width < 1) {
                scale = 1.0/width;
            } else if (scale * width * 3 > parentHeight || scale * width * 3 > parentWidth) {
                if (parentHeight > parentWidth) {
                    scale = parentWidth / 3.0 / width;
                } else {
                    scale = parentHeight / 3.0 / width;
                }
            } else if (scale * width * SIZE < parentWidth) {
                scale = parentWidth / (double)SIZE / width;
            } else if (scale * width * SIZE < parentHeight) {
                scale = parentHeight / (double)SIZE / width;
            }*/

            this.repaint();
            setPreferredSize(new Dimension((int)(scale * width * SIZE),
                    (int)(scale * width * SIZE)));

            JViewport viewport = ((JViewport)(getParent().getParent().getComponent(0)));
            Point orig = viewport.getViewPosition();
            viewport.setViewPosition(new Point(
                    orig.x - (int)Math.round(loc.x*(1 - scale/prevScale)),
                    orig.y - (int)Math.round(loc.y*(1 - scale/prevScale))));
            System.out.println(orig + "\n  " + loc + "\n  " + (1 - scale / prevScale));
            revalidate();
        }

        private class MapMouseWheelListener implements MouseWheelListener {

            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                loc = e.getPoint();
                incrementScale(e.getWheelRotation());
            }
        }
    }
}

回答1:


looks like as JViewPort#scrollRectToVisible(Rectangle r) for me works

     viewport.scrollRectToVisible(new Rectangle(new Point(
     orig.x - (int) Math.round(loc.x * (1 - scale / prevScale)),
     orig.y - (int) Math.round(loc.y * (1 - scale / prevScale)))));

EDIT and with correct Swing repaint's rulles, then yout codeBlock must ends with revalidate(); + repaint();

        setPreferredSize(new Dimension((int) (scale * width * SIZE), 
        (int) (scale * width * SIZE)));
        JViewport viewport = ((JViewport) (getParent().getParent().getComponent(0)));
        Point orig = viewport.getViewPosition();
        /*viewport.setViewPosition(new Point(
                orig.x - (int) Math.round(loc.x * (1 - scale / prevScale)),
                orig.y - (int) Math.round(loc.y * (1 - scale / prevScale))));*/
        viewport.scrollRectToVisible(new Rectangle(new Point(
                orig.x - (int) Math.round(loc.x * (1 - scale / prevScale)),
                orig.y - (int) Math.round(loc.y * (1 - scale / prevScale)))));
        System.out.println(orig + "\n  " + loc + "\n  " + (1 - scale / prevScale));

        revalidate();
        repaint();


来源:https://stackoverflow.com/questions/6819243/jscrollpane-jumping-when-scrollbars-start-being-used

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