Creazione di JButton personalizzati da immagini contenenti pixel trasparenti

Leggi la modifica 2 per quello che mi manca in realtà per farlo funzionare

Attualmente sto cercando di creare alcuni JButtons personalizzati usando le immagini create in Photoshop con un parametro alpha.

Finora, ignorare il metodo paint () per disegnare l’immagine ha funzionato nel senso che il pulsante è disegnato mostrando l’immagine corretta. Mi piacerebbe migliorarlo, però, rendendo la sua forma (area cliccabile) uguale ai pixel visibili sull’immagine (in questo momento se disegno il bordo del pulsante, è un quadrato).

C’è un modo semplice per farlo o devo analizzare l’immagine e trovare i pixel alfa per creare un bordo personalizzato?

Quali metodi dovrei scavalcare per farlo funzionare nel modo che voglio?

Inoltre, un’altra domanda che avrò in seguito: sarebbe meglio usare un qualche tipo di algoritmo per cambiare i colors delle immagini per far sembrare che si stia facendo clic quando le persone cliccano su di esso o sto meglio creando un secondo immagine e disegno quella mentre il pulsante è attivo?

Edit: Ho appena letto su qualche altra domanda che dovrei ridefinire paintComponent () invece di paint (), mi piacerebbe sapere perché da quando ridefinisce paint () funziona bene?

Modifica 2: ho modificato tutto per assicurarmi che i miei JButtons vengano creati utilizzando il costruttore predefinito con un’icona. Quello che sto cercando di fare è ottenere la posizione X e Y di dove è stato registrato il clic e prendere il pixel dell’icona in quella posizione e controllare il suo canale alfa per vedere se è 0 (se lo è, non fare nulla, altrimenti fare il azione che dovrebbe fare).

Il fatto è che il canale alfa restituisce sempre 255 (e blu, rosso e verde sono a 238 su pixel trasparenti). Sugli altri pixel, tutto restituisce il valore che dovrebbe restituire.

Ecco un esempio (provalo con un’altra immagine se vuoi) che ricrea il mio problema:

public class TestAlphaPixels extends JFrame { private final File FILECLOSEBUTTON = new File("img\\boutonrondX.png"); //My round button with transparent corners private JButton closeButton = new JButton(); //Creating it empty to be able to place it and resize the image after the button size is known public TestAlphaPixels() throws IOException { setLayout(null); setSize(150, 150); closeButton.setSize(100, 100); closeButton.setContentAreaFilled(false); closeButton.setBorderPainted(false); add(closeButton); closeButton.addMouseListener(new MouseListener() { public void mouseClicked(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { System.out.println("Alpha value of pixel (" + e.getX() + ", " + e.getY() + ") is: " + clickAlphaValue(closeButton.getIcon(), e.getX(), e.getY())); } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } }); Image imgCloseButton = ImageIO.read(FILECLOSEBUTTON); //Resize the image to fit the button Image newImg = imgCloseButton.getScaledInstance((int)closeButton.getSize().getWidth(), (int)closeButton.getSize().getHeight(), java.awt.Image.SCALE_SMOOTH); closeButton.setIcon(new ImageIcon(newImg)); } private int clickAlphaValue(Icon icon, int posX, int posY) { int width = icon.getIconWidth(); int height = icon.getIconHeight(); BufferedImage tempImage = (BufferedImage)createImage(width, height); Graphics2D g = tempImage.createGraphics(); icon.paintIcon(null, g, 0, 0); g.dispose(); int alpha = (tempImage.getRGB(posX, posY) >> 24) & 0x000000FF; return alpha; } public static void main(String[] args) { try { TestAlphaPixels testAlphaPixels = new TestAlphaPixels(); testAlphaPixels.setVisible(true); testAlphaPixels.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } catch(IOException ioe) { ioe.printStackTrace(); } } } 

Ciò che questo campione mostra effettivamente

Questa è solo un’ipotesi, ma è ansible che quando la mia immagine viene lanciata su un’icona, perde la sua proprietà Alpha e quindi non restituisce il valore corretto? Ad ogni modo, sarei davvero grato se qualcuno potesse davvero darmi una mano e dirmi cosa dovrei cambiare per ottenere il valore corretto.

Immagino che, quando lo provo con l’immagine originale, il valore del canale alfa sia corretto, ma non posso effettivamente usare quel BufferedImage perché lo ridimensiono, quindi in realtà ottengo i valori del canale dell’immagine con la dimensione originale …

Penso che tu sia sulla strada sbagliata. Non è necessario sovrascrivere né i metodi paint () né paintComponent (). JButton “sa” già essere mostrato solo con l’immagine:

 ImageIcon cup = new ImageIcon("images/cup.gif"); JButton button2 = new JButton(cup); 

Vedere ad esempio il seguente tutorial: http://www.apl.jhu.edu/~hall/java/Swing-Tutorial/Swing-Tutorial-JButton.html

Inoltre lo swing è completamente personalizzato. Puoi controllare l’opacità, il bordo, il colore, ecc. Probabilmente dovresti scavalcare alcuni metodi menzionati per cambiare funzionalità. Ma nella maggior parte dei casi esiste una soluzione migliore e più semplice.

Dato che c’erano buoni elementi in più risposte, ma nessuna delle risposte era completa da sola, risponderò alla mia stessa domanda in modo che altre persone che hanno lo stesso problema possano provare qualcosa di simile.

Ho creato i miei pulsanti utilizzando una nuova class che estende JButton, con un nuovo costruttore che accetta un BufferedImage come parametro anziché un’icona. Il motivo è che quando ho fatto qualcosa come myButton.getIcon (), restituiva un’icona, quindi dovevo fare varie manipolazioni su di essa per renderla una BufferedImage delle giuste dimensioni, e alla fine non funzionava comunque perché sembra che il primo cast di Icon abbia fatto perdere i dati alfa nei pixel, quindi non ho potuto verificare se l’utente stesse facendo clic sui pixel trasparenti o meno.

Così ho fatto qualcosa del genere per il costruttore:

 public class MyButton extends JButton { private BufferedImage bufImg; public MyButton(BufferedImage bufImg) { super(new ImageIcon(bufImg)); this.bufImg = bufImg; } } 

Poi ho creato un accessorio per il mio bufImg che ridimensionava l’immagine per adattarla a JButton usando il metodo getSize () e poi restituiva un’immagine ridimensionata alla giusta dimensione. Faccio le trasformazioni dell’accessorio getBufImg () perché la dimensione dell’immagine potrebbe cambiare quando la finestra viene ridimensionata. Quando chiami getBufImg (), di solito è perché hai fatto clic sul pulsante e quindi non stai ridimensionando la finestra.

Qualcosa di simile restituirà l’immagine alla giusta dimensione:

  public BufferedImage getBufImg() { BufferedImage newImg = new BufferedImage(getSize().getWidth(), getSize().getHeight(), BufferedImage.TYPE_INT_ARGB); //Create a new buffered image the right size Graphics2D g2d = newImg.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2d.drawImage(bufImg, 0, 0, getSize().getWidth(), getSize().getHeight(), null); g2d.dispose(); return newImg; } 

Con quell’immagine bufferizzata, puoi quindi codificare un metodo come questo:

  private int clickAlphaValue(BufferedImage bufImg, int posX, int posY) { int alpha; alpha = (bufImg.getRGB(posX, posY) >>24) & 0x000000FF; //Gets the bit that contains alpha information return alpha; } 

Che chiami sul pulsante che implementa un MouseListener, come questo:

 myButton.addMouseListener(new MouseListener() { public void mouseClicked(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { if(clickAlphaValue(((myButton)e.getSource()).getBufImg(), e.getX(), e.getY()) != 0) //If alpha is not set to 0 System.exit(0); //Or other things you want your button to do } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } }); 

E voilà! Il pulsante eseguirà l’azione solo se hai fatto clic su pixel non trasparenti.

Grazie per l’aiuto di tutti, non avrei potuto trovare da solo queste soluzioni.

Se si desidera avere punti di clic specifici per forma, è meglio usare Shape e il loro metodo di contenimento. Se lo desideri, puoi creare una forma quando crei la tua class di pulsanti personalizzata come parte di essa e implementare un metodo contiene avvolgendo il metodo di contenimento della forma.

Come per il JButton personalizzato, crea una class che estende JButton, in questo modo:

 import java.awt.*; import javax.swing.*; public class CustomButton extends JButton{ /** Filename of the image to be used as the button's icon. */ private String fileName; /** The width of the button */ private int width; /** The height of the button. */ private int height; public CustomButton(String fileName, int width, int height){ this.fileName = fileName; this.width = width; this.height = height; createButton(); } /** * Creates the button according to the fields set by the constructor. */ private void createButton(){ this.setIcon(getImageIcon(filename)); this.setPreferredSize(new Dimension(width, height)); this.setMaximumSize(new Dimension(width, height)); this.setFocusPainted(false); this.setRolloverEnabled(false); this.setOpaque(false); this.setContentAreaFilled(false); this.setBorderPainted(false); this.setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); } } 

Ecco come caricare ImageIcon, se vuoi farlo in questo modo.

  public ImageIcon getImageIcon(String fileName){ String imageDirectory = "images/"; //relative to classpath URL imgURL = getClass().getResource(imageDirectory + fileName); return new ImageIcon(imgURL); } 

Questo ti darà un pulsante che avrà almeno l’aspetto della tua immagine. Ho fatto una domanda simile per quanto riguarda gli eventi basati su immagini al clic e le forms hanno aiutato meraviglie. Immagino si tratti di quanto siano complesse le tue immagini dei pulsanti. Ecco comunque riferimento:
Come si può rilevare un evento click-mouse su un object Immagine in Java?

PS: Forse guardi nel generare forms dalle immagini, che girano attorno a tutti i pixel che non sono trasparenti. Non so se questo è ansible, ma significherebbe che un pulsante verrebbe “premuto” solo se l’utente fa clic sulla parte dell’immagine di esso. Solo un pensiero.

Se vuoi che il layout dei pulsanti sia quello dei pixel non trasparenti nell’immagine, devi ridefinire il metodo paintComponent() . È il modo più corretto di farlo (override paint () ha funzionato nei vecchi tempi ma ora è scoraggiato).

Comunque penso che non sia esattamente quello che vuoi: vuoi che un clic sul pulsante venga rilevato solo se si trova su un pixel non trasparente, giusto? In questo caso devi analizzare la tua immagine e quando si fa clic su confronta le coordinate del mouse con il canale alfa pixel della tua immagine dato che JButton non ha tale caratteristica.

Se hai un pulsante rotondo, questo è esattamente ciò di cui hai bisogno:

  public class RoundButton extends JButton { public RoundButton() { this(null, null); } public RoundButton(Icon icon) { this(null, icon); } public RoundButton(String text) { this(text, null); } public RoundButton(Action a) { this(); setAction(a); } public RoundButton(String text, Icon icon) { setModel(new DefaultButtonModel()); init(text, icon); if(icon==null) return; setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); setContentAreaFilled(false); setFocusPainted(false); initShape(); } protected Shape shape, base; protected void initShape() { if(!getBounds().equals(base)) { Dimension s = getPreferredSize(); base = getBounds(); shape = new Ellipse2D.Float(0, 0, s.width, s.height); } } @Override public Dimension getPreferredSize() { Icon icon = getIcon(); Insets i = getInsets(); int iw = Math.max(icon.getIconWidth(), icon.getIconHeight()); return new Dimension(iw+i.right+i.left, iw+i.top+i.bottom); } @Override public boolean contains(int x, int y) { initShape(); return shape.contains(x, y); //or return super.contains(x, y) && ((image.getRGB(x, y) >> 24) & 0xff) > 0; } } 

JButton ha un metodo contains() . Sovrascrivi e chiamalo su mouseReleased ();

paintComponent() invece di paint() dipende se si paint() all’interno di XxxButtonUI o si paintComponent() override di paintComponent() , ma esiste l’opzione JButton # setIcon .