防止JPanel子组件两次显示

时间:2019-12-29 23:31:13

标签: java swing kotlin jpanel

我有一个JFrame,其中包含JPanelJPanel中包含更多组件,例如JLabelJTextFieldJLabelJFileChooser。 这些组件可以正确显示在应有的位置,但它们也应显示在窗口中(0, 0)下方应有的位置。在上方显示的是最后一个关注的对象: (Image) The JFileChooser is displayed as a separate window, but also displayed underneath what is supposed to be displayed at (0, 0)

如何防止这种情况发生?


代码:(省略了一些方法以使代码更短。这些与发布无关。)

package com.kasad.pathfinder.mapmaker.graphics.components

import com.kasad.pathfinder.mapmaker.grid.Node
import java.awt.Color
import java.awt.FlowLayout
import java.awt.Graphics
import java.awt.event.*
import javax.swing.JButton
import javax.swing.JFileChooser
import javax.swing.JLabel
import javax.swing.JPanel
import javax.swing.filechooser.FileNameExtensionFilter

class DisplaySO : JPanel() {
    companion object {
        val WALKABLE_TILE_COLOR: Color = Color.WHITE
        val WALL_TILE_COLOR: Color = Color.BLACK
        val START_TILE_COLOR: Color = Color.GREEN
        val END_TILE_COLOR: Color = Color.RED
        val TILE_BORDER_COLOR: Color = Color.BLACK

        const val GRID_MARGIN: Int = 50
    }

    private var nodes = arrayOf(    // Start with a 3x3 grid of walkable nodes
        ... // Not relevant to issue (omitted)
    )

    // These variables (nodeSize, centerOffset[X/Y], grid[Height/Width]) are referred to as "draw sizes"
    private var nodeSize = 0        // Size of each node
    private var centerOffsetX = 0   // X offset to center grid
    private var centerOffsetY = 0   // Y offset to center grid
    private var gridWidth = 0      // The total width of the grid
    private var gridHeight = 0     // The total height of the grid

    private var previousWidth = 0   // Previous width of window
    private var previousHeight = 0  // Previous height of window

    private var startNode: Node? = null
    private var endNode: Node? = null

    private lateinit var prevMousedNode: Node   // Previous node selected by mouse
    private var currentKeyPressed: Char? = null
    private var currentMouseButton: Int? = null

    private val widthBox = JNumberField(6)  // Create text boxes for grid width & height input
    private val heightBox = JNumberField(6) // |
    private val widthBoxLabel = JLabel("Rows:")     // Text box labels
    private val heightBoxLabel = JLabel("Columns:") // |
    private val resizeButton = JButton("Resize")    // Button to resize grid/process inputs
    private val fileChooser = JFileChooser()
    private val saveButton = JButton("Save to File")

    fun init() {
        isDoubleBuffered = true
        isVisible = true
        isFocusable = true
        layout = FlowLayout(FlowLayout.LEFT)

        add(widthBoxLabel)
        add(widthBox)
        add(heightBoxLabel)
        add(heightBox)
        add(resizeButton)
        add(saveButton)
        resizeButton.addActionListener {
            this@DisplaySO.requestFocusInWindow()
            val newWidth: Int
            val newHeight: Int
            try {
                newWidth = widthBox.value
                newHeight = heightBox.value

            } catch (e: NumberFormatException) {
                println("Error: Invalid number")
                return@addActionListener
            }
            resizeGrid(newWidth, newHeight)
        }
        saveButton.addActionListener {  // When the save button is pressed,
            saveGrid()                  // Save the grid to a file
        }
        resizeButton.isFocusable = false    // Set buttons as unfocusable (forces keystrokes to be processed by window)
        saveButton.isFocusable = false      // |
        fileChooser.apply {
            dialogTitle = "Choose file to save to..."                                 // Window title
            addChoosableFileFilter(FileNameExtensionFilter("Grid Map Files", "gmap")) // Add a file extension filter and
            fileFilter = choosableFileFilters[1]                                      // set it as the default
            isMultiSelectionEnabled = false                                           // Only allow selecting one file
        }

        addKeyListener(object : KeyListener {
            override fun keyPressed(e: KeyEvent) {
                if ((e.keyChar in 'A'..'Z' || e.keyChar in 'a'..'z') && currentKeyPressed != e.keyChar) {
                    currentKeyPressed = e.keyChar
                    println(e.keyChar)
                }
            }

            override fun keyReleased(e: KeyEvent) {
                if (currentKeyPressed == e.keyChar)
                    currentKeyPressed = null
            }

            override fun keyTyped(e: KeyEvent?) {}
        })
        addMouseListener(object : MouseListener {
            override fun mousePressed(e: MouseEvent) {
                if (!this@DisplaySO.isFocusOwner)
                    this@DisplaySO.requestFocusInWindow()
                currentMouseButton = e.button
                processMouseDown(e)
            }

            override fun mouseReleased(e: MouseEvent) {
                if (currentMouseButton == e.button)
                    currentMouseButton = null
            }

            override fun mouseEntered(e: MouseEvent?) {}
            override fun mouseExited(e: MouseEvent?) {}
            override fun mouseClicked(e: MouseEvent?) {}
        })
        addMouseMotionListener(object : MouseMotionListener {
            override fun mouseMoved(e: MouseEvent?) {}

            override fun mouseDragged(e: MouseEvent) {
                processMouseDown(e)
            }
        })

        nodeSize = (height - 2 * GRID_MARGIN) / nodes.size
        repaint()
    }

    private fun resizeGrid(newGridWidth: Int, newGridHeight: Int) {
        ... // Not relevant to issue (omitted)
    }

    private fun computeDrawSizes() {
        ... // Not relevant to issue (omitted)
    }

    private fun drawGrid(g: Graphics) {
        var nodePositionX: Int
        var nodePositionY: Int
        for (row in nodes) {
            for (node in row) {
                nodePositionX = node.gridX * nodeSize + centerOffsetX
                nodePositionY = node.gridY * nodeSize + centerOffsetY
                with(g) {
                    color = when (true) {
                        node.walkable -> when {
                            node.isStartNode -> START_TILE_COLOR
                            node.isEndNode -> END_TILE_COLOR
                            else -> WALKABLE_TILE_COLOR
                        }
                        else -> WALL_TILE_COLOR
                    }
                    fillRect(
                        nodePositionX,
                        nodePositionY,
                        nodeSize,
                        nodeSize
                    )
                    color = TILE_BORDER_COLOR
                    drawRect(
                        nodePositionX,
                        nodePositionY,
                        nodeSize,
                        nodeSize
                    )
                }
            }
        }
    }

    private fun processMouseDown(e: MouseEvent) {
        ... // Not relevant to issue (omitted)
    }

    private fun getNodeByPosition(x: Int, y: Int): Node? {
        ... // Not relevant to issue (omitted)
    }

    @Throws(IllegalArgumentException::class)
    private fun updateNode(node: Node, newStatus: Int) {
        ... // Not relevant to issue (omitted)
    }

    private fun saveGrid() {
        if (startNode == null || endNode == null) {
            println("Error: must choose a start node and an end node")
            return
        }
        if (fileChooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION)
            return
        fileChooser.selectedFile.writeText(
            gridToString()
        )
    }

    private fun gridToString(): String {
        ... // Not relevant to issue (omitted)
    }

    override fun paintComponent(g: Graphics) {
        if (width != previousWidth || height != previousHeight) // If window has been resized,
            computeDrawSizes()                                  // recalculate the draw sizes

        drawGrid(g) // Draw the grid
    }
}

1 个答案:

答案 0 :(得分:1)

对Kotlin一无所知,但我想问题是:

override fun paintComponent(g: Graphics) {
    if (width != previousWidth || height != previousHeight) 

在Swing中重写paintComponent(...)时的第一条语句应该是:

override fun paintComponent(g: Graphics) {
    super.paintComponent(g); // added
    if (width != previousWidth || height != previousHeight) 

为确保清除背景,以确保没有绘画伪影。