Fighting in the inner details of classes javax.swing.JOptionPane
and javax.swing.plaf.basic.BasicOptionPaneUI
, I managed to do this:
import java.awt.EventQueue;
import java.awt.image.BufferedImage;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.IntFunction;
import java.util.stream.Stream;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
/**
* @author Victor Stafusa
*/
public class TesteJOptionPane {
private static final Constructor<?> BUTTON_CONSTRUCTOR;
private static final IntFunction<Object[]> BUTTON_ARRAY_CONSTRUCTOR;
static {
Class<?> buttonClass;
try {
buttonClass = Class.forName("javax.swing.plaf.basic.BasicOptionPaneUI$ButtonFactory");
BUTTON_CONSTRUCTOR = buttonClass.getDeclaredConstructor(String.class, int.class, Icon.class, int.class);
BUTTON_CONSTRUCTOR.setAccessible(true);
} catch (ClassNotFoundException x) {
throw new NoClassDefFoundError(x.getMessage());
} catch (NoSuchMethodException x) {
throw new AssertionError(x);
}
BUTTON_ARRAY_CONSTRUCTOR = n -> (Object[]) Array.newInstance(buttonClass, n);
}
private static Icon imagem1() {
BufferedImage image = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
for (int i = 0; i < 50; i++) {
for (int j = 0; j < 50; j++) {
image.setRGB(i, j, 0xFF336699);
}
}
return new ImageIcon(image);
}
private static Icon imagem2() {
BufferedImage image = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
for (int i = 0; i < 50; i++) {
for (int j = 0; j < 50; j++) {
image.setRGB(i, j, 0xFF993366);
}
}
return new ImageIcon(image);
}
public static void main(String[] args) throws InterruptedException, InvocationTargetException {
AtomicReference<String> x = new AtomicReference<>();
EventQueue.invokeAndWait(() -> {
String resposta = escolherOpcao(
"Mensagem de texto",
"Aviso",
"Cancelar",
new Opcao("Continuar", imagem1()),
new Opcao("Cancelar", imagem2()));
x.set(resposta);
});
System.out.println(x.get());
}
public static final class Opcao {
private final String texto;
private final Icon imagem;
private final Object buttonFactory;
public Opcao(String texto, Icon imagem) {
this.texto = texto;
this.imagem = imagem;
try {
this.buttonFactory = BUTTON_CONSTRUCTOR.newInstance(texto, 0, imagem, -1);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException x) {
throw new AssertionError(x);
}
}
public String getTexto() {
return texto;
}
public Icon getImagem() {
return imagem;
}
public Object getButton() {
return buttonFactory;
}
}
public static String escolherOpcao(String message, String title, String defaultOption, Opcao... options) {
String[] nomes = Stream.of(options).map(Opcao::getTexto).toArray(String[]::new);
JOptionPane pane = new JOptionPane(
message,
JOptionPane.WARNING_MESSAGE,
JOptionPane.NO_OPTION,
null,
nomes,
defaultOption);
pane.setOptions(Stream.of(options).map(Opcao::getButton).toArray(BUTTON_ARRAY_CONSTRUCTOR));
JDialog dialog = pane.createDialog(null, title);
dialog.show();
Object selectedValue = pane.getValue();
dialog.dispose();
return Stream.of(options).filter(o -> o.getButton() == selectedValue).findFirst().map(Opcao::getTexto).orElse(null);
}
}
Here's the result:
Thiscodeiscomplicated,butI'llexplain:
Theimagem1()
andimagem2()
methodsareresponsibleforcreatingtheicons.Oneofthemmakestheblueiconandtheothertheredicon.
TheclassJOptionPane
delegatestoOptionPaneUI
thecreationofthebuttons.HoweverOptionPaneUI
isanabstractclass,andtheconcretesubclassusedisBasicOptionPaneUI
.
The BasicOptionPaneUI
class calls the getOptions()
method of JOptionPane
to decide whether to create the buttons. The buttons are created using the inner private class ButtonFactory
, which accepts icons.
-
So the solution is to inject instances of ButtonFactory
into JOptionPane
. Since this class is private, I instantiate it by means of reflection.
-
The class Opcao
corresponds to the buttons that will be passed. Each one with a name and an icon.
-
The escolherOpcao(String, String, String, Opcao...)
method is used to display the message box. The parameters are: window message, title, default option and the buttons of the chosen options.
-
The escolherOpcao
method returns the text of the selected button or null
if user clicks the .