简体   繁体   English

如何使 JLabel 变得可拖动?

[英]How to make a JLabel became dragable?

Guys I want to know if there is a way to make this arrow to be dragable just with X axis.伙计们,我想知道是否有办法让这个arrow可以只用 X 轴拖动。 I am using a null layout here, and this arrow is a jlabel that has been add into the jframe .我在这里使用null layout ,此arrow是已添加到jlabel中的jframe Here is the image for more info.这是有关更多信息的图像。 Thank you in advance.先感谢您。

在此处输入图像描述

Doing this kind of thing with a JLabel isn't impossible, it's just, complicated.JLabel做这种事情并非不可能,只是很复杂。

Personally, I'd be tempted to just use a custom painted route, as it gives you all the control.就个人而言,我很想只使用自定义绘制的路线,因为它可以让您控制所有内容。

For example...例如...

在此处输入图像描述

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException ex) {
                    Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() throws IOException {
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());
            add(new SlideToUnlock());
        }

    }

    public class SlideToUnlock extends JPanel {

        private String text;
        private Image indicatorImage;
        private Rectangle indicatorBounds;

        private Integer clickXOffset; // I can make it null and then ignore it
        private Integer dragX; // The x position of the drag

        public SlideToUnlock() throws IOException {
            indicatorImage = ImageIO.read(getClass().getResource("/images/ArrowRight.png"));
            setText("Slide to unlock");

            MouseAdapter mouseAdapter = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    resetTimer();

                    Rectangle bounds = getIndiciatorImageBounds();
                    if (bounds.contains(e.getPoint())) {
                        clickXOffset = e.getPoint().x - bounds.x;
                    } else {
                        clickXOffset = null;
                    }
                    dragX = null;
                    repaint();
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    invalidate();
                    repaint();
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    dragX = e.getPoint().x;
                    if (didReachTheOtherSide()) {
                        // Notifiy some kind of observer
                    }
                    repaint();
                }
            };

            addMouseListener(mouseAdapter);
            addMouseMotionListener(mouseAdapter);
        }

        @Override
        public Dimension getPreferredSize() {
            FontMetrics fm = getFontMetrics(getFont());
            Image indicatorImage = getIndicatorImage();
            Insets insets = getInsets();

            int imageWidth = 0;
            int imageHeight = 0;

            if (indicatorImage != null) {
                imageWidth = indicatorImage.getWidth(this);
                imageHeight = indicatorImage.getHeight(this);
            }

            int height = Math.max(fm.getHeight(), imageHeight)
                    + 1 // Border
                    + 8; // Inner track

            int width = 1 + 8 + fm.stringWidth(getText()) + imageWidth;

            width += insets.left + insets.right;
            height += insets.top + insets.top;

            return new Dimension(width, height);
        }

        @Override
        public void invalidate() {
            super.invalidate();
            indicatorBounds = null;
            clickXOffset = null;
            dragX = null;
        }

        @Override
        public void revalidate() {
            super.revalidate();
            indicatorBounds = null;
            clickXOffset = null;
            dragX = null;
        }

        protected boolean didReachTheOtherSide() {
            Rectangle bounds = getIndiciatorImageBounds();

            return bounds.x + bounds.width >= getWidth() - 1;
        }

        protected Rectangle getIndiciatorImageBounds() {
            if (getParent() == null) {
                return null;
            }

            if (dragX == null && indicatorBounds != null) {
                return indicatorBounds;
            }

            Image indicatorImage = getIndicatorImage();
            int indicatorX = 1;
            int indicatorY = (getHeight() - indicatorImage.getHeight(this)) / 2;
            indicatorBounds = new Rectangle(indicatorX, indicatorY, indicatorImage.getWidth(this), indicatorImage.getHeight(this));

            if (dragX != null) {
                indicatorBounds.x = (indicatorBounds.x - clickXOffset) + dragX;

                if (indicatorBounds.x + indicatorBounds.width > (getWidth() - 1)) {
                    indicatorBounds.x = getWidth() - indicatorBounds.width - 1;
                } else if (indicatorBounds.x < 1) {
                    indicatorBounds.x = 1;
                }
            }

            return indicatorBounds;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public Image getIndicatorImage() {
            return indicatorImage;
        }

        public void setIndicatorImage(Image indicatorImage) {
            this.indicatorImage = indicatorImage;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            int cornerRadius = 16;

            paintText(g2d);
            paintOverlay(g2d);
            paintIndicator(g2d);

            g2d.setColor(getForeground());
            g2d.draw(new RoundRectangle2D.Double(0, 0, getWidth() - 1, getHeight() - 1, cornerRadius, cornerRadius));

            g2d.dispose();
        }

        protected void paintOverlay(Graphics2D g) {
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(getBackground());
            Rectangle bounds = getIndiciatorImageBounds();
            g2d.fillRect(1, 1, bounds.x + bounds.width, getHeight() - 2);
            g2d.dispose();
        }

        protected void paintText(Graphics2D g) {
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(getForeground());
            FontMetrics fm = g2d.getFontMetrics();
            String text = getText();
            int xPos = getWidth() - 1 - 4 - fm.stringWidth(text);
            int yPos = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
            g2d.drawString(text, xPos, yPos);
            g2d.dispose();
        }

        protected void paintIndicator(Graphics2D g) {
            Graphics2D g2d = (Graphics2D) g.create();
            Rectangle bounds = getIndiciatorImageBounds();

            g2d.translate(bounds.x, bounds.y);
            Image indicatorImage = getIndicatorImage();
            g2d.drawImage(indicatorImage, 0, 0, this);
            g2d.dispose();
        }

    }
}

Ok, so, that "works", it does the job, but it's missing something... rebound, When the user lets go of the slide control, it should animate the rebound!好的,所以,“工作”,它完成了工作,但它缺少一些东西......反弹,当用户让滑动控件的 go 时,它应该为反弹设置动画!

(I bet you're sorry you asked now) (我打赌你很抱歉你现在问了)

在此处输入图像描述

Ah, that's better, it's the small details which make it啊,这样更好,是小细节造就了它

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException ex) {
                    Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() throws IOException {
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());
            add(new SlideToUnlock());
        }

    }

    public class SlideToUnlock extends JPanel {

        private String text;
        private Image indicatorImage;
        private Rectangle indicatorBounds;

        private Integer clickXOffset; // I can make it null and then ignore it
        private Integer dragX; // The x position of the drag

        private Timer reboundTimer;

        public SlideToUnlock() throws IOException {
            indicatorImage = ImageIO.read(getClass().getResource("/images/ArrowRight.png"));
            setText("Slide to unlock");

            MouseAdapter mouseAdapter = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    resetReboundTimer();

                    Rectangle bounds = getIndiciatorImageBounds();
                    if (bounds.contains(e.getPoint())) {
                        clickXOffset = e.getPoint().x - bounds.x;
                    } else {
                        clickXOffset = null;
                    }
                    dragX = null;
                    repaint();
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    startReboundTimer();
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    dragX = e.getPoint().x;
                    if (didReachTheOtherSide()) {
                        // Notifiy some kind of observer
                    }
                    repaint();
                }
            };

            addMouseListener(mouseAdapter);
            addMouseMotionListener(mouseAdapter);
        }

        protected void resetReboundTimer() {
            if (reboundTimer == null) {
                return;
            }
            reboundTimer.stop();
            reboundTimer = null;
        }

        protected void startReboundTimer() {
            resetReboundTimer();
            Rectangle bounds = getIndiciatorImageBounds();

            clickXOffset = 0;
            dragX = bounds.x + (bounds.width / 2);

            int lowerRange = 1 + (bounds.width / 2);
            int upperRange = getWidth() - 1 - (bounds.width / 2);

            int fullRange = upperRange - lowerRange;
            int currentRange = (bounds.x + (bounds.width / 2)) - lowerRange;
            double progressRange = currentRange / (double) fullRange;
            Duration fullDuration = Duration.ofMillis(250);
            Duration desiredDuration = Duration.ofMillis((long) (fullDuration.toMillis() * progressRange));

            int remainingRange = (int) (fullRange * progressRange);

            reboundTimer = new Timer(5, new ActionListener() {
                private Instant startTime = null;

                @Override
                public void actionPerformed(ActionEvent e) {
                    if (startTime == null) {
                        startTime = Instant.now();
                    }
                    Duration runTime = Duration.between(startTime, Instant.now());
                    double runTimeProgress = runTime.toMillis() / (double) desiredDuration.toMillis();
                    if (runTimeProgress >= 1.0) {
                        resetReboundTimer();
                        invalidate();
                    } else {
                        dragX = (int) (remainingRange * (1.0 - runTimeProgress));
                    }

                    repaint();
                }
            });
            reboundTimer.setInitialDelay(0);
            reboundTimer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            FontMetrics fm = getFontMetrics(getFont());
            Image indicatorImage = getIndicatorImage();
            Insets insets = getInsets();

            int imageWidth = 0;
            int imageHeight = 0;

            if (indicatorImage != null) {
                imageWidth = indicatorImage.getWidth(this);
                imageHeight = indicatorImage.getHeight(this);
            }

            int height = Math.max(fm.getHeight(), imageHeight)
                    + 1 // Border
                    + 8; // Inner track

            int width = 1 + 8 + fm.stringWidth(getText()) + imageWidth;

            width += insets.left + insets.right;
            height += insets.top + insets.top;

            return new Dimension(width, height);
        }

        @Override
        public void invalidate() {
            super.invalidate();
            indicatorBounds = null;
            clickXOffset = null;
            dragX = null;
        }

        @Override
        public void revalidate() {
            super.revalidate();
            indicatorBounds = null;
            clickXOffset = null;
            dragX = null;
        }

        protected boolean didReachTheOtherSide() {
            Rectangle bounds = getIndiciatorImageBounds();

            return bounds.x + bounds.width >= getWidth() - 1;
        }

        protected Rectangle getIndiciatorImageBounds() {
            if (getParent() == null) {
                return null;
            }

            if (dragX == null && indicatorBounds != null) {
                return indicatorBounds;
            }

            Image indicatorImage = getIndicatorImage();
            int indicatorX = 1;
            int indicatorY = (getHeight() - indicatorImage.getHeight(this)) / 2;
            indicatorBounds = new Rectangle(indicatorX, indicatorY, indicatorImage.getWidth(this), indicatorImage.getHeight(this));

            if (dragX != null) {
                indicatorBounds.x = (indicatorBounds.x - clickXOffset) + dragX;

                if (indicatorBounds.x + indicatorBounds.width > (getWidth() - 1)) {
                    indicatorBounds.x = getWidth() - indicatorBounds.width - 1;
                } else if (indicatorBounds.x < 1) {
                    indicatorBounds.x = 1;
                }
            }

            return indicatorBounds;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public Image getIndicatorImage() {
            return indicatorImage;
        }

        public void setIndicatorImage(Image indicatorImage) {
            this.indicatorImage = indicatorImage;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            int cornerRadius = 16;

            paintText(g2d);
            paintOverlay(g2d);
            paintIndicator(g2d);

            g2d.setColor(getForeground());
            g2d.draw(new RoundRectangle2D.Double(0, 0, getWidth() - 1, getHeight() - 1, cornerRadius, cornerRadius));

            g2d.dispose();
        }

        protected void paintOverlay(Graphics2D g) {
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(getBackground());
            Rectangle bounds = getIndiciatorImageBounds();
            g2d.fillRect(1, 1, bounds.x + bounds.width, getHeight() - 2);
            g2d.dispose();
        }

        protected void paintText(Graphics2D g) {
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(getForeground());
            FontMetrics fm = g2d.getFontMetrics();
            String text = getText();
            int xPos = getWidth() - 1 - 4 - fm.stringWidth(text);
            int yPos = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
            g2d.drawString(text, xPos, yPos);
            g2d.dispose();
        }

        protected void paintIndicator(Graphics2D g) {
            Graphics2D g2d = (Graphics2D) g.create();
            Rectangle bounds = getIndiciatorImageBounds();

            g2d.translate(bounds.x, bounds.y);
            Image indicatorImage = getIndicatorImage();
            g2d.drawImage(indicatorImage, 0, 0, this);
            g2d.dispose();
        }

    }
}

Now, doing this with labels would follow a very similar course, the only addition would be, you'd have to manage the label size and position yourself (as most layout managers won't allow you to this)现在,使用标签执行此操作将遵循非常相似的过程,唯一的补充是,您必须自己管理 label 大小和 position (因为大多数布局管理器不允许您这样做)

The following are some examples of dragging a label via a MouseMotionListener以下是通过MouseMotionListener拖动 label 的一些示例

Now, in your case, you don't really care about the y position, so that can remain centred relative to the container (or the track) and you'd just need to update the x position based on the click offset and the current drag position.现在,在您的情况下,您并不真正关心y position,因此它可以相对于容器(或轨道)保持居中,您只需要根据点击偏移量和当前更新x position拖动 position。 Complications arise in the fact that you'd need to be monitoring the label for drags, but converting the position of the movement to the parent container.复杂性在于您需要监控 label 的拖动,但将移动的 position 转换为父容器。 Doable, but it's an additional complication可行,但这是一个额外的并发症

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM