Problema con immagine background JPanel

di il
13 risposte

Problema con immagine background JPanel

Salve a tutti, sono marte (si proprio il pianeta ) e sono un nuovo arrivato in questa fantastica Comunity. Ciò che mi ha spinto a chiedere aiuto qui è un problema che purtroppo neanche con il sacro Google sono riuscito a risolvere!
In buona sostanza su un JFrame ho agganciato un pannello che a sua volta contiene un JScrollPane contenente un JTextPane, dopo aver inserito nel pannello (con overriding del metodo paint etc...) una immagine in background e aver settato scroll e textpane trasparenti per renderla visibile, vado a scrivere su quest'ultima attraverso i Document. Il punto è che una volta effettuato lo scroll, per via della trasparenza, si crea un casino a livello grafico. Ho risolto semplicemente effettuando un repaint() del pannello ma il problema (che più che problema è una questione estetica) ogni volta che lui effettua un repaint() questo risulta visibile all'utente, cioè si vede quest'immagine in background (insieme al testo del textpane) traslarsi per poi assestarsi. E' una cosa che si nota nell'arco di mezzo secondo ma chiaramente non è bello da vedere (immaginate se si presentasse in una chat come whatsapp, darebbe parecchio fastidio).
Forse mi sono dilungato un po troppo ma in sostanza la mia domanda è:

E' possibile rendere statica un'immagine in background (con questo settaggio e gerarchia di componenti, evitando repaint()) anche quando effettuo uno scroll?

Grazie in anticipo!!!

13 Risposte

  • Re: Problema con immagine background JPanel

    marte ha scritto:


    E' possibile rendere statica un'immagine in background (con questo settaggio e gerarchia di componenti, evitando repaint()) anche quando effettuo uno scroll?
    Ma dovrebbe già essere così (e non servono repaint()), nel senso che se: a) la immagine è sulla superficie di JPanel e b) JScrollPane è contenuto nel JPanel, allora lo sfondo è "fisso". JScrollPane NON può di certo spostarlo a fronte di scrolling del componente scrollato.

    Mostra del codice (probabilmente c'è qualcosa che non quadra). Nel frattempo faccio una prova io.
  • Re: Problema con immagine background JPanel

    Questo è il codice relativo alla classe che rappresenta il pannello
    public class MyPanel extends JPanel 
    {
        private Image backgroundImage = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/icons/background.png"));
        
        
        public MyPanel()
        {
            super();
            setOpaque(false);
        }
        
        @Override
            public void paint(Graphics g)
            {
                BufferedImage img = null;
                try {
                    setBackground(new Color(0,0,0,0));
                    img = ImageIO.read(getClass().getResource("/icons/background_compress.png"));
                    MediaTracker track = new MediaTracker(this);
                    track.addImage(img, 0);
                    track.waitForID(0);
                } catch (IOException | InterruptedException ex) {
                    System.out.println("Errore : "+ex.getMessage());
                }
                
                g.drawImage(img, 0, 0, null);
                super.paintComponents(g);
                this.repaint();
            }
            }
    Questo è il codice dei componenti:
    
    private void init_textPane()
        {
            textPane = new JTextPane();
            textPane.setEditable(false);
            textPane.setHighlighter(null);
            textPane.setBackground(new Color(0,0,0,0));
            textPane.setAutoscrolls(true);
            DefaultCaret caret = (DefaultCaret) textPane.getCaret();
            caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
            
            textPane.setEditorKit(new WrapEditorKit());
            textPane.setCaret(caret);
            
            scrollLog = new JScrollPane(textPane);
            scrollLog.setSize(new Dimension(790,525));
            scrollLog.setLocation(new Point(5,3));
            scrollLog.getVerticalScrollBar().setUI(new MyScrollBarUI());
            scrollLog.getHorizontalScrollBar().setUI(new MyScrollBarUI());
            
            scrollLog.getVerticalScrollBar().setAutoscrolls(true);
            scrollLog.setBorder(new MyLineBorder2(Color.black, 1, true));
            scrollLog.setBackground(new Color(0,0,0,0));
            scrollLog.setVisible(true);
            
        }

    Il tutto poi con
    
    protected void setPanels() 
        {
            panel = new MyPanel();
            
            panel.setLayout(null);
           
            panel.add(scrollLog);
            
            panel.setVisible(false);
            
        }
    
    
  • Re: Problema con immagine background JPanel

    marte ha scritto:


    public class MyPanel extends JPanel 
    [.....]
    
    Svariate cose non vanno.

    1) Nei componenti Swing il "contesto" di disegno è paintComponent, non paint. Il paint(Graphics) esiste ancora ma è molto più "a monte" nella fase di disegno. Quindi si dovrebbe tipicamente ridefinire paintComponent mentre paint lo puoi ridefinire ma solo se c'è davvero bisogno e "sai" veramente che cosa stai facendo.

    2) In un paint/paintComponent MAI fare caricamento di immagini così incondizionato (al massimo una-tantum, es.: se image è null, carica; così alla volta dopo non carichi più).

    3) Il MediaTracker non servirebbe a nulla, la ImageIO fornisce già la immagine completa, non c'è alcun bisogno di tracking.

    4) in un paint/paintComponent MAI fare un repaint()
  • Re: Problema con immagine background JPanel

    andbin ha scritto:


    marte ha scritto:


    public class MyPanel extends JPanel 
    [.....]
    
    Svariate cose non vanno.

    1) Nei componenti Swing il "contesto" di disegno è paintComponent, non paint. Il paint(Graphics) esiste ancora ma è molto più "a monte" nella fase di disegno. Quindi si dovrebbe tipicamente ridefinire paintComponent mentre paint lo puoi ridefinire ma solo se c'è davvero bisogno e "sai" veramente che cosa stai facendo.

    2) In un paint/paintComponent MAI fare caricamento di immagini così incondizionato (al massimo una-tantum, es.: se image è null, carica; così alla volta dopo non carichi più).

    3) Il MediaTracker non servirebbe a nulla, la ImageIO fornisce già la immagine completa, non c'è alcun bisogno di tracking.

    4) in un paint/paintComponent MAI fare un repaint()

    Grazie per la immediata risposta! Comunque mi hai dato delle utilissime delucidazioni, il repaint lo effettuavo perchè mi risolveva (male) il problema.In ogni caso proverò a modificare con quanto mi ha detto di fare e vediamo se riesco a risolvere!
  • Re: Problema con immagine background JPanel

    Ho modificato la classe in questo modo:
    public class MyPanel extends JPanel 
    {
        private BufferedImage backgroundImage;
        
        
        public MyPanel() throws IOException
        {
            super();
            this.backgroundImage = backgroundImage = ImageIO.read(getClass().getResource("/icons/background_compress.png"));
            
        }
        
        @Override
            public void paintComponent(Graphics g)
            {
                g.drawImage(backgroundImage, 0, 0, null);
                super.paintComponents(g);
               
            }
            }
    Il risultato non cambia, stesso problema. Posto le immaggini di quello che succede:

    Senza Scroll:
    [img]
  • Re: Problema con immagine background JPanel

    marte ha scritto:


            public void paintComponent(Graphics g)
            {
                g.drawImage(backgroundImage, 0, 0, null);
                super.paintComponents(g);
               
            }
            }
    Ci sono 2 questioni:
    - hai invocato super.paintComponents(g); questo NON è il paintComponent ma è un'altra cosa e non va invocato dal paintComponent.
    - dovresti prima invocare super.paintComponent(g) (nota, senza s finale) e poi fai il tuo drawImage
  • Re: Problema con immagine background JPanel

    andbin ha scritto:


    marte ha scritto:


            public void paintComponent(Graphics g)
            {
                g.drawImage(backgroundImage, 0, 0, null);
                super.paintComponents(g);
               
            }
            }
    Ci sono 2 questioni:
    - hai invocato super.paintComponents(g); questo NON è il paintComponent ma è un'altra cosa e non va invocato dal paintComponent.
    - dovresti prima invocare super.paintComponent(g) (nota, senza s finale) e poi fai il tuo drawImage
    Ho provato a fare come mi hai detto, ma il problema persiste .
  • Re: Problema con immagine background JPanel

    Ciao !

    Visto che il problema non sembra immediato da risolvere, sarebbe utile per noi avere più codice, per poter compilare ed eseguire direttamente il tuo sorgente con qualche modifica.

    Ovviamente puoi togliere tutto ciò che non è legato al problema, ma se riesci a creare un piccolo esempio (ma completo, senza bisogno di inserire gli import o scriverci classi/metodi che non fornisci) in cui riesci a riprodurre il problema, sarebbe molto più facile e veloce aiutarti !
  • Re: Problema con immagine background JPanel

    Ho riscritto le classi, basta fare uno scroll e il problema si visualizza immediatamente.
    Ecco la classe principale:
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Point;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextPane;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.DefaultCaret;
    import javax.swing.text.Document;
    
    public class Problema extends JFrame
    {
        private JPanel panel;
        private JTextPane tpane;
        private JScrollPane scroll;
        
        public Problema() throws BadLocationException
        {
            super("Problema");
            super.setSize(new Dimension(800,600));
            super.setLocationRelativeTo(null);
            setPanel();
            super.add(panel);
            super.setResizable(false);
            
            super.setDefaultCloseOperation(EXIT_ON_CLOSE);
            super.setVisible(true);
        }
        
        private void setPanel() throws BadLocationException
        {
            try {
                panel = new MyPanel();
            } catch (Exception ex) {
                System.out.println("Errore : "+ex.getMessage());
            }
            init_tpane();
            panel.setLayout(null);
            panel.add(scroll);
            panel.setVisible(true);
        }
        
        private void init_tpane() throws BadLocationException
        {
            tpane = new JTextPane();
            tpane.setEditable(false);
            tpane.setHighlighter(null);
            tpane.setBackground(new Color(0,0,0,0));
            tpane.setAutoscrolls(true);
            tpane.setForeground(Color.white);
            DefaultCaret caret = (DefaultCaret) tpane.getCaret();
            caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
            
            scroll = new JScrollPane(tpane);
            scroll.setSize(new Dimension(790,525));
            scroll.setLocation(new Point(5,3));
            scroll.setBackground(new Color(0,0,0,0));
            scroll.setVisible(true);
            
            Document doc = tpane.getDocument();
            String newline = System.getProperty("line.separator");
            for(int i=0; i<100; i++)
                doc.insertString(doc.getEndPosition().getOffset(), "prova"+newline, null);
        }
        
    }

    Classe MyPanel (Ho inserito un URL di un'immagine qualsiasi così è tutto già pronto):
    import java.awt.Graphics;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.net.URL;
    import javax.imageio.ImageIO;
    import javax.swing.JPanel;
    
    public class MyPanel extends JPanel 
    {
        private BufferedImage backgroundImage;
        
        
        public MyPanel() throws IOException, InterruptedException
        {
            super();
            this.backgroundImage = ImageIO.read(new URL("https://needswag.files.wordpress.com/2013/01/background.png"));
        }
        
        @Override
            public void paintComponent(Graphics g)
            {
                super.paintComponent(g);
                g.drawImage(backgroundImage, 0, 0, null);
            }
    }
  • Re: Problema con immagine background JPanel

    marte ha scritto:


    Ho riscritto le classi, basta fare uno scroll e il problema si visualizza immediatamente.
    Eh ... ho capito. Hai "giocato" un po' troppo con la trasparenza a livello del Color. Non va bene!

    Quindi togli:

    tpane.setBackground(new Color(0,0,0,0));
    scroll.setBackground(new Color(0,0,0,0));


    e poi ti bastano solo 3 cose:

    tpane.setOpaque(false);
    scroll.setOpaque(false);
    scroll.getViewport().setOpaque(false);
  • Re: Problema con immagine background JPanel

    andbin ha scritto:


    marte ha scritto:


    Ho riscritto le classi, basta fare uno scroll e il problema si visualizza immediatamente.
    Eh ... ho capito. Hai "giocato" un po' troppo con la trasparenza a livello del Color. Non va bene!

    Quindi togli:

    tpane.setBackground(new Color(0,0,0,0));
    scroll.setBackground(new Color(0,0,0,0));


    e poi ti bastano solo 3 cose:

    tpane.setOpaque(false);
    scroll.setOpaque(false);
    scroll.getViewport().setOpaque(false);
    Problema risolto .
    Comunque, semplicemente per informazione, tu sapresti perchè impostando la trasparenza in quel modo si vanno a generare quelle sovrapposizioni grafiche??
    In ogni caso grazie mille per la disponibilità e la professionalità andbin!
  • Re: Problema con immagine background JPanel

    marte ha scritto:


    Comunque, semplicemente per informazione, tu sapresti perchè impostando la trasparenza in quel modo si vanno a generare quelle sovrapposizioni grafiche??
    Guarda, di preciso non sono sicuro al 100% cioè dovrei andare a vedere i sorgenti di Swing per dare la spiegazione esatta ma vado a fare una ipotesi abbastanza ragionevole.
    Se metti SOLO un colore totalmente "trasparente" (alpha=0) senza mettere opaque false, per Swing il componente è comunque "opaco".
    All'inizio, al primissimo disegno della interfaccia, viene prima ovviamente disegnato lo sfondo del pannello con la tua bella immagine di sfondo. Poi viene disegnato il JScrollPane con il suo contenuto. Ma siccome il background è trasparente, effettivamente il disegno dello sfondo NON ha effetti (è come se verniciassi con una vernice trasparente!) e quindi JScrollPane "eredita" fisicamente lo sfondo del pannello.
    Ma JScrollPane "sa" di essere comunque opaco. E quando vai a scrollare mette molto probabilmente in atto delle ottimizzazioni per cui va copiare rettangoli del suo contenuto per effettuare lo scrolling. E questo "sporca" il suo sfondo.

    Insomma, è una situazione strana e ingannevole per Swing.

    Mentre invece mettere tutto NON opaco vuol dire per JScrollPane e company: guardate, NON disegnate nulla voi, aspettatevi che il vostro sfondo (che è quello del pannello in questo caso) venga sempre ridisegnato PRIMA di voi.

    marte ha scritto:


    In ogni caso grazie mille per la disponibilità e la professionalità andbin!
  • Re: Problema con immagine background JPanel

    Chiarissimo! sono convinto che il tuo ragionamento ci abbia azzeccato Sicuramente da ora starò più attento alle logiche "intrinseche" dei componenti. Ciao!
Devi accedere o registrarti per scrivere nel forum
13 risposte