合成器外观和感觉中的默认按钮输入映射?

4

我正在尝试使用UIManager获取并清除一些默认的按键绑定,以便空格键不会激活我的JButtons,就像这里所解释的那样。问题是,由于我的外观和感觉,(InputMap)UIManager.get("Button.focusInputMap");返回了一个null。有没有人知道一种更容易清除组件输入映射的方法,或者为什么在这种情况下UIManager返回null?任何提示都将不胜感激,提前致谢。


workforme(nimbus,jdk6/7)- 那么你的LAF是什么?看起来它有些问题... - kleopatra
返回键:不起作用;-) inputMap 不为 null,但在其中替换按下/释放绑定 并不能 防止 Nimbus 中的空格触发按钮(就像在 win 和 metal 中一样)。 - kleopatra
2个回答

4

首先,我喜欢另一位回答者@David提出的装饰StyleFactory的想法 :-) - 如果这种方法可行,建议朝着这个方向发展。

无论如何,我忍不住做了一些尝试:看起来Nimbus(以及可能其他基于Synth的LAF)需要在生命周期的早期进行默认值覆盖:只有在任何组件实际创建之前完成才能接受它们。

// setting LAF
InteractiveTestCase.setLAF("Nimbus");
// tweak inputMap, immediately after setting the ui is fine
// uncomment the following line and it doesn't work
// new JPanel();
InputMap inputMap = (InputMap) UIManager.get("Button.focusInputMap");
inputMap.put(KeyStroke.getKeyStroke("SPACE"), "do-nothing");

如果UIManager在那个时候没有返回inputMap,我会认为这是您自定义LAF的不当行为,并尝试进一步了解为什么会发生这种情况。另一件事情是,您可以尝试设置一个全新的inputMap(它具有生存于LAF切换之外的优点,因为它不是像UIResource一样的资源),例如:
// setting LAF
InteractiveTestCase.setLAF("Nimbus");
// tweak inputMap, immediately after setting the ui is fine
InputMap inputMap = (InputMap) UIManager.get("Button.focusInputMap");
InputMap custom = new InputMap();
if (inputMap != null) {
    // copy all bindings to custom
    ...
} else {
    // add the binding we know of (as implementation detail)
    custom.put(KeyStroke.getKeyStroke("released SPACE"), "released");
}
// overwrite the binding you want to change
custom.put(KeyStroke.getKeyStroke("SPACE"), "do-nothing");
// set the custom map
UIManager.put("Button.focusInputMap", custom);

2
很奇怪,我的之前的观察在Mac OS X上使用com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel运行正常。 - trashgod
+1, 1. UIManager.getLookAndFeelDefaults().put("Xxx", "Xxx"),2. 对于 JButton 不确定,因为我尝试过 JMenu(Item),只有在卸载了 Nimbus L&F 的情况下才能正常工作...,3. 在这种情况下,我使用了 putClientProperty("Xxx", "Xxx")ButtonModel - mKorbel
@mKorbel 1) 没有任何区别 2) 好吧,我尝试了很多排列组合 - 这是唯一有效的 :-) 3) 不理解,ButtonModel与此无关。 - kleopatra
@trashgod oioioioi ... 又是一次写一次调试到处都要调试的案例;-) 实际上,我认为Nimbus行为(在这里看到的win)是一个bug:看起来它不听 - 或者不够听 - 管理器属性的变化。 - kleopatra

3
我现在没有计算机可以尝试这个,但是从openjdk 7源代码here来看,默认情况下映射看起来是固定的。
一个可能有点 hacky 的解决方案是创建和安装修饰器SynthStyleFactory,并在返回样式之前修改它。
编辑: 我已经更新了下面的代码示例,因为我有机会测试这个。它的原始形式不能工作,但更新后的代码对我有用。
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.synth.Region;
import javax.swing.plaf.synth.SynthLookAndFeel;
import javax.swing.plaf.synth.SynthStyle;
import javax.swing.plaf.synth.SynthStyleFactory;

import sun.swing.plaf.synth.DefaultSynthStyle;

public class LnFTest {

    public static void main(String[] args) throws UnsupportedLookAndFeelException{

        SynthLookAndFeel laf = new SynthLookAndFeel();
        laf.load(LnFTest.class.getResourceAsStream("laf.xml"), LnFTest.class);
        UIManager.setLookAndFeel(laf);
        SynthLookAndFeel.setStyleFactory(new MyStyleFactory(SynthLookAndFeel.getStyleFactory()));


        JButton button = new JButton("Test");
        button.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                System.out.println("Action Performed");
            }
        });

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.add(button, BorderLayout.CENTER);
        frame.pack();

        frame.setVisible(true);

    }



}

class MyStyleFactory extends SynthStyleFactory {

    private SynthStyleFactory delegate;
    private Map overrides;

     public MyStyleFactory(SynthStyleFactory delegate){
         this.delegate = delegate;

         overrides = new HashMap();
         overrides.put("Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[0]));
     }

     public SynthStyle getStyle(JComponent c, Region id) {
         SynthStyle style = delegate.getStyle(c, id);

         System.out.println("Style is a: " + style);

         if(style instanceof DefaultSynthStyle){
             ((DefaultSynthStyle)style).setData(overrides);
         }

         return style;

     }
}

编辑: 似乎我无法在原始帖子中添加评论,因此仅澄清一下,在创建任何组件之前,我已确认UIManager.get("Button.focusInputMap")只使用纯净的Synth返回null。可能是Nimbus覆盖了这种行为。

 SynthLookAndFeel laf = new SynthLookAndFeel();
 UIManager.setLookAndFeel(laf);
 System.out.println(UIManager.get("Button.focusInputMap") == null); 

getDefaultValue() 在 SynthStyle 中是静态和私有的,还有其他获取输入映射的想法吗? - SuperTron
翻译:糟糕,我的错。根据我发布的openJDK 7链接,SynthStyle有一个方法public Object get(SynthContext context, Object key),它只调用了那个私有静态方法。它只使用了key。 - David Hutchison
嗯,我一直在尝试这个,但是我不知道该使用什么上下文:S 调用(InputMap)style.get(null, "Button.focusInputMap")会导致空指针异常,即使上下文没有被使用... - SuperTron
1
我知道。从API来看,这似乎是一个奇怪的类。在原始帖子中,由于它是一个解析的LNF,它可能不使用该类,而是使用其子类(ParsedSynthStyle),该子类不在sun包中。 - David Hutchison
有没有想法如何在JDK7中让这个装饰器起作用?似乎他们正在删除DefaultSynthStyle类... - SuperTron
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接