2012-05-09 363 views
15

Me gustaría crear un botón personalizado, que tiene dos estados presionados o no, como un botón de alternar. Tengo dos imágenes para hacer esto (presionadas y no presionadas), entonces, ¿cómo puedo crear el botón y mostrarlo con mis imágenes? El botón debe tomar el tamaño de la imagen.
No estoy usando FXML. Gracias por ayudarme.JavaFX - crear botón personalizado con imagen

Respuesta

40

Hay algunas maneras diferentes de lograr esto, esbozaré mis favoritos.

Utilice un ToggleButton y aplique un estilo personalizado. Sugiero esto porque su control requerido es "como un botón para alternar", pero se ve diferente del estilo del botón de alternar predeterminado.

Mi método preferido es definir un gráfico para el botón en css:

.toggle-button { 
    -fx-graphic: url('http://icons.iconarchive.com/icons/aha-soft/desktop-buffet/128/Pizza-icon.png'); 
} 

.toggle-button:selected { 
    -fx-graphic: url('http://icons.iconarchive.com/icons/aha-soft/desktop-buffet/128/Piece-of-cake-icon.png'); 
} 

o utilizar el css adjunto para definir una imagen de fondo.

// file imagetogglebutton.css deployed in the same package as ToggleButtonImage.class 
.toggle-button { 
    -fx-background-image: url('http://icons.iconarchive.com/icons/aha-soft/desktop-buffet/128/Pizza-icon.png'); 
    -fx-background-repeat: no-repeat; 
    -fx-background-position: center; 
} 

.toggle-button:selected { 
    -fx-background-image: url('http://icons.iconarchive.com/icons/aha-soft/desktop-buffet/128/Piece-of-cake-icon.png'); 
} 

prefiero la especificación -fx-gráfico sobre los -Fx-fondo-especificaciones * como las reglas para las imágenes de fondo de estilo son difíciles y establecer el fondo no dimensionar de forma automática el botón de la imagen, mientras que el establecimiento de la gráfico hace.

Y un código de ejemplo:

import javafx.application.Application; 
import javafx.scene.*; 
import javafx.scene.control.ToggleButton; 
import javafx.scene.layout.StackPaneBuilder; 
import javafx.stage.Stage; 

public class ToggleButtonImage extends Application { 
    public static void main(String[] args) throws Exception { launch(args); } 
    @Override public void start(final Stage stage) throws Exception { 
    final ToggleButton toggle = new ToggleButton(); 
    toggle.getStylesheets().add(this.getClass().getResource(
     "imagetogglebutton.css" 
    ).toExternalForm()); 
    toggle.setMinSize(148, 148); toggle.setMaxSize(148, 148); 
    stage.setScene(new Scene(
     StackPaneBuilder.create() 
     .children(toggle) 
     .style("-fx-padding:10; -fx-background-color: cornsilk;") 
     .build() 
    )); 
    stage.show(); 
    } 
} 

Algunas de las ventajas de hacer esto son:

  1. Usted obtener el comportamiento botón predeterminado de palanca y no tenga que volver a aplicar usted mismo añadiendo su diseño de foco propio, mouse y manejadores de teclas, etc.
  2. Si su aplicación se transfiere a una plataforma diferente, como un dispositivo móvil, funcionará de manera inmediata respondiendo a eventos táctiles en lugar de eventos de mouse, etc.
  3. Su estilo está separado de la lógica de su aplicación por lo que es más fácil cambiar el estilo de su aplicación.

Una alternativa es no utilizar CSS y todavía utilizar un ToggleButton, pero establecer la imagen gráfica de código:

import javafx.application.Application; 
import javafx.beans.binding.Bindings; 
import javafx.scene.*; 
import javafx.scene.control.ToggleButton; 
import javafx.scene.image.*; 
import javafx.scene.layout.StackPaneBuilder; 
import javafx.stage.Stage; 

public class ToggleButtonImageViaGraphic extends Application { 
    public static void main(String[] args) throws Exception { launch(args); } 
    @Override public void start(final Stage stage) throws Exception { 
    final ToggleButton toggle  = new ToggleButton(); 
    final Image  unselected = new Image(
     "http://icons.iconarchive.com/icons/aha-soft/desktop-buffet/128/Pizza-icon.png" 
    ); 
    final Image  selected = new Image(
     "http://icons.iconarchive.com/icons/aha-soft/desktop-buffet/128/Piece-of-cake-icon.png" 
    ); 
    final ImageView toggleImage = new ImageView(); 
    toggle.setGraphic(toggleImage); 
    toggleImage.imageProperty().bind(Bindings 
     .when(toggle.selectedProperty()) 
     .then(selected) 
     .otherwise(unselected) 
    ); 

    stage.setScene(new Scene(
     StackPaneBuilder.create() 
     .children(toggle) 
     .style("-fx-padding:10; -fx-background-color: cornsilk;") 
     .build() 
    )); 
    stage.show(); 
    } 
} 

El enfoque basado en el código tiene la ventaja de que no tiene que utilizar CSS si no estás familiarizado con eso.

Para obtener el mejor rendimiento y la facilidad de transporte a aplicaciones sin firmar y espacios de prueba de webstart, agrupe las imágenes con su aplicación y haga referencia a ellas por direcciones de ruta relativas en lugar de descargarlas de la red.

+0

¡Guau! Qué puedo decir ?Esa es una gran respuesta, muchas gracias. Creo que buscaré la primera solución, aplicaré el tamaño a mi botón en el código, parece ser común en la aplicación JavaFX a diferencia de Swing. – jerome

+1

Tuve que agregar -fx-background-color: transparent; en el CSS para que solo mi botón se llene con mi imagen. – jerome

+0

Es una buena respuesta pero demuestra una debilidad en JavaFX. Este tipo de requisito es desordenado e injustificado en el siglo XXI. –

6

Solo necesita crear su propia clase heredada del elemento primario. Coloque un ImageView sobre eso, y en los eventos de mousedown y mouse up simplemente cambie las imágenes de ImageView.

public class ImageButton extends Parent { 

    private static final Image NORMAL_IMAGE = ...; 
    private static final Image PRESSED_IMAGE = ...; 

    private final ImageView iv; 

    public ImageButton() { 
     this.iv = new ImageView(NORMAL_IMAGE); 
     this.getChildren().add(this.iv); 

     this.iv.setOnMousePressed(new EventHandler<MouseEvent>() { 

      public void handle(MouseEvent evt) { 
       iv.setImage(PRESSED_IMAGE); 
      } 

     }); 

     // TODO other event handlers like mouse up 

    } 

} 
+0

Eso es un gran comienzo gracias – jerome

2

Una combinación de las 2 respuestas anteriores hizo el truco. Gracias. Una nueva clase que hereda de Button. Nota: se debe llamar a updateImages() antes de mostrar el botón.

import javafx.event.EventHandler; 
import javafx.scene.control.Button; 
import javafx.scene.image.Image; 
import javafx.scene.image.ImageView; 
import javafx.scene.input.MouseEvent; 

public class ImageButton extends Button { 

    public void updateImages(final Image selected, final Image unselected) { 
     final ImageView iv = new ImageView(selected); 
     this.getChildren().add(iv); 

     iv.setOnMousePressed(new EventHandler<MouseEvent>() { 
      public void handle(MouseEvent evt) { 
       iv.setImage(unselected); 
      } 
     }); 
     iv.setOnMouseReleased(new EventHandler<MouseEvent>() { 
      public void handle(MouseEvent evt) { 
       iv.setImage(selected); 
      } 
     }); 

     super.setGraphic(iv); 
    } 
} 
Cuestiones relacionadas