2013-08-12 21 views
6

Sto lavorando a un'app utilizzando Nimbus Look and Feel. C'è una tabella e una colonna contiene pulsanti (usando lo Table Button Column from Rob Camick). Questo funziona, ma il risultato non è quello che mi aspettavo. Ho provato a sistemare l'aspetto, ma senza risultato.Imposta il pulsante "background" di un pulsante Nimbus

Quindi la domanda è: come si modifica lo "sfondo" (l'area esterna al rettangolo arrotondato) di un pulsante Nimbus? Preferibilmente in modo non hacky :-)

Uso del pulsante di colonna di tabella di default, il risultato appare così:

Buttons with incorrect background

Come si può vedere, lo sfondo (e con questo intendo l'area al di fuori del rettangolo arrotondato del pulsante) è errata per le righe dispari (bianche). Il codice che produce questa uscita è:

public Component getTableCellRendererComponent(
     JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) 
{ 
    if (isSelected) { 
     renderButton.setForeground(table.getSelectionForeground()); 
     renderButton.setBackground(table.getSelectionBackground()); 
    } else { 
     renderButton.setForeground(table.getForeground()); 
     renderButton.setBackground(table.getBackground()); 
    } 

    if (hasFocus) { 
     renderButton.setBorder(focusBorder); 
    } else { 
     renderButton.setBorder(originalBorder); 
    } 

    // <snip some code> 

    renderButton.setOpaque(true); 

    return renderButton; 
} 

il renderButton è un'istanza di una predefinita JButton. Ho provato scherzi con il colore di sfondo del pulsante, ma che non ha funzionato come mi aspettavo in un primo momento:

 Color alternate = (Color)LookAndFeel.getDesktopPropertyValue("Table.alternateRowColor", Color.lightGray); 
     Color normal = (Color)LookAndFeel.getDesktopPropertyValue("Table.background", Color.white); 
     if (row % 2 == 0) { 
      renderButton.setBackground(normal); 
     } else { 
      renderButton.setBackground(alternate); 
     } 

Questo produce:

Also incorrect button background

così questa volta i tasti quello che sembra giusto nella prima immagine ora è cattivo e viceversa. Gli sfondi interni del pulsante (le aree all'interno dei rettangoli arrotondati) sembrano avere il colore corretto in base alla proprietà del colore di sfondo (che è ciò che è realmente modificato con la chiamata setBackground()). Ma l'area esterna è completamente sbagliata. Va bene, cerchiamo di combinare le due cose:

 Color alternate = table.getBackground(); 
     Color normal = (Color)LookAndFeel.getDesktopPropertyValue("Table.background", Color.white); 
     if (row % 2 == 0) { 
      renderButton.setBackground(normal); 
     } else { 
      renderButton.setBackground(alternate); 
     } 

Il risultato:

Still incorrect buttons

Così ora il "background" ha un aspetto corretto, ma i pulsanti non assomigliano pulsanti Nimbus più. Come faccio a fare in modo che lo "sfondo" abbia il colore corretto con i pulsanti Nimbus?

+0

Hai provato a usare setOpaque (falso) (scusami per il cellulare quindi è difficile leggere tutto il codice) – MadProgrammer

+0

Sì I avere, con 'setOpaque (false)' lo sfondo è sempre bianco. – DarkDust

risposta

1

Non impostare lo sfondo su JButton. Usa JPanel per avvolgere JButton e impostare lo sfondo su JPanel. Questo sarebbe probabilmente ovvio se avessi usato più pulsanti in una colonna di JTable.

Per impostare il colore di sfondo corretto di JPanel ho fatto (si dovrebbe):

  1. Mantenere riferimento al renderer originale
  2. Let renderer originale rendere la propria componente (per ogni rendering)!
  3. Utilizzare lo sfondo del componente sottoposto a rendering per impostare lo sfondo di JPanel (per ogni rendering)!

In questo modo non c'è bisogno di scegliere il colore corretto da soli

Inoltre è necessario eseguire l'override paintComponent per dipingere correttamente sfondo bianco di JPanel:

@Override 
protected void paintComponent(Graphics g) { 
    Color background = getBackground(); 
    setBackground(new Color(background.getRGB())); 
    super.paintComponent(g); 
} 

Edit: come suggerisce @kleopatra non è necessario sovrascrivere paintComponent, impostare solo il colore di sfondo come non-uiresource (mostrato nell'esempio completo)

Ecco completo esempio:

import java.awt.Color; 
import java.awt.Component; 
import java.awt.Graphics; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTable; 
import javax.swing.UIManager; 
import javax.swing.UIManager.LookAndFeelInfo; 
import javax.swing.table.TableCellRenderer; 

public class Test { 
public static void main(String[] args) throws Throwable { 
    for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { 
     if ("Nimbus".equals(info.getName())) { 
      UIManager.setLookAndFeel(info.getClassName()); 
      break; 
     } 
    } 

    String[] columnNames = new String[]{"c1"}; 
    Object[][] data = new Object[4][1]; 
    data[0][0] = "First"; 
    data[1][0] = "Second"; 
    data[2][0] = "Third"; 
    data[3][0] = "Fourth"; 

    JTable table = new JTable(data, columnNames){ 
     @Override 
     public javax.swing.table.TableCellRenderer getCellRenderer(int row, int column) { 
      final TableCellRenderer ori = super.getCellRenderer(row, column); 
      final TableCellRenderer mine = new TableCellRenderer() { 
       @Override 
       public Component getTableCellRendererComponent(JTable table, Object value, 
         boolean isSelected, boolean hasFocus, int row, int column) { 
        Component c = ori.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 
        JPanel p = new JPanel(); 
        if(value == null){ 
         value = ""; 
        } 
        p.add(new JButton(value.toString())); 
        p.setBackground(new Color(c.getBackground().getRGB())); 
        return p; 
       } 
      }; 
      return mine; 
     }; 
    }; 
    table.setRowHeight(50); 
    JFrame f = new JFrame(); 
    f.add(table); 
    f.setVisible(true); 
    f.pack(); 
} 
} 

Risultato:

Result

+0

Ho un codice funzionante, ma non ho il tempo di postare esempi più dettagliati ora. Cosa intendi per non lavorare. Devi ancora impostare correttamente lo sfondo su JPanel. – Piro

+0

Ho aggiornato la mia risposta dopo alcuni test della cena. Alla fine sembra essere più semplice di quanto io sia (è difficile dire nel vecchio codice cosa sia importante) – Piro

+0

Sì, intendevo al momento del rendering. Sto creando JPanel per ogni rendering, ma il riutilizzo probabilmente funzionerebbe anche – Piro

2

Qui di seguito c'è un modo hacky, seguendo il suggerimento di @ Piro: utilizzando un JPanel con il tasto come componente figlio. Che di per sé è una buona idea, dato che non vogliamo veramente toccare le immagini di sfondo "interne" del pulsante.

Qui l'hack viene quando costringendo interni Nimbus non utilizzare sfondo predefinito un di JPanel per il riempimento nei invece usare lo sfondo della proposta esempio pannello Questo deve fare affidamento su dettagli di implementazione, in particolare il meccanismo di ricerca di un colore di sfondo. Questo accade in SynthStyle.getColor():

// If the developer has specified a color, prefer it. Otherwise, get 
// the color for the state. 
Color color = null; 
if (!id.isSubregion()) { 
    if (type == ColorType.BACKGROUND) { 
     color = c.getBackground(); 
    } 
    .... 
} 

if (color == null || color instanceof UIResource) { 
    // Then use what we've locally defined 
    color = getColorForState(context, type); 
} 

Tradotto: essa infatti interrogare il colore dell'istanza, ma escluderà con il default se il colore istanza è un UIResource - che in genere è il caso se usato come un renderer . Quindi il trucco (provato senza successo da SynthBooleanRenderer, ma questa è un'altra storia ;-) è quello di rendere il colore dell'istanza non a UIResource. Un ulteriore aspetto fastidioso è che essere UIResource è necessaria per garantire il colore striatura - che non è di tipo UIResource, haha ​​- essere applicato ... intuitivo, non è vero ...

public class RendererPanel implements TableCellRenderer { 

    private JComponent panel; 
    private JButton button; 
    public RendererPanel() { 
     panel = new JPanel(new BorderLayout()); 
     panel.setBorder(BorderFactory.createEmptyBorder(3, 10, 2, 10)); 
     button = new JButton(); 
     panel.add(button); 
    } 

    @Override 
    public Component getTableCellRendererComponent(JTable table, 
      Object value, boolean isSelected, boolean hasFocus, int row, 
      int column) { 
     // suggestion by Piro - use background of default 
     DefaultTableCellRenderer dt = (DefaultTableCellRenderer) table.getDefaultRenderer(Object.class); 
     dt.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 
     // first try: set the color as-is - doesn't work 
     // panel.setBackground(dt.getBackground()); 
     // second try: set color as not ui-resource 
     // that's working because at this point we already have the color that will be used 
     // let's hinder synth background color searching to fall back to component defaults 
     panel.setBackground(new Color(dt.getBackground().getRGB())); 
     // hack: unwrap ui-resource as needed 
     // updateBackground(isSelected ? table.getSelectionBackground() : table.getBackground(), row); 
     button.setText(String.valueOf(value)); 
     return panel; 
    } 

    private void updateBackground(Color color, int row) { 
     Color hack = row % 2 == 0 ? unwrap(color) : color; 
     panel.setBackground(hack); 
    } 

    private Color unwrap(Color c) { 
     if (c instanceof UIResource) { 
      return new Color(c.getRGB()); 
     } 
     return c; 
    } 

} 

Screenshot: con mod unwrap

enter image description here

Screenshot: utilizzando colori predefiniti (dal renderer installata per Object.class)

enter image description here

Il modo non hacky out potrebbe essere (non provate qui, ma ricordo di aver fatto una volta) per registrare una regione con lo stile, analogamente a quanto NimbusDefaults fa internamente:

register(Region.PANEL, "Table:\"Table.cellRenderer\""); 

problema qui è che non c'è nessun pubblico a farlo (o potrebbe essere che semplicemente non ne so abbastanza su Synth ;-)

+0

Funziona bene, grazie per il tuo sforzo! – DarkDust