JLabelの文字列をシマー効果で装飾する
Total: 137, Today: 1, Yesterday: 1
Posted by aterai at
Last-modified:
Summary
JLabelの文字列をLinearGradientPaintで作成した光沢が移動するアニメーション付きで描画します。
Screenshot

Advertisement
Source Code Examples
class ShimmerLayerUI extends LayerUI<JLabel> {
private static final int FPS = 16;
private static final int BAND_WIDTH = 100;
private static final float SPEED = 4f;
private static final int ALPHA = 180;
private static final Color TRANSPARENT = new Color(0x0, true);
private final Timer timer = new Timer(FPS, null);
private final float[] fractions = {0f, .5f, 1f};
private float animX;
private transient BufferedImage buffer;
@Override public void installUI(JComponent c) {
super.installUI(c);
timer.addActionListener(ev -> {
animX += SPEED;
if (animX > c.getWidth() + BAND_WIDTH) {
animX = -BAND_WIDTH;
}
c.repaint();
});
timer.start();
}
@Override public void uninstallUI(JComponent c) {
timer.stop();
super.uninstallUI(c);
}
@Override public void paint(Graphics g, JComponent c) {
// super.paint(g, c);
@SuppressWarnings("unchecked")
JLayer<? extends JLabel> layer = (JLayer<? extends JLabel>) c;
JLabel label = layer.getView();
if (label == null || label.getText() == null || label.getText().isEmpty()) {
super.paint(g, c);
} else {
int w = Math.max(1, c.getWidth());
int h = Math.max(1, c.getHeight());
ensureBuffer(w, h);
paintLayerToBuffer(c, w, h);
BufferedImage shimBuf = createShimmerBuffer(label, c, w, h);
paintShimmerBuffer(shimBuf);
g.drawImage(buffer, 0, 0, c);
}
}
private void ensureBuffer(int w, int h) {
if (buffer == null || buffer.getWidth() != w || buffer.getHeight() != h) {
buffer = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
}
}
private void paintLayerToBuffer(JComponent c, int w, int h) {
Graphics2D buf = buffer.createGraphics();
buf.setComposite(AlphaComposite.Clear);
buf.fillRect(0, 0, w, h);
buf.setComposite(AlphaComposite.SrcOver);
c.paint(buf);
buf.dispose();
}
private BufferedImage createShimmerBuffer(JLabel label, JComponent c, int w, int h) {
BufferedImage shimBuf = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D sg = shimBuf.createGraphics();
AbstractShimmerLabel.applyRenderingHints(sg);
Rectangle[] rects = ShimmerLayout.layoutLabel(label, sg);
Rectangle textRect = rects[1];
FontMetrics fm = sg.getFontMetrics(label.getFont());
Point labelOrigin = SwingUtilities.convertPoint(label, 0, 0, c);
float x = labelOrigin.x + textRect.x;
float y = labelOrigin.y + textRect.y + fm.getAscent();
// 1. Draw the text outline to the shimmer buffer (to serve as a mask).
sg.setPaint(label.getForeground());
FontRenderContext frc = sg.getFontRenderContext();
new TextLayout(label.getText(), label.getFont(), frc).draw(sg, x, y);
// 2. Compose the gradient using SrcAtop (masked by the text's alpha channel).
sg.setComposite(AlphaComposite.SrcAtop);
Color[] colors = {
TRANSPARENT,
ShimmerColors.shimmerBright(label.getForeground(), ALPHA),
TRANSPARENT,
};
float endX = animX + BAND_WIDTH;
sg.setPaint(new LinearGradientPaint(animX, 0f, endX, 0f, fractions, colors));
sg.fillRect(0, 0, w, h);
sg.dispose();
// Clear the text area painted
Graphics2D buf = buffer.createGraphics();
buf.setPaint(label.getBackground());
buf.fill(textRect);
buf.dispose();
return shimBuf;
}
private void paintShimmerBuffer(BufferedImage shimBuf) {
Graphics2D buf = buffer.createGraphics();
buf.setComposite(AlphaComposite.SrcOver);
buf.drawImage(shimBuf, 0, 0, null);
buf.dispose();
}
}
View in GitHub: Java, KotlinDescription
Standard Area ShimmerJLabel#paintComponent(...)をオーバーライドしてJLabel全体にLinearGradientPaintで作成した半透明の光沢を上書きJLabelのテキストだけではなく、アイコンや背景にも光沢が描画される- ダークモードには対応していない
Text-Only Shimmer (Clipping)Standard Area Shimmer同様、JLabel#paintComponent(...)をオーバーライドしてLinearGradientPaintで作成した半透明の光沢を描画しているが、テキストをアウトラインでクリップしてアイコンや背景は光沢を描画しない- クリップ領域はアンチエイリアスで滑らかにできないので、文字の縁にジャギーが表示される
- Fontのアウトラインを取得して文字列の内部を修飾する
Text-Only Shimmer (Composite)setClip(...)でのクリッピングではなく、別バッファに作成した光沢付き文字列をAlphaComposite.SrcOver、AlphaComposite.SrcAtopで滑らかに合成- Windowの縁をソフトクリッピングでなめらかにする
JLayer Shimmer LabelText-Only Shimmer (Composite)と同様の光沢付き文字列描画をJLayer上で描画