以下は大急ぎで書いたもので、細部的にはアラがありますが、やり方の基本は学べると思います。このプログラムでは、外枠(”注射器")にBufferedImageを使っていますが、実際は外枠もグラデーションも、どちらも単なるShapeにしてpaintComponent()の中でdrawやfillできます。そのほうが、考え方としてはシンプルです。BufferedImageを使ったのは、透明属性(AlphaComposite)を、下のグラデーションを見せるために便利に使えると思ったからです。2つのShapeを作ったほうが、楽だったかな…。
--------------------------------------------------
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
public class Tube extends JPanel{
int panelWidth, panelHeight;
GeneralPath tube, bar;
BufferedImage tubeImg;
GradientPaint gp;
Arc2D topArc, bottomArc,
leftTopBarc, rightTopBarc; // parts of topArc
Line2D tleft, tright, bleft, bright;
float bodyWidth, bodyLength, bodyFullLength, barWidth, barLength,
domeHeight, barTopY;
Point2D.Float bodyLtop, bodyRtop, bodyLbottom, bodyRbottom,
barBottomLeft, barBottomRight, barCielLeft, barCielRight;
float wrate, hrate;
public Tube(int width, int height){
panelWidth = width;
panelHeight = height;
setPreferredSize(new Dimension(width, height));
setBackground(new Color(240, 240, 240));
wrate = 2f / 5f; // tube width ratio against panel width
hrate = 2f / 4f; // tube height ratio against panel height
bodyWidth = barWidth = (int)(panelWidth * wrate);
domeHeight = bodyWidth / 2;
bodyLength = panelHeight * hrate;
bodyFullLength = bodyLength + bodyWidth; // bw == domeHeight * 2
barLength = bodyFullLength * 0.85f;
bodyLtop = new Point2D.Float
(((1 - wrate) / 2) * panelWidth, ((1 - hrate) / 2) * panelHeight);
bodyLbottom = new Point2D.Float(bodyLtop.x, bodyLtop.y + bodyLength);
bodyRtop = new Point2D.Float(bodyLtop.x + bodyWidth, bodyLtop.y);
bodyRbottom = new Point2D.Float(bodyRtop.x, bodyLbottom.y);
barTopY = (bodyLtop.y - domeHeight) + (bodyFullLength - barLength);
barCielLeft = new Point2D.Float(bodyLtop.x, barTopY);
barCielRight = new Point2D.Float(bodyRtop.x, barTopY);
barBottomLeft
= new Point2D.Float(bodyLbottom.x, bodyLbottom.y + domeHeight);
barBottomRight
= new Point2D.Float(bodyRbottom.x, bodyLbottom.y + domeHeight);
tube = makeTube(bodyLtop, bodyLbottom, bodyRbottom, bodyRtop, bodyWidth);
tubeImg = makeTubeImg(tube);
bar = makeBar(barBottomLeft, barBottomRight, barCielRight, barCielLeft);
}
GeneralPath makeTube(Point2D.Float tl, Point2D.Float bl, Point2D.Float br,
Point2D.Float tr, float width){
GeneralPath gp = new GeneralPath();
gp.append(new Line2D.Float(tl, bl), true);
gp.append(new Arc2D.Float
(new Rectangle2D.Float(bl.x, bl.y - width / 2, width, width),
180, 180, Arc2D.OPEN), true);
gp.append(new Line2D.Float(tr, br), true);
gp.append(new Arc2D.Float
(new Rectangle2D.Float(tl.x, tl.y - width / 2, width, width),
0, 180, Arc2D.OPEN), true);
return gp;
}
GeneralPath makeBar
(Point2D.Float bl, Point2D.Float br, Point2D.Float tr, Point2D.Float tl){
GeneralPath gp = new GeneralPath();
gp.append(new Line2D.Float(bl, br), true);
gp.append(new Line2D.Float(br, tr), true);
gp.append(new Line2D.Float(tr, tl), true);
gp.append(new Line2D.Float(tl, bl), true);
return gp;
}
BufferedImage makeTubeImg(GeneralPath closedShape){
BufferedImage bim
= new BufferedImage(panelWidth, panelHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bim.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.white);
g2d.fillRect(0, 0, panelWidth, panelHeight);
g2d.setColor(Color.black);
g2d.setStroke(new BasicStroke(2.0f));
g2d.draw(closedShape);
g2d.setComposite(AlphaComposite.Clear);
g2d.fill(closedShape);
g2d.dispose();
return bim;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setPaint(new GradientPaint
(barCielLeft, Color.yellow, bodyLbottom, Color.pink, true));
g2.fill(bar);
g2.drawImage(tubeImg, 0, 0, this);
}
/* test program */
static Tube t;
static JButton up, dw;
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container con = frame.getContentPane();
t = new Tube(400, 400);
con.add(t, BorderLayout.CENTER);
up = new JButton("<html>U<br>P<br></html>");
dw = new JButton("<html>D<br>O<br>W<br>N<br></html>");
up.setPreferredSize(new Dimension(40, 100));
dw.setPreferredSize(new Dimension(40, 100));
UpDown ud = new UpDown();
up.addActionListener(ud);
dw.addActionListener(ud);
JPanel p = new JPanel(new GridLayout(2, 1));
p.add(up);
p.add(dw);
con.add(p, BorderLayout.WEST);
frame.pack();
frame.setVisible(true);
}
static class UpDown implements ActionListener{
public void actionPerformed(ActionEvent e){
float inc;
JButton bt = (JButton)e.getSource();
if (bt == up){
inc = -5;
}
else{
inc = 5;
}
// 以下、本当は増値/減値に際して値チェックが必要です
t.barCielRight
= new Point2D.Float(t.barCielRight.x, t.barCielRight.y + inc);
t.barCielLeft
= new Point2D.Float(t.barCielLeft.x, t.barCielLeft.y + inc);
t.bar = t.makeBar
(t.barBottomLeft, t.barBottomRight, t.barCielRight, t.barCielLeft);
t.repaint();
}
}
}
--------------------------------------------------
お礼
ここまで作成して頂き、本当にありがとうございます。 実行してみたら、やりたかったことが全て実現されていました。 透明属性も付けたかったので、BufferedImageを使う方法も教えて頂けて良かったです。