Размещение компонента на стеклянной панели - PullRequest
10 голосов
/ 01 апреля 2010

У меня есть подкласс JLabel, который является компонентом моего GUI. Я реализовал возможность перетаскивать компонент из одного контейнера в другой, но без каких-либо визуальных эффектов. Я хочу, чтобы этот JLabel следовал за курсором при перетаскивании элемента из одного контейнера в другой. Я подумал, что мог бы просто создать стеклянную панель и нарисовать ее там. Однако даже после добавления компонента на стеклянную панель, установки видимого компонента, отображения видимой стеклянной панели и установки непрозрачной стеклянной панели я все равно не вижу компонента. Я знаю, что компонент работает, потому что я могу добавить его на панель содержимого и показать его.

Как добавить компонент на стеклянную панель?


Наконец-то понял, как заставить работать простой пример. Спасибо, @akf. Мне удалось адаптировать это решение к моей исходной проблеме, что позволило мне удалить ~ 60 строк кода Java2D, который вручную отображал представление JLabel.

package test;

import java.awt.Color;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;

public class MainFrame extends JFrame {

    /**
     * @param args
     */
    public static void main(String[] args) {
        MainFrame mf = new MainFrame();
        mf.setSize(400, 400);
        mf.setLocationRelativeTo(null);
        mf.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        mf.setGlassPane(new JPanel());

        JLabel l = new JLabel();
        l.setText("Hello");
        l.setBorder(new LineBorder(Color.BLACK, 1));
        l.setBounds(10, 10, 50, 20);
        l.setBackground(Color.RED);
        l.setOpaque(true);
        l.setPreferredSize(l.getSize());

        //mf.add(l);
        ((JPanel)mf.getGlassPane()).add(l);
        mf.getGlassPane().setVisible(true);

        mf.setVisible(true);
    }
}

Ответы [ 4 ]

13 голосов
/ 01 апреля 2010

Пример кода ниже показывает, как перетащить шахматную фигуру вокруг шахматной доски. Он использует JLayeredPane вместо стеклянной панели, но я уверен, что концепции будут одинаковыми. То есть:

а) добавить стеклянную панель в корневую панель
б) сделать стекло видимым
c) добавьте компонент на стеклянную панель, убедившись, что границы действительны
d) использовать setLocation () для анимации перетаскивания компонента

Редактировать: добавлен код для исправления SSCCE

JLabel l = new JLabel();
l.setText("Hello");
l.setBorder(new LineBorder(Color.BLACK, 1));
// l.setPreferredSize(l.getSize());
// l.setBounds(10, 10, 50, 20);
((JPanel)mf.getGlassPane()).add(l);

mf.setVisible(true);
mf.getGlassPane().setVisible(true);

При использовании менеджеров компоновки вы никогда не используете методы setSize () или setBounds (). В вашем случае вы просто устанавливаете предпочтительный размер (0, 0), так как это размер по умолчанию для всех компонентов.

Это работает, когда вы добавляете метку к фрейму, потому что менеджер раскладки по умолчанию для области содержимого фрейма - это макет границы, поэтому предпочтительный размер метки игнорируется, а метка становится размером фрейма.

Однако по умолчанию JPanel использует FlowLayout, который учитывает предпочтительный размер компонента. Поскольку предпочтительный размер равен 0, рисовать нечего.

Кроме того, стеклянная панель должна быть видимой, чтобы ее можно было покрасить.

Я предлагаю вам прочитать учебник Swing . Есть раздел о том, как работают менеджеры по расположению и как работают стеклянные панели, и у каждого раздела есть рабочие примеры.

Редактировать: Пример кода добавлен ниже:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class ChessBoard extends JFrame implements MouseListener, MouseMotionListener
{
    JLayeredPane layeredPane;
    JPanel chessBoard;
    JLabel chessPiece;
    int xAdjustment;
    int yAdjustment;

    public ChessBoard()
    {
        Dimension boardSize = new Dimension(600, 600);

        //  Use a Layered Pane for this this application

        layeredPane = new JLayeredPane();
        layeredPane.setPreferredSize( boardSize );
        layeredPane.addMouseListener( this );
        layeredPane.addMouseMotionListener( this );
        getContentPane().add(layeredPane);

        //  Add a chess board to the Layered Pane

        chessBoard = new JPanel();
        chessBoard.setLayout( new GridLayout(8, 8) );
        chessBoard.setPreferredSize( boardSize );
        chessBoard.setBounds(0, 0, boardSize.width, boardSize.height);
        layeredPane.add(chessBoard, JLayeredPane.DEFAULT_LAYER);

        //  Build the Chess Board squares

        for (int i = 0; i < 8; i++)
        {
            for (int j = 0; j < 8; j++)
            {
                JPanel square = new JPanel( new BorderLayout() );
                square.setBackground( (i + j) % 2 == 0 ? Color.red : Color.white );
                chessBoard.add( square );
            }
        }

        // Add a few pieces to the board

        ImageIcon duke = new ImageIcon("dukewavered.gif"); // add an image here

        JLabel piece = new JLabel( duke );
        JPanel panel = (JPanel)chessBoard.getComponent( 0 );
        panel.add( piece );
        piece = new JLabel( duke );
        panel = (JPanel)chessBoard.getComponent( 15 );
        panel.add( piece );
    }

    /*
    **  Add the selected chess piece to the dragging layer so it can be moved
    */
    public void mousePressed(MouseEvent e)
    {
        chessPiece = null;
        Component c =  chessBoard.findComponentAt(e.getX(), e.getY());

        if (c instanceof JPanel) return;

        Point parentLocation = c.getParent().getLocation();
        xAdjustment = parentLocation.x - e.getX();
        yAdjustment = parentLocation.y - e.getY();
        chessPiece = (JLabel)c;
        chessPiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);

        layeredPane.add(chessPiece, JLayeredPane.DRAG_LAYER);
        layeredPane.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
    }

    /*
    **  Move the chess piece around
    */
    public void mouseDragged(MouseEvent me)
    {
        if (chessPiece == null) return;

        //  The drag location should be within the bounds of the chess board

        int x = me.getX() + xAdjustment;
        int xMax = layeredPane.getWidth() - chessPiece.getWidth();
        x = Math.min(x, xMax);
        x = Math.max(x, 0);

        int y = me.getY() + yAdjustment;
        int yMax = layeredPane.getHeight() - chessPiece.getHeight();
        y = Math.min(y, yMax);
        y = Math.max(y, 0);

        chessPiece.setLocation(x, y);
     }

    /*
    **  Drop the chess piece back onto the chess board
    */
    public void mouseReleased(MouseEvent e)
    {
        layeredPane.setCursor(null);

        if (chessPiece == null) return;

        //  Make sure the chess piece is no longer painted on the layered pane

        chessPiece.setVisible(false);
        layeredPane.remove(chessPiece);
        chessPiece.setVisible(true);

        //  The drop location should be within the bounds of the chess board

        int xMax = layeredPane.getWidth() - chessPiece.getWidth();
        int x = Math.min(e.getX(), xMax);
        x = Math.max(x, 0);

        int yMax = layeredPane.getHeight() - chessPiece.getHeight();
        int y = Math.min(e.getY(), yMax);
        y = Math.max(y, 0);

        Component c =  chessBoard.findComponentAt(x, y);

        if (c instanceof JLabel)
        {
            Container parent = c.getParent();
            parent.remove(0);
            parent.add( chessPiece );
            parent.validate();
        }
        else
        {
            Container parent = (Container)c;
            parent.add( chessPiece );
            parent.validate();
        }
    }

    public void mouseClicked(MouseEvent e) {}
    public void mouseMoved(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}

    public static void main(String[] args)
    {
        JFrame frame = new ChessBoard();
        frame.setDefaultCloseOperation( DISPOSE_ON_CLOSE );
        frame.setResizable( false );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible(true);
     }
}
12 голосов
/ 02 апреля 2010

Хотя это и относится к вопросу, пример JLayeredPane , на который ссылается @camickr, допускает следующую адаптацию, в которой подчеркивается эффект mouseReleased() над существующим компонентом.

public ChessBoard() {
    ...
    // Add a few pieces to the board
    addPiece(3, 0, "♛"); 
    addPiece(4, 0, "♚");
    addPiece(3, 7, "♕");
    addPiece(4, 7, "♔");
}

static Font font = new Font("Sans", Font.PLAIN, 72);

private void addPiece(int col, int row, String glyph) {
    JLabel piece = new JLabel(glyph, JLabel.CENTER);
    piece.setFont(font);
    JPanel panel = (JPanel) chessBoard.getComponent(col + row * 8);
    panel.add(piece);
}
3 голосов
/ 03 апреля 2010

Помимо указателей на примеры LayerPane, уже предоставленных, проблема с вашим исходным кодом связана с настройкой предпочтительного размера вашей метки. Вы устанавливаете его до того, как размер JLabel будет измерен, поэтому ваш:

l.setPreferredSize(l.getSize());

неэффективно. Если, с другой стороны, вы сделаете этот звонок после того, как сделаете звонок на setBounds, вы увидите желаемые результаты. Имея это в виду, измените порядок следующим образом:

l.setPreferredSize(l.getSize());
l.setBounds(10, 10, 50, 20);

чтобы выглядеть так:

l.setBounds(10, 10, 50, 20);
l.setPreferredSize(l.getSize());
3 голосов
/ 01 апреля 2010

С тех пор, как я давно слежу за блогами Ромена Гая на Swing. У меня есть ссылка, которая может вас заинтересовать. Он выпустил источник, который использовал GlassPane для эффектов DnD.

http://jroller.com/gfx/entry/drag_and_drop_effects_the

Я сам никогда не использовал шипучую анимацию / эффект на DnD, поэтому не могу комментировать дальше: - |

...