Пользовательская панель прокрутки с использованием только панели или любого другого контейнера - PullRequest
2 голосов
/ 17 февраля 2012

Я пытаюсь имитировать ScrollPane, просто наследуя от Panel, который сам перемещает своего потомка. Должно быть простой задачей, но ребенок не будет обрезан должным образом, то есть, если я прокручиваю, используя setScrollPosition(Point), контент виден, хотя и вне родителя Panel.

public class ScrollFrame
    extends Container
{
    public ScrollFrame() {
        setLayout(null);
    }

    public void paint(Graphics g) {
        g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
        super.paint(g);
    }

    public void setScrollPosition(Point p) {
        setScrollPosition(p.x, p.y);
    }

    public void setScrollPosition(int x, int y) {
        synchronized (getTreeLock()) {
            Component component = getComponent(0);

            int viewPortWidth = getWidth(),
                viewPortHeight = getHeight(),
                componentWidth = component.getWidth(),
                componentHeight = component.getHeight(),
                componentX = component.getX(),
                componentY = component.getY();

            if (x < 0) {
                x = 0;
            } else if (x > 0 && x + viewPortWidth > componentWidth) {
                x = componentWidth - viewPortWidth;
                if (x < 0)
                    x = 0;
            }

            if (y < 0) {
                y = 0;
            } else if (y > 0 && y + viewPortHeight > componentHeight) {
                y = componentHeight - viewPortHeight;
                if (y < 0)
                    y = 0;
            }

            component.setLocation(x * -1, y * -1);
            validate();
        }
    }

    public Point getScrollPosition() {
        synchronized (getTreeLock()) {
            Point p =  getComponent(0).getLocation();
            p.x *= -1;
            p.y *= -1;
            return p;
        }
    }
}

Проблема: Дочерний компонент, добавленный к ScrollFrame, виден за пределами ScrollFrame.

И, наконец, SSCCE для тестирования C & P, просто нажмите на красную область и перемещайте мышь вверх и вниз:

import java.awt.*;
import java.awt.event.*;

public class TestScrollPane extends Frame implements MouseListener,
    MouseMotionListener
{
    /* starting point */
    public static void main(String[] args)
    {
        TestScrollPane window = new TestScrollPane();
        window.setSize(800, 480);
        window.setVisible(true);
    }

    /*
     * a translucent element to be placed above all other components to receive
     * the MouseEvents
     */
    public class GlassPane extends Component
    {
        public void paint(Graphics g)
        {
            g.setColor(Color.RED);
            g.fillRect(0, 0, getWidth(), getHeight());
            super.paint(g);
        }
    }

    public class ScrollContainer extends Container
    {
        public ScrollContainer()
        {
            setLayout(null);
        }

        public void paint(Graphics g)
        {
            g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
            super.paint(g);
        }

        public void printComponents(Graphics g)
        {
            Component c = getComponent(0);
            Point p = c.getLocation();

            Graphics cg = g.create();

            try {
                cg.clipRect(0, 0, getWidth(), getHeight());
                cg.translate(p.x * -1, p.y * -1);
                c.paintAll(cg);
            } finally {
                cg.dispose();
            }
        }

        public void setScrollPosition(Point p)
        {
            setScrollPosition(p.x, p.y);
        }

        public void setScrollPosition(int x, int y)
        {
            synchronized (getTreeLock()) {
                Component component = getComponent(0);

                int viewPortWidth = getWidth(), viewPortHeight = getHeight(), componentWidth = component
                    .getWidth(), componentHeight = component.getHeight(), componentX = component
                    .getX(), componentY = component.getY();

                if (x < 0) {
                    x = 0;
                } else if (x > 0 && x + viewPortWidth > componentWidth) {
                    x = componentWidth - viewPortWidth;
                    if (x < 0)
                        x = 0;
                }

                if (y < 0) {
                    y = 0;
                } else if (y > 0 && y + viewPortHeight > componentHeight) {
                    y = componentHeight - viewPortHeight;
                    if (y < 0)
                        y = 0;
                }

                component.setLocation(x * -1, y * -1);
            }
        }

        public Point getScrollPosition()
        {
            synchronized (getTreeLock()) {
                Point p = getComponent(0).getLocation();
                p.x *= -1;
                p.y *= -1;
                return p;
            }
        }
    }

    private ScrollContainer scrollPane;
    private Panel scrollPaneContainer;
    private GlassPane glassPane;
    private boolean scrolling = false;
    private Point scrollingStartMouse;
    private Point scrollingStartPane;

    public TestScrollPane()
    {
        /* simple test window */
        super("TestScrollPane");
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e)
            {
                e.getWindow().dispose();
                System.exit(0);
            }
        });
        setLayout(null);

        /*
         * the ScrollPane provides the ability to show only a limited of a much
         * larger child element. Note that the ScrollPane is a container for
         * only ONE element, but the child element may act as a container, e.g.
         * a Panel
         */
        scrollPane = new ScrollContainer();
        scrollPane.addMouseListener(this);
        scrollPane.setBounds(30, 50, 300, 300);

        /*
         * the element we place in the scrollpane, a panel with null layout to
         * place the children
         */
        scrollPaneContainer = new Panel();
        scrollPaneContainer.setLayout(null);

        /* add some random buttons for testing purposes */
        for (int i = 0; i < 30; ++i) {
            Button button = new Button("Click Me " + i);
            button.addMouseListener(this);
            button.setBounds(0, 40 * i, 100, 30);
            scrollPaneContainer.add(button);
        }

        /*
         * a translucent component on top of all elements placed inside the
         * scrollPaneContainer to receive the MouseEvent's and properly scroll
         * the ScrollPane or propagate the event
         */
        glassPane = new GlassPane();
        glassPane.setBounds(0, 0, 1, 1);
        glassPane.addMouseListener(this);
        glassPane.addMouseMotionListener(this);

        /*
         * we do need to add the GlassPane first to place it above all other
         * elements
         */
        // scrollPaneContainer.add(glassPane);

        /*
         * the GlassPane and the scrollPaneContainer need to have the same size.
         * you have to set a size on the scrollPaneContainer else there won't be
         * any scrolling ;)
         */
        scrollPaneContainer.setSize(100, 1190);
        scrollPaneContainer.setPreferredSize(new Dimension(100, 1190));

        /* place the scrollPaneContainer (Panel) inside the ScrollPane */
        scrollPane.add(scrollPaneContainer);

        scrollPane.setBounds(30, 30, 150, 300);
        glassPane.setBounds(30, 30, 30, 300);

        Panel all = new Panel();
        all.setLayout(null);
        all.setBounds(30, 30, 400, 400);
        all.add(glassPane);
        all.add(scrollPane);

        add(all);
    }

    /* MouseListener implementation */
    public void mouseClicked(MouseEvent e)
    {
    }

    public void mouseEntered(MouseEvent e)
    {
    }

    public void mouseExited(MouseEvent e)
    {
    }

    public void mouseReleased(MouseEvent e)
    {
        if (e.getSource().equals(glassPane)) {
            scrolling = false;
        }
    }

    public void mousePressed(MouseEvent e)
    {
        if (e.getSource().equals(glassPane)) {
            scrolling = true;
            scrollingStartMouse = e.getPoint();
            scrollingStartPane = scrollPane.getScrollPosition();
        }
    }

    /* MouseMotionListener implementation */
    public void mouseDragged(MouseEvent e)
    {
        if (scrolling) {
            Point currentPos = e.getPoint();

            int mouseDeltaX = currentPos.x - scrollingStartMouse.x;
            int mouseDeltaY = currentPos.y - scrollingStartMouse.y;

            scrollPane.setScrollPosition(scrollingStartPane.x - mouseDeltaX,
                scrollingStartPane.y - mouseDeltaY);
        }
    }

    public void mouseMoved(MouseEvent e)
    {
    }

    void log(String message)
    {
        System.out.println(message);
    }

    void log(String message, MouseEvent e)
    {
        log(e.getComponent().getClass().getName() + ": " + message);
    }
}
...