JOGL中myCanvas.addKeyListener(this)报错的原因咨询
问题:JOGL中调用
myCanvas.addKeyListener(this)抛出类型不匹配错误 错误提示:
The method addKeyListener(KeyListener) in the type Component is not applicable for the arguments (Code)
用户需求:通过上下左右箭头控制立方体绕水平/垂直轴旋转,实现KeyListener接口后添加监听器时触发上述错误,完整代码如下:
import java.io.*; import java.lang.Math; import java.nio.*; import javax.swing.*; import static com.jogamp.opengl.GL4.*; import com.jogamp.opengl.*; import com.jogamp.opengl.awt.GLCanvas; import com.jogamp.opengl.util.texture.*; import com.jogamp.common.nio.Buffers; import com.jogamp.newt.event.KeyListener; import org.joml.*; import java.awt.event.KeyEvent; public class Code extends JFrame implements GLEventListener,KeyListener { private GLCanvas myCanvas; private int renderingProgram; private int vao[] = new int[1]; private int vbo[] = new int[3]; private float cameraX, cameraY, cameraZ; private float objLocX, objLocY, objLocZ; // allocate variables for display() function private FloatBuffer vals = Buffers.newDirectFloatBuffer(16); private Matrix4f pMat = new Matrix4f(); // perspective matrix private Matrix4f vMat = new Matrix4f(); // view matrix private Matrix4f mMat = new Matrix4f(); // model matrix private Matrix4f mvMat = new Matrix4f(); // model-view matrix private int mvLoc, pLoc; private float aspect; private int shuttleTexture; private int rotateX = 0,rotateY = 0; private int numObjVertices; private ImportedModel myModel; public Code() { setTitle("Chapter6 - program3"); setSize(600, 600); myCanvas = new GLCanvas(); myCanvas.addKeyListener(this); //this line throws an error. myCanvas.addGLEventListener(this); this.add(myCanvas); myCanvas.setFocusable(true); this.setVisible(true); } public void display(GLAutoDrawable drawable) { GL4 gl = (GL4) GLContext.getCurrentGL(); gl.glClear(GL_COLOR_BUFFER_BIT); gl.glClear(GL_DEPTH_BUFFER_BIT); gl.glUseProgram(renderingProgram); int mvLoc = gl.glGetUniformLocation(renderingProgram, "mv_matrix"); int pLoc = gl.glGetUniformLocation(renderingProgram, "p_matrix"); vMat.identity().setTranslation(-cameraX,-cameraY,-cameraZ); mMat.identity(); mMat.translate(objLocX, objLocY, objLocZ); mMat.rotateX((float)Math.toRadians(20.0f)); mMat.rotateY((float)Math.toRadians(130.0f)); mMat.rotateZ((float)Math.toRadians(5.0f)); mvMat.identity(); mvMat.mul(vMat); mvMat.mul(mMat); gl.glUniformMatrix4fv(mvLoc, 1, false, mvMat.get(vals)); gl.glUniformMatrix4fv(pLoc, 1, false, pMat.get(vals)); gl.glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); gl.glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); gl.glEnableVertexAttribArray(0); gl.glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); gl.glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0); gl.glEnableVertexAttribArray(1); gl.glActiveTexture(GL_TEXTURE0); gl.glBindTexture(GL_TEXTURE_2D, shuttleTexture); gl.glEnable(GL_DEPTH_TEST); gl.glDepthFunc(GL_LEQUAL); gl.glDrawArrays(GL_TRIANGLES, 0, myModel.getNumVertices()); } public void init(GLAutoDrawable drawable) { GL4 gl = (GL4) GLContext.getCurrentGL(); myModel = new ImportedModel("cube1.obj"); renderingProgram = Utils.createShaderProgram("src/code/vertShader.glsl", "src/code/fragShader.glsl"); float aspect = (float) myCanvas.getWidth() / (float) myCanvas.getHeight(); pMat.identity().setPerspective((float) Math.toRadians(60.0f), aspect, 0.1f, 1000.0f); setupVertices(); cameraX = 0.0f; cameraY = 0.0f; cameraZ = 3.0f; objLocX = 0.0f; objLocY = 0.0f; objLocZ = 0.0f; shuttleTexture = Utils.loadTexture("Material5.jpg"); } private void setupVertices() { GL4 gl = (GL4) GLContext.getCurrentGL(); numObjVertices = myModel.getNumVertices(); Vector3f[] vertices = myModel.getVertices(); Vector2f[] texCoords = myModel.getTexCoords(); Vector3f[] normals = myModel.getNormals(); float[] pvalues = new float[numObjVertices*3]; float[] tvalues = new float[numObjVertices*2]; float[] nvalues = new float[numObjVertices*3]; for (int i=0; i<numObjVertices; i++) { pvalues[i*3] = (float) (vertices[i]).x() + rotateX; pvalues[i*3+1] = (float) (vertices[i]).y() + rotateY; pvalues[i*3+2] = (float) (vertices[i]).z(); tvalues[i*2] = (float) (texCoords[i]).x(); tvalues[i*2+1] = (float) (texCoords[i]).y(); nvalues[i*3] = (float) (normals[i]).x(); nvalues[i*3+1] = (float) (normals[i]).y(); nvalues[i*3+2] = (float) (normals[i]).z(); } gl.glGenVertexArrays(vao.length, vao, 0); gl.glBindVertexArray(vao[0]); gl.glGenBuffers(vbo.length, vbo, 0); gl.glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); FloatBuffer vertBuf = Buffers.newDirectFloatBuffer(pvalues); gl.glBufferData(GL_ARRAY_BUFFER, vertBuf.limit()*4, vertBuf, GL_STATIC_DRAW); gl.glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); FloatBuffer texBuf = Buffers.newDirectFloatBuffer(tvalues); gl.glBufferData(GL_ARRAY_BUFFER, texBuf.limit()*4, texBuf, GL_STATIC_DRAW); gl.glBindBuffer(GL_ARRAY_BUFFER, vbo[2]); FloatBuffer norBuf = Buffers.newDirectFloatBuffer(nvalues); gl.glBufferData(GL_ARRAY_BUFFER, norBuf.limit()*4,norBuf, GL_STATIC_DRAW); } public void keyTyped(KeyEvent e) {} public static void main(String[] args) { new Code();} public void dispose(GLAutoDrawable drawable) {} public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { float aspect = (float) myCanvas.getWidth() / (float) myCanvas.getHeight(); pMat.identity().setPerspective((float) Math.toRadians(60.0f), aspect, 0.1f, 1000.0f); } public void keyPressed(com.jogamp.newt.event.KeyEvent arg0) { // TODO Auto-generated method stub int key = arg0.getKeyCode(); System.out.println(arg0.getKeyCode()); if ( key == KeyEvent.VK_LEFT ) { rotateX +=2; System.out.println("Left arrow is pressed"); } else if ( key == KeyEvent.VK_RIGHT ) rotateY += 2; else if ( key == KeyEvent.VK_DOWN) rotateX += 2; else if ( key == KeyEvent.VK_UP ) rotateX -= 2; else if ( key == KeyEvent.VK_HOME ) rotateX = rotateY = 0; } public void keyReleased(com.jogamp.newt.event.KeyEvent arg0) {} }
原因分析
核心问题是监听器类型不兼容:
- 你导入的是
com.jogamp.newt.event.KeyListener(JOGL NEWT框架的键盘监听器),但GLCanvas是AWT组件,它的addKeyListener方法要求传入的是AWT标准的java.awt.event.KeyListener,两者属于不同的UI工具链,类型无法匹配。 - 代码还混合了两种
KeyEvent(AWT和NEWT),会导致后续按键逻辑的混淆。
解决方法
方案1:改用AWT标准KeyListener
完全适配AWT组件体系:
- 替换监听器导入:
// 删除 import com.jogamp.newt.event.KeyListener; import java.awt.event.KeyListener; - 统一使用AWT的
KeyEvent,修改按键方法的参数类型:@Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { rotateX += 2; } else if (key == KeyEvent.VK_RIGHT) { rotateY += 2; } else if (key == KeyEvent.VK_DOWN) { rotateX += 2; } else if (key == KeyEvent.VK_UP) { rotateX -= 2; } else if (key == KeyEvent.VK_HOME) { rotateX = rotateY = 0; } } @Override public void keyReleased(KeyEvent e) {} - 保留
keyTyped方法的AWT参数类型即可。
方案2:改用NEWT组件替代GLCanvas
如果想使用NEWT的监听器体系(JOGL推荐的跨平台方案),替换AWT的GLCanvas为NEWT的GLWindow:
- 导入NEWT窗口类:
import com.jogamp.newt.opengl.GLWindow; - 修改窗口初始化代码:
private GLWindow myWindow; public Code() { setTitle("Chapter6 - program3"); GLProfile profile = GLProfile.get(GLProfile.GL4); GLCapabilities capabilities = new GLCapabilities(profile); myWindow = GLWindow.create(capabilities); myWindow.addKeyListener(this); // 此时NEWT监听器可正常添加 myWindow.addGLEventListener(this); myWindow.setSize(600, 600); myWindow.setVisible(true); } - 原有的NEWT
KeyListener和KeyEvent方法无需修改。
额外优化提示
当前代码中修改rotateX/rotateY后不会实时更新画面,因为顶点数据只在初始化时生成一次。需要把旋转逻辑移到display方法的模型矩阵中:
public void display(GLAutoDrawable drawable) { // ... 其他代码不变 ... mMat.identity(); mMat.translate(objLocX, objLocY, objLocZ); // 应用动态旋转 mMat.rotateX((float)Math.toRadians(rotateX)); mMat.rotateY((float)Math.toRadians(rotateY)); // 保留原有固定旋转(如果需要) mMat.rotateX((float)Math.toRadians(20.0f)); mMat.rotateY((float)Math.toRadians(130.0f)); mMat.rotateZ((float)Math.toRadians(5.0f)); // ... 其他代码不变 ... }
内容的提问来源于stack exchange,提问作者DRV
相关产品推荐
相关产品推荐

