2016-12-26 2 views
4

Je travaille sur la création d'un JPanel qui prend en charge la transparence, et qui est venu au problème que je ne suis pas sûr de la façon d'appliquer le même niveau de transparence à tous Component ajouté à ce panneau. Mon code à ce jour:Java Swing - Appliquer transparence aux composants sur transparent JPanel

package de.uebertreiberman.project.swing; 

import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.Rectangle; 

import javax.swing.JPanel; 

@SuppressWarnings("serial") 
public class JTransparancyPanel extends JPanel { 

    float opacity = 1.0f; 

    /**Constructor for JTransparentPanel 
    */ 
    public JTransparancyPanel() 
    { 
     super(); 
     this.setOpaque(false); 
    } 

    /**Getter for opacity value of panel 
    * 
    * @return float containing opacity value of frame (0-1) 
    */ 
    public float getOpacity() 
    { 
     return opacity; 
    } 

    /**Setter for opacity value of panel 
    * 
    * @param value as float for opacity of frame (0-1) 
    */ 
    public void setOpacity(float value) 
    { 
     opacity = value; 
     repaint(); 
    } 

    /**Converts opacity value (0-1) to opacity color (0-255) 
    * 
    * @param opacity as float opacity value (0-1) 
    * @return integer containing converted opacity value (0-255) 
    */ 
    public static int getOpacityColor(float opacity) 
    { 
     return (int)(opacity * 255); 
    } 

    /**Converts opacity color (0-255) to opacity value (0-1) 
    * 
    * @param opacity as integer value (0-255) 
    * @return float containing converted opacity value (0-1) 
    */ 
    public static float getOpacityValue(int opacity) 
    { 
     //Returns more or less the correct, capped value 
     //Just ignore it, it works, leave it :D 
     return capFloat((3.9216f*opacity)/1000f, 0.0f, 1.0f); 
    } 

    /**Returns float capped between minimum and maximum value 
    * 
    * @param value as original value 
    * @param min as minimum cap value 
    * @param max as maximum cap value 
    * @return float containing capped value 
    */ 
    public static float capFloat(float value, float min, float max) 
    { 
     if(value < min) value = min; 
      else if(value > max) value = max; 

     return value; 
    } 

    /**Merges color and opacity to new color 
    * 
    * @param bg as color for old color, only RGB will be used from that 
    * @return color with RGB from bg and A from opacity of frame 
    */ 
    Color getTransparencyColor(Color bg) 
    { 
     return new Color(getOpacityValue(bg.getRed()), getOpacityValue(bg.getGreen()), getOpacityValue(bg.getBlue()), opacity); 
    } 

    @Override 
    public void paintComponent(Graphics g) 
    { 
     //Draw transparent background before painting other Components 
     g.setColor(getTransparencyColor(getBackground())); 
     Rectangle r = g.getClipBounds(); 
     g.fillRect(r.x, r.y, r.width, r.height); 

     //Paint other components 
     super.paintComponent(g); 
    } 
} 

La partie importante est fondamentalement à la fin où j'overwride la méthode paintComponent(Graphics g).

Je prends simplement le backgroundcolor, appliquez la transparence et réglez-le.

Maintenant, je veux que tous les enfants composant de ce panneau deviennent également transparents, et je ne suis pas sûr de la manière la plus efficace de le faire.

Que suggéreriez-vous?

+0

qui est difficile sans écraser les méthodes 'paint' /' paintComponent' des composants respectifs. Rien n'empêche un composant enfant de remplir de façon flagrante sa zone avec une couleur solide et opaque. On pourrait essayer de tricher en peignant les composants enfants dans une image, puis en peignant cette image avec une opacité appropriée. Je vais essayer, mais c'est peut-être un peu compliqué. – Marco13

+0

Je n'ai pas essayé et je n'aurai pas le temps aujourd'hui, mais ne pouvez-vous pas simplement obtenir les composants ('Component [] components = jpanel.getComponents();'), les parcourir et définir leurs arrière-plans en utilisant 'getGraphics() .setBackgroundColour() '? – MrB

Répondre

3

J'ai trouvé une solution - essentiellement ce que j'ai déjà mentionné dans les commentaires: Le panneau n'est pas vraiment peint à l'écran. Au lieu de cela, il est peint dans une image, et cette image est peinte avec le AlphaComposite approprié.

OpacityPanel

(Le curseur peut être utilisé pour définir l'opacité du panneau)

Bien que cette solution est simple, générique et devrait essentiellement travailler avec tous les composants de l'enfant, il y a une chose que je n » J'aime à ce sujet: La nécessité de tordre le RepaintManager. Afin de s'assurer que l'opacité du panneau est appliquée (et que les composants enfant ne sont pas peints indépendamment, et avec leur opacité complète), le gestionnaire de repeints doit déclencher une repeinte du composant entier chaque fois qu'un composant enfant a été repeint. I pensé qu'il devrait y avoir une solution sans cette solution de contournement (par exemple, que cela pourrait être l'un des rares cas où l'annulation getGaphics sur le panneau pourrait être approprié), mais sans succès. Peut-être que les suspects habituels (camickr, Hovercraft etc.) trouvent une solution plus élégante et plus concise.

Jusque-là, voici le code comme MCVE:

import java.awt.AlphaComposite; 
import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.image.BufferedImage; 

import javax.swing.JButton; 
import javax.swing.JComponent; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JSlider; 
import javax.swing.JTree; 
import javax.swing.RepaintManager; 
import javax.swing.SwingUtilities; 

public class TransparencyPanelTest 
{ 
    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(() -> createAndShowGui()); 
    } 

    private static void createAndShowGui() 
    { 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.getContentPane().setLayout(new BorderLayout()); 

     OpacityPanel opacityPanel = new OpacityPanel(); 

     opacityPanel.setLayout(new BorderLayout()); 
     opacityPanel.add(new JLabel("Label"), BorderLayout.NORTH); 
     opacityPanel.add(new JTree(), BorderLayout.CENTER); 
     opacityPanel.add(new JButton("Button"), BorderLayout.SOUTH); 

     f.getContentPane().add(opacityPanel, BorderLayout.CENTER); 

     JSlider slider = new JSlider(0, 100, 50); 
     slider.addChangeListener(e -> 
      opacityPanel.setOpacity(slider.getValue()/100.0f)); 
     f.getContentPane().add(slider, BorderLayout.SOUTH); 

     f.setSize(500,500); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 

} 

class OpacityPanel extends JPanel 
{ 
    static 
    { 
     RepaintManager.setCurrentManager(new RepaintManager() 
     { 
      @Override 
      public void addDirtyRegion(
       JComponent c, int x, int y, int w, int h) 
      { 
       Component cc = c; 
       while (cc != null) 
       { 
        if (cc instanceof OpacityPanel) 
        { 
         OpacityPanel p = (OpacityPanel)cc; 
         super.addDirtyRegion(
          p, 0, 0, p.getWidth(), p.getHeight()); 
        } 
        cc = cc.getParent(); 
       } 
       super.addDirtyRegion(c, x, y, w, h); 
      } 
     }); 
    } 
    private float opacity = 0.5f; 
    private BufferedImage image; 

    public OpacityPanel() 
    { 
     setOpaque(false); 
    } 

    public void setOpacity(float opacity) 
    { 
     this.opacity = Math.max(0.0f, Math.min(1.0f, opacity)); 
     repaint(); 
    } 

    private void updateImage() 
    { 
     int w = Math.min(1, getWidth()); 
     int h = Math.min(1, getHeight()); 
     if (image == null || image.getWidth() != w || image.getHeight() != h) 
     { 
      image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 
     } 
     Graphics2D g = image.createGraphics(); 
     g.setColor(new Color(0,0,0,0)); 
     g.setComposite(AlphaComposite.SrcOver); 
     g.fillRect(0, 0, w, h); 
     g.dispose(); 
    } 

    @Override 
    protected void paintComponent(Graphics gr) 
    { 
     updateImage(); 
     Graphics2D imageGraphics = image.createGraphics(); 
     super.paintComponent(imageGraphics); 
     imageGraphics.dispose(); 

     Graphics2D g = (Graphics2D) gr; 
     g.setComposite(AlphaComposite.getInstance(
      AlphaComposite.SRC_OVER, opacity)); 
     g.drawImage(image, 0, 0, null); 
    } 

} 
+0

Cool, merci pour la réponse rapide. Je pense qu'il est un peu trop tard pour saisir complètement cette solution, mais je pense que je comprends ce que vous faites. Je vais essayer de mettre cela dans mon code demain. J'étudierai également plus en détail le RepaintManager, car cela m'intéresse maintenant. Mais encore merci, vous m'avez beaucoup aidé! – Uebertreiberman

+0

@Uebertreiberman Ne pas übertreiben avec la vitesse d'accepter une réponse ;-) Vous pourriez vouloir attendre un peu, peut-être quelqu'un d'autre propose une meilleure solution. Je pense toujours qu'il devrait y avoir un plus facile sans un gestionnaire de repeindre, mais les mécanismes de peinture Swing sont parfois assez complexes sous le capot, et cela semblait être un moyen facile d'atteindre le but désiré. – Marco13