Swingで矩形選択範囲の表現
下記の投稿を見て、良いサンプルが見つからないので自分で書いてみました。
- Graphicsクラスの透明色設定について(22654)|teratail
選択範囲を色反転で表現
java.awt.Graphics
クラスにsetXORMode(Color)
というメソッドがあります。
これを使って、選択範囲の描画を行います。
選択範囲をドラッグで指定して、ドラッグが終わるとそこに選択範囲の矩形が残ります。
Ctrl+クリックすると、選択範囲がすべて除去されます。
- サンプル1:選択範囲を色反転で表現
import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.util.List; import javax.imageio.ImageIO; import javax.swing.*; final class PaintPanel extends JPanel implements MouseListener, MouseMotionListener { Image image; List<Rectangle> rects; Rectangle draggingRect; volatile boolean dragging; PaintPanel(Image image) { this.image = image; this.rects = Collections.synchronizedList(new ArrayList<>()); this.draggingRect = new Rectangle(0, 0); this.dragging = false; addMouseListener(this); addMouseMotionListener(this); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(image, 0, 0, this); g.setXORMode(Color.WHITE); if (dragging) { g.drawRect(draggingRect.x, draggingRect.y, draggingRect.width, draggingRect.height); } for (Rectangle r : rects) { g.drawRect(r.x, r.y, r.width, r.height); } } @Override public void mousePressed(MouseEvent e) { draggingRect.setBounds(e.getX(), e.getY(), 0, 0); dragging = true; repaint(); } @Override public void mouseDragged(MouseEvent e) { int x = (int)draggingRect.getX(); int y = (int)draggingRect.getY(); draggingRect.setSize(e.getX() - x, e.getY() - y); repaint(); } @Override public void mouseReleased(MouseEvent e) { dragging = false; Dimension rectSize = draggingRect.getSize(); if (rectSize.getWidth() > 0 && rectSize.getHeight() > 0) { rects.add(new Rectangle(draggingRect)); } draggingRect.setBounds(0, 0, 0, 0); repaint(); } @Override public void mouseClicked(MouseEvent e) { if (e.isControlDown()) { rects.clear(); } } @Override public void mouseMoved(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } } public final class App { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Image image; try { image = ImageIO.read(new File("./image.jpg")); } catch (IOException e) { e.printStackTrace(); return; } JFrame f = new JFrame(); f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); f.setSize(400, 300); f.add(new PaintPanel(image), BorderLayout.CENTER); f.setLocationRelativeTo(null); f.setVisible(true); } }); } }
こんな感じになります。
- 結果(サンプル1:選択範囲を色反転で表現)
点滅で表現
色を周期的に変化させるクラスAutoColorChanger
*1を作って、それを使って線を引きます。
ここでは、黒と白を行ったり来たりするエフェクトにしていますが、お好みで、run
メソッドの実装を変えてください。
色の表現以外は、サンプル1とほぼ同じです。
- サンプル2:選択範囲を点滅で表現
import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; import javax.imageio.ImageIO; import javax.swing.*; final class AutoColorChanger implements Runnable { static final int AMOUNT = 38; JComponent component; AtomicReference<Color> colorRef; int v; AutoColorChanger(JComponent component) { this.component = component; this.colorRef = new AtomicReference<Color>(Color.BLACK); this.v = 0; Executors.newSingleThreadExecutor().execute(this); } @Override public void run() { // 好きなように色を変化させるコードを書く boolean negate = false; while (true) { if (negate) { v -= AMOUNT; if (v < 0) { v = 0; negate = false; } } else { v += AMOUNT; if (v >= 256) { v = 255; negate = true; } } colorRef.set(new Color(v, v, v)); // 再描画&ウェイト repaintAndWait(); } } void repaintAndWait() { SwingUtilities.invokeLater(() -> { component.repaint(); }); try { Thread.sleep(120L); } catch (InterruptedException e) { throw new IllegalStateException(e); } } Color getColor() { return colorRef.get(); } } final class PaintPanel extends JPanel implements MouseListener, MouseMotionListener { Image image; List<Rectangle> rects; Rectangle draggingRect; volatile boolean dragging; AutoColorChanger autoColorChanger; PaintPanel(Image image) { this.image = image; this.rects = Collections.synchronizedList(new ArrayList<>()); this.draggingRect = new Rectangle(0, 0); this.dragging = false; this.autoColorChanger = new AutoColorChanger(this); addMouseListener(this); addMouseMotionListener(this); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(image, 0, 0, this); g.setColor(autoColorChanger.getColor()); if (dragging) { g.drawRect(draggingRect.x, draggingRect.y, draggingRect.width, draggingRect.height); } for (Rectangle r : rects) { g.drawRect(r.x, r.y, r.width, r.height); } } @Override public void mousePressed(MouseEvent e) { draggingRect.setBounds(e.getX(), e.getY(), 0, 0); dragging = true; repaint(); } @Override public void mouseDragged(MouseEvent e) { int x = (int)draggingRect.getX(); int y = (int)draggingRect.getY(); draggingRect.setSize(e.getX() - x, e.getY() - y); repaint(); } @Override public void mouseReleased(MouseEvent e) { dragging = false; Dimension rectSize = draggingRect.getSize(); if (rectSize.getWidth() > 0 && rectSize.getHeight() > 0) { rects.add(new Rectangle(draggingRect)); } draggingRect.setBounds(0, 0, 0, 0); repaint(); } @Override public void mouseClicked(MouseEvent e) { if (e.isControlDown()) { rects.clear(); } } @Override public void mouseMoved(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } } public final class App { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Image image; try { image = ImageIO.read(new File("./image.jpg")); } catch (IOException e) { e.printStackTrace(); return; } JFrame f = new JFrame(); f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); f.setSize(400, 300); f.add(new PaintPanel(image), BorderLayout.CENTER); f.setLocationRelativeTo(null); f.setVisible(true); } }); } }
追記:三角関数を使ったほうがシンプルかも。動作は少し変わりますけどね。
@Override public void run() { // 好きなように色を変化させるコードを書く while (true) { ++v; // 値が大きくなったらリセットしたほうが良いかも float f = (float)((Math.sin(v / 2f) + 1) / 2); colorRef.set(new Color(f, f, f)); // 再描画&ウェイト repaintAndWait(); } }
- 結果(サンプル2:選択範囲を点滅で表現)
少し大がかりになってしまいましたが、画像によってはこちらのほうが見やすくなるのではと思います。
選択範囲を回る点線で表現する、というのも有りかと思いますが、さすがに大変なので今回はここまで。
ちなみに、GIF画像のキャプチャーはGifCamというソフトを使いました。便利!
- GifCam | BahraniApps Blog
via
(おわり)
*1:クラス名がイケてないですよね...