Размещение компонента на стеклянной панели

У меня есть подкласс JLabel, который формирует компонент моего графического интерфейса. Я реализовал возможность перетаскивания компонента из одного контейнера в другой, но без каких-либо визуальных эффектов. Я хочу, чтобы этот 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);
    }
}

Ответ 1

Помимо указателей на уже предоставленные примеры 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());

Ответ 2

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

a) добавьте стеклянную панель в корневую панель
б) сделайте стеклянную панель видимой - 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 tutorial. Есть раздел о том, как работают менеджеры компоновки и как работают стеклопакеты, и в каждом разделе есть рабочие примеры.

Изменить: Пример кода, добавленного ниже:

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);
     }
}

Ответ 3

Несмотря на тангенциальный вопрос, пример 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);
}

Ответ 4

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

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

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