Creación de un JButton personalizado en Java

97

¿Hay alguna manera de crear un JButtongráfico con su propio botón y no solo con una imagen dentro del botón?

Si no es así, ¿hay otra forma de crear una costumbre JButtonen java?

Anton
fuente

Respuestas:

91

Cuando estaba aprendiendo Java por primera vez, tuvimos que hacer Yahtzee y pensé que sería genial crear componentes y contenedores personalizados de Swing en lugar de simplemente dibujar todo en uno JPanel. El beneficio de extender los Swingcomponentes, por supuesto, es tener la capacidad de agregar soporte para atajos de teclado y otras funciones de accesibilidad que no puede hacer con solo tener un paint()método que imprima una imagen bonita. Sin embargo, puede que no se haga de la mejor manera, pero puede ser un buen punto de partida para usted.

Editar 8/6: si no fuera evidente en las imágenes, cada dado es un botón en el que puede hacer clic. Esto lo moverá al DiceContainersiguiente. Al mirar el código fuente, puede ver que cada botón de Die se dibuja dinámicamente, según su valor.

texto alternativo
texto alternativo
texto alternativo

Aquí están los pasos básicos:

  1. Crea una clase que se extienda JComponent
  2. Llame al constructor padre super()en sus constructores
  3. Asegúrese de que su clase implemente MouseListener
  4. Pon esto en el constructor:

    enableInputMethods(true);   
    addMouseListener(this);
  5. Anule estos métodos:

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

    public void paintComponent(Graphics g)

La cantidad de espacio con la que tiene que trabajar al dibujar su botón está definida por getPreferredSize(), asumiendo getMinimumSize()y getMaximumSize()devolviendo el mismo valor. No he experimentado demasiado con esto pero, dependiendo del diseño que use para su GUI, su botón podría verse completamente diferente.

Y finalmente, el código fuente . Por si me perdí algo.

Kevin
fuente
1
Hola, primero gracias por el código. Sugeriría agregar un 'setActionComme (String Command)' a su código. es una de las formas de filtrar eventos en Swing. (pero luego puede argumentar que hay 1001 cosas que podrían agregarse para mejorar un poco las cosas: P)
Jason Rogers
1
Para crear un dado que puede presionar, puede usar un JButton antiguo y simple (en lugar de subclasificar o crear un JComponent personalizado) aprovechando setIcon / setPressedIcon, así como otras funciones de Icon en JButton
ControlAltDel
34

Si, esto es posible. Una de las principales ventajas del uso de Swing es la facilidad con la que se pueden crear y manipular los controles abstractos.

Aquí hay una forma rápida y sucia de extender la clase JButton existente para dibujar un círculo a la derecha del 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);
    }

}

Tenga en cuenta que al anular paintComponent, el contenido del botón se puede cambiar, pero que el borde se pinta con el método paintBorder . El método getPreferredSize también debe administrarse para admitir dinámicamente cambios en el contenido. Se debe tener cuidado al medir las métricas de fuente y las dimensiones de la imagen.

Para crear un control en el que pueda confiar, el código anterior no es el enfoque correcto. Las dimensiones y los colores son dinámicos en Swing y dependen de la apariencia que se utilice. Incluso el aspecto metálico predeterminado ha cambiado en las versiones de JRE. Sería mejor implementar AbstractButton y cumplir con las pautas establecidas por la API Swing. Un buen punto de partida es mirar las clases javax.swing.LookAndFeel y javax.swing.UIManager .

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

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

Comprender la anatomía de LookAndFeel es útil para escribir controles: Creación de una apariencia y sensación personalizadas

McDowell
fuente
13

Siempre puedes probar el look & feel del sintetizador. Proporciona un archivo xml que actúa como una especie de hoja de estilo, junto con las imágenes que desee utilizar. El código podría verse así:

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 de ahí, continúe y agregue su JButton como lo haría normalmente. El único cambio es que usa el método setName (cadena) para identificar a qué se debe asignar el botón en el archivo xml.

El archivo xml podría verse así:

<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>

El elemento bind allí especifica a qué mapear (en este ejemplo, aplicará ese estilo a cualquier botón cuya propiedad de nombre se haya establecido en "suciedad").

Y un par de enlaces útiles:

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

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

rjohnston
fuente
8

Probablemente voy un millón de millas en el directo equivocado (pero solo soy joven: P). pero ¿no podría agregar el gráfico a un panel y luego un ratón de escucha al objeto gráfico para que cuando el usuario en el gráfico realice su acción?

AngelOfCake
fuente
1
Esto funcionaría, pero preferiría usar el JButton estándar que crear mi tipo de botón si es posible.
Anton
7

No he realizado el desarrollo SWING desde mis primeras clases de CS, pero si no estaba integrado, podría heredar javax.swing.AbstractButtony crear el suyo propio. Debería ser bastante simple conectar algo junto con su marco existente.

John Downey
fuente