Digging into the inner details of the classes javax.swing.JOptionPane
and javax.swing.plaf.basic.BasicOptionPaneUI
, I managed to do with it:
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);
}
}
That is the result:
This code is complicated, but I’ll explain:
The methods imagem1()
and imagem2()
are responsible for creating the icons. One of them makes the blue icon and the other the red icon.
The class JOptionPane
delegates to OptionPaneUI
the creation of buttons. However OptionPaneUI
is an abstract class, and the concrete subclass used is BasicOptionPaneUI
.
The class BasicOptionPaneUI
calls the method getOptions()
of JOptionPane
to decide whether to create the buttons. The buttons are created through the private inner class ButtonFactory
, that accepts icons.
Therefore, the solution consists in injecting instances of ButtonFactory
within the JOptionPane
. As this class is private, I urge it through Reflection.
The class Opcao
corresponds to the buttons that will be passed. Each with a name and an icon.
The method escolherOpcao(String, String, String, Opcao...)
is used to display the message box. The parameters are: the window message, the title, the default option and the buttons of the options chosen.
The method escolherOpcao
returns the text of the selected button or null
if and user click on .
I think he wants to put icons on buttons, not the little screen icon.
– Victor Stafusa
@Victorstafusa I suppose is duplicate of this one here, in this case, because he needs to create a custom version of Joptionpane agree? https://answall.com/a/199687/28595
– user28595
So that’s what he said here in the comment. I need to change the Button icon
– Vinicius Silva
Thank you very much I managed to solve thanks to you.
– Vinicius Silva
@Viniciussilva which of the forms used? I could not test the example of the answer because I am without IDE at the moment.
– user28595
This last one was only missing the 2 parameters I passed.
– Vinicius Silva
@Victorstafusa pq erased? Your answer exploring the class rewrite was great. I was even voting for it when I received an error.
– user28595
@Okay, I restored it. I don’t know, I got the impression that I was reinventing the square wheel.
– Victor Stafusa
The solution worked well to add icons , however these buttons do not close Joptionpane by default. Is there a method that does this so I can call in Jbutton Action? like a . Dispose() ??
– Vinicius Silva
@Viniciussilva then Oce needs to make the first hint, implement his own dialog screen, comforted explained at the beginning of the answer, or can implement the solution given by Ictor in the other answer.
– user28595