Criação de um JButton personalizado em Java

97

Existe uma maneira de criar um JButtoncom seu próprio gráfico de botão e não apenas com uma imagem dentro do botão?

Se não, existe outra maneira de criar um customizado JButtonem java?

Anton
fonte

Respostas:

91

Quando eu estava aprendendo Java, tínhamos que fazer o Yahtzee e achei que seria legal criar componentes e contêineres Swing personalizados em vez de apenas desenhar tudo em um JPanel. O benefício de estender Swingcomponentes, é claro, é ter a capacidade de adicionar suporte para atalhos de teclado e outros recursos de acessibilidade que você não pode fazer apenas por ter um paint()método para imprimir uma imagem bonita. No entanto, pode não ser feito da melhor maneira, mas pode ser um bom ponto de partida para você.

Editar 8/6 - Se não estiver aparente nas imagens, cada Dado é um botão no qual você pode clicar. Isso o moverá para o DiceContainerabaixo. Olhando para o código-fonte, você pode ver que cada botão Die é desenhado dinamicamente, com base em seu valor.

texto alternativo
texto alternativo
texto alternativo

Aqui estão as etapas básicas:

  1. Crie uma classe que estenda JComponent
  2. Chame o construtor pai super()em seus construtores
  3. Certifique-se de implementar implementos de classe MouseListener
  4. Coloque isso no construtor:

    enableInputMethods(true);   
    addMouseListener(this);
  5. Substitua esses métodos:

    public Dimension getPreferredSize()  
    public Dimension getMinimumSize()  
    public Dimension getMaximumSize()
  6. Substitua este método:

    public void paintComponent(Graphics g)

A quantidade de espaço com que você tem que trabalhar ao desenhar seu botão é definida por getPreferredSize(), assumindo getMinimumSize()e getMaximumSize()retornando o mesmo valor. Não experimentei muito com isso, mas, dependendo do layout que você usa para sua GUI, seu botão pode parecer completamente diferente.

E, finalmente, o código-fonte . Caso eu tenha perdido alguma coisa.

Kevin
fonte
1
Olá, primeiro obrigado pelo código! Eu sugeriria adicionar um 'setActionComme (String Command)' ao seu código. é uma das formas de filtrar eventos no Swing. (mas então você pode argumentar que há 1001 coisas que poderiam ser adicionadas para tornar as coisas um pouco melhores: P)
Jason Rogers
1
Para criar um dado que você pode pressionar, você pode realmente usar um JButton antigo simples (em vez de criar uma subclasse ou criar um JComponent personalizado) tirando proveito do setIcon / setPressedIcon, bem como de outras funcionalidades do ícone no JButton
ControlAltDel
34

Sim, isso é possível. Uma das principais vantagens de usar o Swing é a facilidade com que os controles abstratos podem ser criados e manipulados.

Esta é uma maneira rápida e suja de estender a classe JButton existente para desenhar um círculo à direita do texto.

package test;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JFrame;

public class MyButton extends JButton {

    private static final long serialVersionUID = 1L;

    private Color circleColor = Color.BLACK;

    public MyButton(String label) {
        super(label);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Dimension originalSize = super.getPreferredSize();
        int gap = (int) (originalSize.height * 0.2);
        int x = originalSize.width + gap;
        int y = gap;
        int diameter = originalSize.height - (gap * 2);

        g.setColor(circleColor);
        g.fillOval(x, y, diameter, diameter);
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension size = super.getPreferredSize();
        size.width += size.height;
        return size;
    }

    /*Test the button*/
    public static void main(String[] args) {
        MyButton button = new MyButton("Hello, World!");

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 400);

        Container contentPane = frame.getContentPane();
        contentPane.setLayout(new FlowLayout());
        contentPane.add(button);

        frame.setVisible(true);
    }

}

Observe que, ao substituir paintComponent, o conteúdo do botão pode ser alterado, mas a borda é pintada pelo método paintBorder . O método getPreferredSize também precisa ser gerenciado para oferecer suporte dinâmico às mudanças no conteúdo. É necessário ter cuidado ao medir as métricas da fonte e as dimensões da imagem.

Para criar um controle no qual você pode confiar, o código acima não é a abordagem correta. As dimensões e cores são dinâmicas no Swing e dependem da aparência que está sendo usada. Até mesmo a aparência padrão do Metal mudou nas versões do JRE. Seria melhor implementar AbstractButton e estar em conformidade com as diretrizes estabelecidas pela API Swing. Um bom ponto de partida é olhar para os javax.swing.LookAndFeel e javax.swing.UIManager classes.

http://docs.oracle.com/javase/8/docs/api/javax/swing/LookAndFeel.html

http://docs.oracle.com/javase/8/docs/api/javax/swing/UIManager.html

Compreender a anatomia de LookAndFeel é útil para escrever controles: Criando uma aparência e comportamento personalizados

McDowell
fonte
13

Você pode sempre experimentar a aparência e o comportamento do Synth. Você fornece um arquivo xml que atua como uma espécie de folha de estilo, junto com quaisquer imagens que deseja usar. O código pode ser assim:

try {
    SynthLookAndFeel synth = new SynthLookAndFeel();
    Class aClass = MainFrame.class;
    InputStream stream = aClass.getResourceAsStream("\\default.xml");

    if (stream == null) {
        System.err.println("Missing configuration file");
        System.exit(-1);                
    }

    synth.load(stream, aClass);

    UIManager.setLookAndFeel(synth);
} catch (ParseException pe) {
    System.err.println("Bad configuration file");
    pe.printStackTrace();
    System.exit(-2);
} catch (UnsupportedLookAndFeelException ulfe) {
    System.err.println("Old JRE in use. Get a new one");
    System.exit(-3);
}

A partir daí, vá em frente e adicione seu JButton como faria normalmente. A única mudança é que você usa o método setName (string) para identificar o que o botão deve mapear no arquivo xml.

O arquivo xml pode ter a seguinte aparência:

<synth>
    <style id="button">
        <font name="DIALOG" size="12" style="BOLD"/>
        <state value="MOUSE_OVER">
            <imagePainter method="buttonBackground" path="dirt.png" sourceInsets="2 2 2 2"/>
            <insets top="2" botton="2" right="2" left="2"/>
        </state>
        <state value="ENABLED">
            <imagePainter method="buttonBackground" path="dirt.png" sourceInsets="2 2 2 2"/>
            <insets top="2" botton="2" right="2" left="2"/>
        </state>
    </style>
    <bind style="button" type="name" key="dirt"/>
</synth>

O elemento bind especifica para o qual mapear (neste exemplo, ele aplicará esse estilo a quaisquer botões cuja propriedade name tenha sido definida como "dirt").

E alguns links úteis:

http://javadesktop.org/articles/synth/

http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/synth.html

Rjohnston
fonte
8

Provavelmente estou indo um milhão de milhas na direção errada (mas sou apenas jovem: P). mas você não poderia adicionar o gráfico a um painel e, em seguida, um mouseelistener ao objeto gráfico para que, quando o usuário no gráfico, sua ação fosse executada.

AngelOfCake
fonte
1
Isso funcionaria, mas eu preferiria usar o JButton padrão do que criar um meu tipo de botão, se possível.
Anton,
7

Não tenho feito o desenvolvimento SWING desde minhas primeiras aulas de CS, mas se ele não fosse integrado, você poderia simplesmente herdar javax.swing.AbstractButtone criar o seu próprio. Deve ser muito simples conectar algo com sua estrutura existente.

John Downey
fonte