`
wubin123456
  • 浏览: 6032 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
文章分类
社区版块
存档分类
最新评论

JAVA中的控件重绘

    博客分类:
  • 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);

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

分享到:
评论

相关推荐

    java 日历控件 源码

    5. 更新组件时,通常需要调用`repaint()`方法来触发重绘。 在提供的压缩包中,`mycalendar`可能是包含这个自定义日历控件源码的文件或目录。为了深入理解其工作原理,你需要打开这个文件或目录,查看源代码,了解它...

    java swt自定义控件

    尽量减少不必要的重绘,合理利用缓存,并确保在适当的地方调用`dispose()`方法释放资源。 总之,Java SWT自定义控件为开发者提供了创造独特用户界面的强大工具。通过理解控件的基本原理、绘图机制、事件处理和布局...

    Java生成自定义控件源代码

    - 考虑性能影响,避免在`paint()`或`paintComponent()`方法中执行昂贵的操作,因为这些方法会在每次组件需要重绘时被调用。 - 使用异步处理来改善用户体验,特别是在涉及I/O操作或计算密集型任务时。 总之,Java...

    java awt应用控件

    这可能涉及到减少不必要的重绘,使用事件委托,以及适时地调整布局策略。 10. **示例代码**:理解和掌握这些知识点最好的方式是通过编写实际的代码示例。创建一个简单的AWT应用程序,添加控件,设置布局,处理事件...

    java 实现控件面板的镜面反射效果

    `RepaintManagerDemo.java` 文件可能是一个示例程序,展示了如何在Swing应用程序中有效地管理重绘操作。Swing的RepaintManager是控制组件何时以及如何重绘的关键类。在这个示例中,它可能展示了如何调整重绘策略,以...

    重绘CheckBox资料

    在IT行业中,重绘Checkbox(复选框)通常是指自定义UI组件,即根据特定需求对标准的系统提供的Checkbox控件进行外观和行为的修改。这涉及到Android或Java GUI编程,因为这两个平台广泛使用Checkbox这样的组件。在此...

    SSTAB完美重绘

    通过对"SSTAB完美重绘"项目的探索,我们可以学习到如何利用源代码级别的控制来改善用户界面,同时也能了解到代码重绘在实际开发中的应用策略。无论是对于个人技能提升,还是团队协作,这样的实践都是宝贵的经验积累...

    java applet 心跳线 控件

    Java Applet 心跳线控件是一种基于Java编程语言的小程序,它被嵌入到HTML网页中,用于在客户端提供动态交互功能。心跳线控件通常用于实时监控系统状态,例如网络连接的活跃性,或者在分布式系统中维持客户端与服务器...

    如何实现重绘

    而在Java的Swing或AWT中,可以使用`repaint`方法来请求组件的重绘。对于Qt这样的C++库,我们可以调用`update`函数达到相同的效果。 在Web开发中,DOM元素的样式更改会触发浏览器的重绘。通过CSS的`will-change`属性...

    基于java的日历控件 Click Calendar.zip

    9. **性能优化**: 对于大量数据或频繁更新的情况,需要考虑性能优化,例如缓存计算结果,避免不必要的重绘。 10. **测试与调试**: 在开发过程中,使用JUnit或其他测试框架进行单元测试和集成测试,确保控件在各种...

    自绘带滚动条的表格控件

    5. 性能优化:避免无谓的重绘,比如只重绘改变的部分,以及使用适当的缓存策略。 总的来说,自绘带滚动条的表格控件是一个涉及图形编程、事件处理和数据管理的复杂任务。它需要开发者具备扎实的编程基础,对图形...

    java源码包---java 源码 大量 实例

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    按钮重绘,背景拉伸

    "按钮重绘,背景拉伸"这一主题涉及到图形用户界面(GUI)开发中的两个关键技术点:自定义控件的绘制和图像资源的适配。下面我们将深入探讨这两个方面。 首先,我们来讨论“按钮重绘”。在许多编程环境中,如Java的...

    闪烁窗体上的控件

    在Windows操作系统中,窗体和控件的绘制是通过消息机制进行的,其中WM_PAINT消息用于指示窗口需要重绘。如果控件在短时间内频繁接收到WM_PAINT消息,就可能导致闪烁。因此,控制重绘的频率和正确响应WM_PAINT消息是...

    Android的一款自定义的仪表盘控件

    6. 整合到你的应用:在你的应用中,将`CustomGaugeView`添加到相应的XML布局文件中,并通过Java代码或XML属性设置它的样式和行为。记得在你的Gradle文件中正确地引入这个自定义库。 7. 性能优化:为了保证流畅的...

    android拖动控件,解决回到原点

    // 设置新的布局参数并请求重绘 view.setLayoutParams(layoutParams); ``` 然而,如果父布局是像ListView这样的可滚动容器,那么在下拉刷新或滚动时,其子视图可能会被复用或者重建,这就可能导致之前拖动的位置...

    自定义控件入门集

    为了提高性能,我们应该尽量减少在`onDraw()`中的计算,避免不必要的重绘,并使用`canvas.save()`和`canvas.restore()`来保存和恢复画布的状态,减少绘图操作的开销。另外,合理使用硬件加速和视图复用也能显著提升...

    按钮重绘制作产生波形图的示波器

    利用了按钮重绘的原理,没有用DrawItem(),而是利用Timer,每次间隔时间段的来绘制,程序利用了缓冲绘图技术,先在内存中绘制好,然后再将这张图贴上去,避免了我们经常遇到绘图闪烁的问题,控件产生了向左移动的...

    Android使用系统控件组合成新的自定义控件

    可以使用`invalidate()`方法来触发重绘,但应谨慎使用,以防止过度绘制。 3. **考虑多语言和多尺寸支持**:确保自定义控件能够适应不同语言和屏幕尺寸。 4. **使用Kotlin**:Kotlin提供了一些方便的特性,如扩展...

Global site tag (gtag.js) - Google Analytics