如何在Java中模拟按媒体键?

8
我该如何在Java中模拟按媒体键?例如播放/暂停、上一曲/下一曲、音量控制等。C#有VK_MEDIA_PLAY_PAUSE,VK_MEDIA_NEXT_TRACK等。Java有Robot类可用于处理按键,但没有媒体键。

http://docs.oracle.com/javase/tutorial/uiswing/events/keylistener.html - Luis Lavieri
1
@Luis 这并没有帮助到楼主。我运行了提供的 KeyListener 演示,但它无法识别我的键盘媒体键。 - CubeJockey
是的,你说得对。在粘贴链接后我也这样做了。我认为在Java中不可能实现: https://dev59.com/KlfUa4cB1Zd3GeqPMecW - Luis Lavieri
OP,这是一个来自2013年的SO帖子。它建议使用_JIntellitype_库:http://stackoverflow.com/questions/16494804/finding-keycode-of-multimedia-key-in-java - CubeJockey
你可以尝试调用一些本地操作系统的功能来实现这个,但这样不够可移植。 - user3079266
1
JNativeHook有一个方法可以将键事件排队到系统中,并支持媒体键。GlobalScreen.postNativeEvent() - J Atkin
4个回答

6
我使用JNI库编写的C代码模拟按键操作。我创建了.dll和.java文件,用于触发"音量减小"、"音量增大"、"静音"、"上一曲"、"下一曲"和"播放/暂停"媒体键。
这里是完整存储库的链接,但我将在下面更详细地解释它。
MediaKeys.java必须位于名为"commands"的包中才能正常工作。
当编译时,MediaKeys.dll必须与"src"文件夹相同路径或与.class文件相同路径。
MediaKeys.java文件包含以下内容:
package commands

public class MediaKeys {

    //loads library from "MediaKeys.dll"
    static {
        System.loadLibrary("MediaKeys");
    }



    public static native void volumeMute();

    public static native void volumeDown();

    public static native void volumeUp();


    public static native void songPrevious();

    public static native void songNext();

    public static native void songPlayPause();



    //test driver
    public static void main(String[] args) {

        //volumeMute();

    }

}

静态块加载.dll文件,然后使用native关键字声明用C编程的函数。
如果只需要这些函数,则可以使用Windows的.dll文件。如果您需要.dll的源代码,则在上面的link中包含了它,并且我将在下面更详细地解释它。
.dll由两个文件制成,一个是用于函数源代码的C文件,另一个是头文件。 (名为MediaKeys.c和MediaKeys.h)
MediaKeys.c包含按所需键的代码。 为了节省空间,以下代码块仅针对“下一曲目”,“上一曲目”和“暂停/播放曲目”功能格式化C和头文件。
头文件: MediaKeys.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MediaKeys */

#ifndef _Included_MediaKeys
#define _Included_MediaKeys
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     MediaKeys
 * Method:    songPrevious
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_commands_MediaKeys_songPrevious
  (JNIEnv *, jclass);

/*
 * Class:     MediaKeys
 * Method:    songNext
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_commands_MediaKeys_songNext
  (JNIEnv *, jclass);

/*
 * Class:     MediaKeys
 * Method:    songPlayPause
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_commands_MediaKeys_songPlayPause
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

头文件包含每个所需方法的语句,格式如下:
JNIEXPORT void JNICALL Java_{package_name}_{class_name}_{method_name}
  (JNIEnv *, jclass);

C文件必须与头文件对应。MediaKeys.c
//standard dependencies for C and the JNI Library
#include <jni.h>
#include <stdio.h>
#include "MediaKeys.h"

//dependencies required to hit the media keys
#define WINVER 0x0500
#include <windows.h>


//hits the previous track key
JNIEXPORT void JNICALL Java_commands_MediaKeys_songPrevious (JNIEnv *env, jobject thisObj) {

    KEYBDINPUT kbi;

    //specific keycode
    kbi.wVk = VK_MEDIA_PREV_TRACK; //this can be changed depending on the key

    kbi.wScan = 0;
    kbi.dwFlags = 0;
    kbi.time = 0;
    kbi.dwExtraInfo = (ULONG_PTR) GetMessageExtraInfo();

    INPUT input;
    input.type = INPUT_KEYBOARD;
    input.ki   = kbi;

    SendInput(1, &input, sizeof(INPUT));

    return;

}


//hits the next track key
JNIEXPORT void JNICALL Java_commands_MediaKeys_songNext (JNIEnv *env, jobject thisObj) {

    KEYBDINPUT kbi;

    //specific keycode
    kbi.wVk = VK_MEDIA_NEXT_TRACK;

    kbi.wScan = 0;
    kbi.dwFlags = 0;
    kbi.time = 0;
    kbi.dwExtraInfo = (ULONG_PTR) GetMessageExtraInfo();

    INPUT input;
    input.type = INPUT_KEYBOARD;
    input.ki   = kbi;

    SendInput(1, &input, sizeof(INPUT));

    return;

}


//hits the play/pause key
JNIEXPORT void JNICALL Java_commands_MediaKeys_songPlayPause (JNIEnv *env, jobject thisObj) {

    KEYBDINPUT kbi;

    //specific keycode
    kbi.wVk = VK_MEDIA_PLAY_PAUSE;

    kbi.wScan = 0;
    kbi.dwFlags = 0;
    kbi.time = 0;
    kbi.dwExtraInfo = (ULONG_PTR) GetMessageExtraInfo();

    INPUT input;
    input.type = INPUT_KEYBOARD;
    input.ki   = kbi;

    SendInput(1, &input, sizeof(INPUT));

    return;

}


C文件包含与每个头文件语句对应的函数,格式如下:
JNIEXPORT void JNICALL Java_{package_name}_{class_name}_{method_name} (JNIEnv *env, jobject thisObj) {

    //specific code goes here
    return;

}

正如代码中所述,您可以通过更改kbi.wVk = specific_key_goes_here;来更改特定的键。 可以在这里找到可用键的列表。

创建C和头文件后,它们可以编译为dll文件。 为此,我使用Code :: Blocks创建了一个新的动态链接库项目,添加了MediaKeys.c和MediaKeys.h文件,然后点击构建。

由于我的JVM是64位的,而Code :: Blocks默认的编译器是32位的,因此我需要安装一个64位编译器到Code :: Blocks中。

你还需要添加链接到jni.h库。在Code::Blocks中,转到设置>编译器>搜索目录并添加目录C:\Program Files\Java\jdk1.8.0_171\includeC:\Program Files\Java\jdk1.8.0_171\include\win32。你可能需要根据jdk版本更改文件路径。
构建完成后,将dll文件复制到java程序所需的位置。
有关设置Java Native Interface的更多信息,请参阅this link
我知道这篇文章有点老,但我认为这些信息可能会帮助其他人。

1
你可能需要在这里添加代码,而不是提供链接。 - Romil Patel
您在此处提供了完全相同的答案(链接:https://stackoverflow.com/a/55321045/2311167)。显然,关键部分(MediaKeys.dll)没有源代码,而且这样的DLL只能在Windows下工作。 - Adrian W
谢谢您的帮助,我会适当地更新我的答案。 - nsnave
1
这是一个真正的、棒极了的答案。终于有了! - Ky -

2
你可以使用https://github.com/kwhat/jnativehook实现此功能,然后监听按键。在我的问题中,我写了一些示例方法:
public static void MediaKeyForward(){
    GlobalScreen.postNativeEvent(new NativeKeyEvent(2401,0,176,57369,org.jnativehook.keyboard.NativeKeyEvent.CHAR_UNDEFINED));

}
public static void MediaKeyBack(){
    GlobalScreen.postNativeEvent(new NativeKeyEvent(2401,0,177,57360,org.jnativehook.keyboard.NativeKeyEvent.CHAR_UNDEFINED));

}
public static void MediaKeyPause(){
 GlobalScreen.postNativeEvent(new NativeKeyEvent(2401,0,179,57378,org.jnativehook.keyboard.NativeKeyEvent.CHAR_UNDEFINED));

}

您只需将该库包含到您的项目中即可。如何监视键并获取创建键事件所需的参数的示例可以在此处找到。


1
创建自己的键盘监听器并监视任何输入,然后使用该值。以下是简单的KeySpy类:
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JLabel;


public class KeySpy {
    JLabel label=new JLabel("Enter the key");
    public KeySpy() {
        JFrame frame=new JFrame("KeySpy");
        frame.add(label);

        frame.addKeyListener(new KeyListener() {

            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
            }

            @Override
            public void keyPressed(KeyEvent e) {
                label.setText(e.toString());
                System.out.println(e.toString());
            }
        });

        frame.setSize(200, 200);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new KeySpy();

    }

}

这是我键盘上2个按钮的结果

   [Stop] = java.awt.event.KeyEvent[KEY_PRESSED,keyCode=0,keyText=Unknown keyCode: 0x0,keyChar=Undefined keyChar,keyLocation=KEY_LOCATION_STANDARD,rawCode=178,primaryLevelUnicode=0,scancode=36,extendedKeyCode=0x0] on frame0

   [Mute] = java.awt.event.KeyEvent[KEY_PRESSED,keyCode=0,keyText=Unknown keyCode: 0x0,keyChar=Undefined keyChar,keyLocation=KEY_LOCATION_STANDARD,rawCode=173,primaryLevelUnicode=0,scancode=32,extendedKeyCode=0x0] on frame0

正如您所看到的,它们没有keyCode,但是它们有rawCode-因此请使用它。

你如何将这个转化为自动化媒体键? - Ky -

0

我改进了Alex的KeySpy应用程序。JFrame保持焦点,因此您可以最小化或最大化应用程序,并在应用程序处于焦点状态时仍然按任何键。

这是GUI界面。

KeySpy GUI

我将信息放置在标签/值网格中,以便更轻松地查找您感兴趣的值。
以下是代码。这是GridBagLayout的一个很好的示例。
package com.ggl.testing;

import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class KeySpy implements Runnable {

    private KeySpyPanel keySpyPanel;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new KeySpy());
    }

    @Override
    public void run() {
        final JFrame frame = new JFrame("Key Spy");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.addWindowFocusListener(new WindowAdapter() {
            public void windowGainedFocus(WindowEvent e) {
                frame.requestFocusInWindow();
            }
        });

        keySpyPanel = new KeySpyPanel();
        frame.add(keySpyPanel.getPanel());
        frame.addKeyListener(new KeyPressedListener(this));

        frame.pack();
        frame.setVisible(true);
    }

    public KeySpyPanel getKeySpyPanel() {
        return keySpyPanel;
    }

    public class KeySpyPanel {

        private final Insets bottomInsets = new Insets(10, 10, 10, 10);
        private final Insets normalInsets = new Insets(10, 10, 0, 10);

        private JPanel panel;

        private JTextField keyCodeField;
        private JTextField keyTextField;
        private JTextField keyCharField;
        private JTextField keyLocationField;
        private JTextField modifiersField;
        private JTextField extModifiersField;
        private JTextField rawCodeField;
        private JTextField primaryLevelUnicodeField;
        private JTextField scancodeField;
        private JTextField extendedKeyCodeField;

        public KeySpyPanel() {
            createPartControl();
        }

        private void createPartControl() {
            panel = new JPanel();
            panel.setLayout(new GridBagLayout());

            int gridy = 0;

            JLabel anyKeyLabel = new JLabel("Press any key");
            anyKeyLabel.setFont(anyKeyLabel.getFont().deriveFont(36F));
            anyKeyLabel.setHorizontalAlignment(JLabel.CENTER);
            addComponent(panel, anyKeyLabel, 0, gridy++, 2, 1, normalInsets,
                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);

            JLabel keyCodeLabel = new JLabel("KeyCode:");
            addComponent(panel, keyCodeLabel, 0, gridy, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            keyCodeField = new JTextField(20);
            keyCodeField.setEditable(false);
            addComponent(panel, keyCodeField, 1, gridy++, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            JLabel keyTextLabel = new JLabel("KeyText:");
            addComponent(panel, keyTextLabel, 0, gridy, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            keyTextField = new JTextField(20);
            keyTextField.setEditable(false);
            addComponent(panel, keyTextField, 1, gridy++, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            JLabel keyCharLabel = new JLabel("KeyChar:");
            addComponent(panel, keyCharLabel, 0, gridy, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            keyCharField = new JTextField(20);
            keyCharField.setEditable(false);
            addComponent(panel, keyCharField, 1, gridy++, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            JLabel keyLocationLabel = new JLabel("KeyLocation:");
            addComponent(panel, keyLocationLabel, 0, gridy, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            keyLocationField = new JTextField(20);
            keyLocationField.setEditable(false);
            addComponent(panel, keyLocationField, 1, gridy++, 1, 1,
                    normalInsets, GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            JLabel modifiersLabel = new JLabel("Modifiers:");
            addComponent(panel, modifiersLabel, 0, gridy, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            modifiersField = new JTextField(20);
            modifiersField.setEditable(false);
            addComponent(panel, modifiersField, 1, gridy++, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            JLabel extModifiersLabel = new JLabel("ExtModifiers:");
            addComponent(panel, extModifiersLabel, 0, gridy, 1, 1,
                    normalInsets, GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            extModifiersField = new JTextField(20);
            extModifiersField.setEditable(false);
            addComponent(panel, extModifiersField, 1, gridy++, 1, 1,
                    normalInsets, GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            JLabel rawCodeLabel = new JLabel("RawCode:");
            addComponent(panel, rawCodeLabel, 0, gridy, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            rawCodeField = new JTextField(20);
            rawCodeField.setEditable(false);
            addComponent(panel, rawCodeField, 1, gridy++, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            JLabel primaryLevelUnicodeLabel = new JLabel("PrimaryLevelUnicode:");
            addComponent(panel, primaryLevelUnicodeLabel, 0, gridy, 1, 1,
                    normalInsets, GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            primaryLevelUnicodeField = new JTextField(20);
            primaryLevelUnicodeField.setEditable(false);
            addComponent(panel, primaryLevelUnicodeField, 1, gridy++, 1, 1,
                    normalInsets, GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            JLabel scancodeLabel = new JLabel("Scancode:");
            addComponent(panel, scancodeLabel, 0, gridy, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            scancodeField = new JTextField(20);
            scancodeField.setEditable(false);
            addComponent(panel, scancodeField, 1, gridy++, 1, 1, normalInsets,
                    GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            JLabel extendedKeyCodeLabel = new JLabel("ExtendedKeyCode:");
            addComponent(panel, extendedKeyCodeLabel, 0, gridy, 1, 1,
                    bottomInsets, GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);

            extendedKeyCodeField = new JTextField(20);
            extendedKeyCodeField.setEditable(false);
            addComponent(panel, extendedKeyCodeField, 1, gridy++, 1, 1,
                    bottomInsets, GridBagConstraints.LINE_START,
                    GridBagConstraints.HORIZONTAL);
        }

        private void addComponent(Container container, Component component,
                int gridx, int gridy, int gridwidth, int gridheight,
                Insets insets, int anchor, int fill) {
            GridBagConstraints gbc = new GridBagConstraints(gridx, gridy,
                    gridwidth, gridheight, 1.0D, 1.0D, anchor, fill, insets, 0,
                    0);
            container.add(component, gbc);
        }

        public JPanel getPanel() {
            return panel;
        }

        public void setKeyPressed(KeyEvent event) {
            String s = event.toString();

            keyCodeField.setText(getValue("keyCode", s));
            keyTextField.setText(getValue("keyText", s));
            keyCharField.setText(getValue("keyChar", s));
            keyLocationField.setText(getValue("keyLocation", s));
            modifiersField.setText(getValue("modifiers", s));
            extModifiersField.setText(getValue("extModifiers", s));
            rawCodeField.setText(getValue("rawCode", s));
            primaryLevelUnicodeField
                    .setText(getValue("primaryLevelUnicode", s));
            scancodeField.setText(getValue("scancode", s));
            extendedKeyCodeField.setText(getValue("extendedKeyCode", s));
        }

        private String getValue(String key, String line) {
            int sPos = line.indexOf(key);
            if (sPos >= 0) {
                int nPos = sPos + key.length() + 1;
                int ePos = line.indexOf(",", nPos);
                if (ePos < 0) {
                    ePos = line.indexOf("]", nPos);
                }
                if (ePos >= 0) {
                    return line.substring(nPos, ePos);
                }
            }

            return "";
        }

    }

    public class KeyPressedListener extends KeyAdapter {

        private KeySpy keySpyFrame;

        public KeyPressedListener(KeySpy keySpyFrame) {
            this.keySpyFrame = keySpyFrame;
        }

        @Override
        public void keyPressed(KeyEvent event) {
            keySpyFrame.getKeySpyPanel().setKeyPressed(event);
        }
    }

}

这似乎完全没有回答问题。 - Ky -

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