【Swing】JPanel重写paint()后按钮被遮挡,点击按钮键盘监听失效
效果图
内容说明
上图是我们小组做的泡泡堂,在接近项目结尾时,我接手一个小任务,就是在游戏进行界面底部增加截图中的3个按钮,并实现部分功能(有些按钮的功能组员已经实现了,我只需要调用他们的方法就可以了)。下面,我来将遇到的问题抽离出来,记录到博客。我相信应该也有同学会遇到这些问题。
1、如何布局能更好的将按钮组放在面板的底部居中位置?并且看起来像悬浮在地图之上。
2、由于面板中复写了paint()方法,导致按钮组被遮挡。但是当鼠标移上去,按钮组又可以显现出来。
3、点击底部任意一个按钮后,键盘监听失效。按键盘,无法操作游戏中的人物。
问题1
其实我对于Java Swing的布局也不是太熟悉,并且感觉不太好用。要实现上述布局效果,可以利用布局嵌套。
(1)首先,按钮组(可以是多个按钮),无论宽度多少,都居中显示,我们可以想到FlowLayout.CENTER。
(2)那么如何将按钮组布局到底部呢?显然,我们可以将这多个按钮放到一个JPanel(可命名为:btnPanel)中,然后将该面板利用BorderLayout.SOUTH放到底部。
(3)又有一个问题,我们想要看到的想过是按钮组像是悬浮在游戏地图之上。但是,放按钮组的btnPanel的面板是灰色的,它会遮挡游戏地图。那么,我们可以通过setOpaque(false)将面板背景设置为透明,这样只有按钮是显示的,背景是透明的。
下面,给出具体代码
// 布局嵌套。BorderLayout.SOUTH和FlowLayout.CENTER)实现按钮组底部居中
// 游戏面板的布局
this.setLayout(new BorderLayout());
// 按钮组面板
JPanel btnPanel = new JPanel();
btnPanel.setOpaque(false); // 将面板设置为透明,否则灰色面板会遮挡地图
btnPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
bgmBtn = new JButton("开启BGM");
runBtn = new JButton("暂停游戏");
overBtn = new JButton("结束游戏");
btnPanel.add(bgmBtn);
btnPanel.add(runBtn);
btnPanel.add(overBtn);
// 将按钮组面板,放置到游戏面板的南部
this.add(btnPanel, BorderLayout.SOUTH);
问题2
经测试,有两种解决方法。
(1)重写paint()方法时,最后加上super.paintChildren(g),重写组件。
(2)不要重写paint()方法,而是直接重写paintComponent()方法
下面我已第(1)种解决方法,给出代码
/**
* 重写绘画方法。绘画时是有固定的顺序,先绘画的图片会在底层,后绘画的图片会覆盖先绘画的
* 约定:本方法只执行一次,想实时刷新需要使用多线程
*/
@Override
public void paint(Graphics g) {
super.paint(g);
/*
// 自己的代码......
// 所有元素的显示
Map<GameElement, List<ElementObj>> all = em.getGameElements();
// GameElement.values()是隐藏方法,无法点进去
// 返回的数组的顺序时是枚举变量声明时的顺序
for (GameElement ge : GameElement.values()) {
List<ElementObj> list = all.get(ge);
// if(ge.equals(GameElement.MAPS)) {
//// if(ge == GameElement.MAPS) {
// Collections.sort(list);
// }
for (int i = 0; i < list.size(); i++) {
ElementObj obj = list.get(i);
obj.showElement(g);
}
}
*/
// !!!重新绘制子组件,否则paint之后会遮挡住组件!!!
super.paintChildren(g);
}
问题3
显然,键盘监听是用来操作游戏中的人物的。那么在游戏进行过程中,点击了底部按钮,键盘监听为什么会失效?
因为,我们的键盘监听是作用在窗体上(JFrame),当点击底部按钮,窗体失去了焦点,焦点落在了按钮上。所以键盘会失效。所以,只需要通过requestFocus()让窗体重新获得焦点即可。
代码如何操作呢?在按钮的点击事件中,在完成了点击事件的所以逻辑之后,拿到当前窗体的实例对象,调用它的requestFocus()让它重新获得焦点即可。
代码比较简单,我就不把我项目中的代码贴出来了。
本文地址:https://blog.csdn.net/qq_43290318/article/details/107612241