图片  
800*600 or above for best view
·Anyview0.3版字库制作…
·Anyview0.2源程序
·J2ME版本连连看源程序
·PC版连连看游戏
·PNG研究:修改调色板
·表单探测密码
 
本站所有的文章、程序和代码,如果无特殊说明,可以在保留作者和出处的情况下不受限制的传播或转载。
 
 
邮件:leftmoon#163.com(将#换为@)

JAVA中的控件重绘(已阅读 次)

众所周知,使用 JAVA 开发出来的应用程序在各种平台上有着相同的用户界面,这一切得归功于 SWING 良好的跨平台性(少数 AWT 控件在不同的平台上有着极为细小的差别)。并且, JAVA 还提供了 Look&Feel 和 Theme 让开发者对用户界面上的控件进行观感上的改变,这样可以做出更多更漂亮的用户界面。然而,当你仅仅是需要对某个控件进行特别的观感设置时,使用 L&F 或 Theme 可能过于复杂,也使得程序变得比较“庞大”,因此,我们可以通过控件重绘来完成这部分特殊的要求。

javax.swing.JComponent 是所有 SWING 控件的“祖宗”,在 JComponent 中, paintComponent(Graphics g) 方法是用来进行绘制控件的,因此,如果想要重绘 SWING 控件,只需要覆盖 paintComponent(Graphics g) 方法就可以了。如:

public class CubeMenuBar extends JMenuBar {

void paintComponent(Graphics g) {

// 这里写重绘代码

}

}

下面,我们将以例子来实现控件的重绘,最终结果如下图:

首先,我们按照上图制作一个简单的小程序,相信这个不是一件太难的事情:

//TestMenu.java

public class TestMenu extends JFrame {

JMenuBar cmbMenu = new JMenuBar();

JMenu mFile = new JMenu();

JMenu mEdit = new JMenu();

JMenu mSource = new JMenu();

JMenuItem miNew = new JMenuItem();

JMenuItem miOpen = new JMenuItem();

JMenuItem miSave = new JMenuItem();

JMenuItem miClose = new JMenuItem();

JTextArea taEditor = new JTextArea();

 

public TestMenu() {

// 创建布局

this.getContentPane().setLayout(new BorderLayout());

 

// 添加菜单

this.setJMenuBar(cmbMenu);

mFile.setText("File");

cmbMenu.add(mFile);

miNew.setText("New");

mFile.add(miNew);

miOpen.setText("Open");

mFile.add(miOpen);

miSave.setText("Save");

mFile.add(miSave);

miClose.setText("Close");

mFile.add(miClose);

 

miClose.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {

dispose();

System.exit(0);

}

});

 

mEdit.setText("Edit");

cmbMenu.add(mEdit);

 

mSource.setText("Source");

cmbMenu.add(mSource);

 

// 添加编辑区域

JScrollPane sp = new JScrollPane(taEditor);

this.getContentPane().add(sp, BorderLayout.CENTER);

 

this.setTitle("Notepad - JAVA");

this.setSize(new Dimension(400, 300));

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

this.show();

}

 

public static void main(String[] args) {

//JFrame.setDefaultLookAndFeelDecorated(true);

TestMenu testmenu = new TestMenu();

}

}

( 程序运行如上图 )

为了实现特殊的菜单栏,我们需要自己重写 JMenu 类,假设我们的类叫作 CubeMenuBar ,那么,我们只需要将 TestMenu.java 中的

JMenuBar cmbMenu = new JMenuBar();

改为

CubeMenuBar cmbMenu = new CubeMenuBar();

现在,我们看一看最简单的 CubeMenuBar 应该怎么写:

// CubeMenuBar.java

public class CubeMenuBar extends JMenuBar {

protected final void paintComponent(Graphics g) {

super.paintComponent(g);

}

}

我们将 paintComponent(Graphics g) 申明为 protected final 是不愿继承的类再次改写重绘方法, super.paintComponent(g); 调用的父类的方法来进行重绘 MenuBar ,如果你写掉了这一句,呵呵,自己看看运行结果。

 

由于我们在这里只是研究如何重绘控件,因此,我提供一个现成的类,这个类负责生成带有过度色块的图像,具体不多说,只帖出代码:

// ImageCreator.java

public class ImageCreator {

// 定义颜色

public static final Color mainMidColor = new Color(0, 64, 196);

public static final Color mainUltraDarkColor = new Color(0, 0, 64);

// 定义色块数量 ( 高度 )

public static final int CUBE_DIMENSION = 5;

 

public ImageCreator() {

}

 

/**

* 产生过度色块图像

* @param width 图像的宽

* @param height 图像的高

* @param leftColor 色块左侧颜色

* @param rightColor 色块结束的颜色

* @param transitionStart 过度色块开始位置

* @param transitionEnd 过度色块结束位置

* @return 创建好的图像

*/

public static BufferedImage getGradientCubesImage(int width, int height,

Color leftColor, Color rightColor, int transitionStart,

int transitionEnd) {

BufferedImage image = new BufferedImage(width, height,

BufferedImage.TYPE_INT_ARGB);

 

Graphics2D graphics = (Graphics2D) image.getGraphics();

graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

 

GradientPaint gradient = new GradientPaint(transitionStart, 0,

leftColor, transitionEnd, 0, rightColor);

graphics.setPaint(gradient);

graphics.fillRect(transitionStart, 0, transitionEnd - transitionStart,

height);

 

graphics.setColor(leftColor);

graphics.fillRect(0, 0, transitionStart, height);

 

graphics.setColor(rightColor);

graphics.fillRect(transitionEnd, 0, width - transitionEnd, height);

 

int cubeCountY = height / ImageCreator.CUBE_DIMENSION;

int cubeCountX = 1 + (transitionEnd - transitionStart)

/ ImageCreator.CUBE_DIMENSION;

int cubeStartY = (height % ImageCreator.CUBE_DIMENSION) / 2;

int cubeStartX = transitionStart

- (ImageCreator.CUBE_DIMENSION - ((transitionEnd - transitionStart) % ImageCreator.CUBE_DIMENSION));

 

for (int col = 0; col < cubeCountX; col++) {

for (int row = 0; row < cubeCountY; row++) {

// 随机放置色块

if (Math.random() < 0.5) {

continue;

}

 

// 使用插值方法产生颜色,结果看起来和随机产生的差不多

double coef = 1.0 - (((double) col / (double) cubeCountX) + 0.9 * (Math

.random() - 0.5));

coef = Math.max(0.0, coef);

coef = Math.min(1.0, coef);

// 计算 RGB

int r = (int) (coef * leftColor.getRed() + (1.0 - coef)

* rightColor.getRed());

int g = (int) (coef * leftColor.getGreen() + (1.0 - coef)

* rightColor.getGreen());

int b = (int) (coef * leftColor.getBlue() + (1.0 - coef)

* rightColor.getBlue());

// 填充色块

graphics.setColor(new Color(r, g, b));

graphics.fillRect(cubeStartX + col

* ImageCreator.CUBE_DIMENSION, cubeStartY + row

* ImageCreator.CUBE_DIMENSION,

ImageCreator.CUBE_DIMENSION,

ImageCreator.CUBE_DIMENSION);

// 绘置色块边框

graphics.setColor(new Color(255 - (int) (0.95 * (255 - r)),

255 - (int) (0.9 * (255 - g)),

255 - (int) (0.9 * (255 - b))));

graphics.drawRect(cubeStartX + col

* ImageCreator.CUBE_DIMENSION, cubeStartY + row

* ImageCreator.CUBE_DIMENSION,

ImageCreator.CUBE_DIMENSION,

ImageCreator.CUBE_DIMENSION);

}

}

 

return image;

}

}

现在,我们的 CubeMenuBar.java 应该写为如下:

public class CubeMenuBar extends JMenuBar {

 

protected final void paintComponent(Graphics g) {

super.paintComponent(g);

g.drawImage(ImageCreator.getGradientCubesImage(this.getWidth(),

this.getHeight(), ImageCreator.mainMidColor,

ImageCreator.mainUltraDarkColor, (int) (0.6 * this.getWidth()),

(int) (0.9 * this.getWidth())), 0, 0, null);

}

}

只通过如此简单的几句代码,我们就可以得到了带和过度色块的菜单栏,是不是很有意思呢?

 

同样,我们在这个小程序中对 JMenu 和 JMenuItem 进行重绘,就可以得到最终的结果,然而,这个程序还有一点点小缺陷,当重绘的文字过大的时候,文字的边缘出现的锯齿,这是 Graphics 类的通病,为以,我们可以再做修改:

// CubeMenu.ajva

public class CubeMenu extends JMenu {

 

protected final void paintComponent(Graphics g) {

Graphics2D graphics = (Graphics2D) g;

Object oldHint = graphics

.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING);

graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,

RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

super.paintComponent(graphics);

graphics

.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, oldHint);

 

graphics.setColor(ImageCreator.mainMidColor);

graphics.setBackground(ImageCreator.mainMidColor);

graphics.fillRect(0, 0, this.getWidth(), this.getHeight());

 

int x = (this.getWidth() - graphics.getFontMetrics().stringWidth(

this.getText())) / 4;

int y = (int) (graphics.getFontMetrics().getLineMetrics(this.getText(),

graphics).getHeight());

 

graphics.setColor(Color.black);

graphics.drawString(this.getText(), x + 1, y + 1);

graphics.setColor(Color.white);

graphics.drawString(this.getText(), x, y);

}

}

在上述代码中,我们首先将 Graphics 对象转换为了 Graphics2D 对象,这样可以充分利用 Graphics2D 带来的更多特性。然后使用

Object oldHint = graphics.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING);

将控件原来绘制的渲染方式进行了保存,通过

graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

来为文字添加反锯齿绘制方式,最后再使用

graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, oldHint);

恢复控件原来的绘制方式。

全部代码由此下载(已下载 次)