Guys I want to know if there is a way to make this arrow
to be dragable just with X axis. I am using a null layout
here, and this arrow
is a jlabel
that has been add into the jframe
. Here is the image for more info. Thank you in advance.
CodePudding user response:
Doing this kind of thing with a JLabel
isn't impossible, it's just, complicated.
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