Visualizzazione dei risultati da 1 a 5 su 5

Hybrid View

  1. #1
    Utente di HTML.it
    Registrato dal
    Oct 2014
    residenza
    Padova
    Messaggi
    361

    Difficoltà a "customizzare" JColorChooser

    Ciao a tutti, come da titolo sto incontrando qualche difficoltà a creare un JColorChooser personalizzato.

    Vorrei ottenere uno molto simile a quello presente in Excel, per intenderci, ecco qui un'immagine:

    ColorChooser.png

    In pratica vorrei un solo pannello colorato (come quello relativo alla saturazione nei pannelli HSV e HSL del JColorChooser demo del tutorial) , rimuovere tutte le tab e le JSlider del JColorChooser e mostrare solo le tre label relative all' RGB e i due riquadretti colorati come Preview.

    E' da un bel po' di tempo che cerco di capire dai sorgenti di JColorChooser, BasicColorChooserUI etc. se sia possibile ottenere ciò che voglio, credo di dover estendere BasicColorChooserUI come ho fatto in altri casi per altri componenti, sarebbe forse più comodo poter utilizzare il codice delle classi final come ColorModel, ColorPanel e così via, ma dovrei copiarlo a pezzi in classi mie per poterlo utilizzare e non mi pare molto fattibile la cosa.

    Cosa potete consigliarmi ? Una curiosità poi, guardando i sorgenti ho visto che JColorChooser nel costruttore richiama UpdateUI e SetUI, ho cercato di seguire la catena di metodi fino a UIManager e così via ma non trovo il punto in cui viene richiamata la BasicColorChooserUI, riuscite a chiarirmi questo passaggio ?

  2. #2
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Quote Originariamente inviata da Ansharja Visualizza il messaggio
    Vorrei ottenere uno molto simile a quello presente in Excel, per intenderci, ecco qui un'immagine:

    ColorChooser.png
    Farlo, certo che è possibile. Ma se non trovi una libreria che offre già quel layout, chiaramente lo devi sviluppare tu e non è proprio banale.

    Quote Originariamente inviata da Ansharja Visualizza il messaggio
    E' da un bel po' di tempo che cerco di capire dai sorgenti di JColorChooser, BasicColorChooserUI etc. se sia possibile ottenere ciò che voglio, credo di dover estendere BasicColorChooserUI come ho fatto in altri casi per altri componenti

    Cosa potete consigliarmi ? Una curiosità poi, guardando i sorgenti ho visto che JColorChooser nel costruttore richiama UpdateUI e SetUI, ho cercato di seguire la catena di metodi fino a UIManager e così via ma non trovo il punto in cui viene richiamata la BasicColorChooserUI, riuscite a chiarirmi questo passaggio ?
    Le classi della "UI" riguardano i Look&Feel ed è tutta una parte molto complessa che richiede competenze elevatissime su Swing. Sconsiglio di arrivare a quel livello ... a meno di avere quelle competenze (io ad esempio non le ho a quel livello).

    JColorChooser è fatto a "schede" e queste schede sono gestibili banalmente tramite:

    void addChooserPanel(AbstractColorChooserPanel panel)
    AbstractColorChooserPanel[] getChooserPanels()
    AbstractColorChooserPanel removeChooserPanel(AbstractColorChooserPanel panel)
    void setChooserPanels(AbstractColorChooserPanel[] panels)

    In sostanza, per definire una nuova scheda basta estendere AbstractColorChooserPanel.

    C'è anche il panello di "preview", gestibile con:

    JComponent getPreviewPanel()
    void setPreviewPanel(JComponent preview)
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  3. #3
    Utente di HTML.it
    Registrato dal
    Oct 2014
    residenza
    Padova
    Messaggi
    361
    Intanto grazie della risposta

    Per quanto riguarda le competenze sui Look&Feel ovviamente non le possiedo, comunque teoricamente sarebbe possibile prendere il codice sorgente delle classi come ColorPanel, ColorModel etc. (tutte classi non accessibili al di fuori del package) e usarlo nelle mie classi, ovviamente cambiando tutti i riferimenti al package?
    La considererei una situazione un po' al limite, ovviamente non mi va di fare le cose a caso...

    Per quanto riguarda i metodi del JColorChooser, li avevo già visti, infatti modificando l'esempio del tutorial riesco a mostrare un solo pannello (quello "HSV") e ho già cambiato a mio piacimento il preview panel, non inserisco questa parte sotto perché sarebbe codice inutile per ora.
    Ho anche rimosso il ColorPanel (in realtà è solo reso non visibile) e in questo modo in effetti rimane solo il diagramma per la scelta dei colori e la barra verticale.
    Ecco il codice usato:

    codice:
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.event.*;
    import javax.swing.colorchooser.*;
    public class ColorChooserDemo extends JPanel
    {
        private AbstractColorChooserPanel panel;
        private Container colorPanel;
        private JColorChooser tcc;
        public ColorChooserDemo()
        {
            tcc=new JColorChooser();
            AbstractColorChooserPanel[] oldPanels=tcc.getChooserPanels(),newPanels=new AbstractColorChooserPanel[1];
            for(int i=0;i<oldPanels.length;i++)
            {
                AbstractColorChooserPanel oldPanel=oldPanels[i];
                if(oldPanel.getDisplayName().equals("HSV"))panel=newPanels[0]=oldPanel;
            }
            tcc.setChooserPanels(newPanels);
            for(int i=0;i<panel.getComponentCount();i++)
            {
                Component c=panel.getComponent(i);
                String className=c.getClass().getName();
                if(className.equals("javax.swing.colorchooser.ColorPanel"))
                {
                    colorPanel=(Container)c;
                    for(int j=0;j<colorPanel.getComponentCount();j++)
                    {
                        Component child=colorPanel.getComponent(j);
                        // Setting the third JButton selected (the "Value" one)
                        //if(j==2){
                            //((JRadioButton)child).setSelected(true);
                            //child.repaint();                        
                        //}
                    }
                    colorPanel.setVisible(false);
                }
            }
            add(tcc,BorderLayout.CENTER);
        }
        private static void createAndShowGUI() {
            //Create and set up the window.
            JFrame frame = new JFrame("ColorChooserDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            //Create and set up the content pane.
            JComponent newContentPane = new ColorChooserDemo();
            newContentPane.setOpaque(true); //content panes must be opaque
            frame.setContentPane(newContentPane);
            //Display the window.
            frame.pack();
            frame.setVisible(true);
        }
        public static void main(String[] args) {
            //Schedule a job for the event-dispatching thread:
            //creating and showing this application's GUI.
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    createAndShowGUI();
                }
            });
        }
    }
    Non mi piace tanto andare a settare come non visibile il componente preso con getComponent(), ma essendo tutto inaccessibile non vedo molte altre soluzioni, cosa puoi consigliarmi ?

    Poi l'ultimo problema: ora vedo solo il pannello relativo alla tonalità per quanto riguarda l' HSV, mentre io vorrei selezionare quello relativo al Valore, che è molto simile al pannello che ho inserito prima nell'immagine.
    Ho provato a selezionare io il JRadioButton corrispondente e fare tutti i revalidate() e repaint() possibili ma il diagramma mostrato rimane lo stesso.

    Questo è il codice del listener che viene aggiunto ai JRadioButton nella classe ColorPanel (in realtà è il ColorPanel stesso che implementa ActionListener):

    codice:
     public void actionPerformed(ActionEvent event) {
            try {
                this.z = Integer.parseInt(event.getActionCommand());
                this.y = (this.z != 2) ? 2 : 1;
                this.x = (this.z != 0) ? 0 : 1;
                getParent().repaint();
            }
            catch (NumberFormatException exception) {
            }
        }
    Immagino che il diagramma rimanga lo stesso perché io non ho modo di andare a settare quelle variabili x,y e z, che sono inaccessibili, come tutta la classe del resto.
    E' corretto? E se sì come potrei aggirare il problema ?
    Ultima modifica di Ansharja; 30-04-2016 a 17:05

  4. #4
    Utente di HTML.it
    Registrato dal
    Oct 2014
    residenza
    Padova
    Messaggi
    361
    Per ora ho risolto il problema richiamando sul JRadioButton desiderato il metodo doClick().

    Ora provo a creare tutto il pannello come da immagine e poi inserisco il codice.

  5. #5
    Utente di HTML.it
    Registrato dal
    Oct 2014
    residenza
    Padova
    Messaggi
    361
    Eccomi qua, sono andato avanti a scrivere e sono molto vicino ad ottenere l'effetto desiderato.
    Posto subito il codice completo:


    codice:
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.colorchooser.AbstractColorChooserPanel;
    import javax.swing.event.*;
    import javax.swing.JSpinner.NumberEditor;
    import javax.swing.text.*;
    public class ColorChooserDemo extends JFrame
    {
        private JColorChooser colorChooser;
    	private BottomPanel bottomPanel;
        public ColorChooserDemo()
    	{
    		super("Color Chooser Demo");
    		setDefaultCloseOperation(EXIT_ON_CLOSE);
    		setResizable(false);
    		Container pane=getContentPane();
    		pane.setBackground(Color.WHITE);
    		/** Creating a custom JColorChooser in a try-catch block, if something goes wrong a standard one will be created */
    		try
    		{
    			Color defaultColor=new Color(114,226,226);
    			colorChooser=new JColorChooser(defaultColor);
    			AbstractColorChooserPanel panelHSV=null;
    			AbstractColorChooserPanel[] oldPanels=colorChooser.getChooserPanels(),newPanels=new AbstractColorChooserPanel[1];
    			for(int i=0;i<oldPanels.length;i++)
    			{
    				AbstractColorChooserPanel oldPanel=oldPanels[i];
    				if(oldPanel.getDisplayName().equals("HSV"))panelHSV=newPanels[0]=oldPanel;
    			}
    			colorChooser.setChooserPanels(newPanels);
    			((JComponent)((JComponent)panelHSV.getParent()).getParent()).setBackground(Color.WHITE);
    			panelHSV.setBackground(Color.WHITE);
    			// Removing ColorPanel component from ColorChooserPanel, only diagrams will be visible
    			Component[] internalComponents=panelHSV.getComponents();
    			for(int i=0;i<internalComponents.length;i++)
    			{
    				Component c=internalComponents[i];
    				if(c.getClass().getName().equals("javax.swing.colorchooser.ColorPanel"))
    				{
    					Container colorPanel=(Container)c;
    					for(int j=0;j<colorPanel.getComponentCount();j++)
    					{
    						Component child=colorPanel.getComponent(j);
    						// Setting the third JRadioButton selected (the "Value" one)
    						if(child instanceof JRadioButton)
    						{
    							((JRadioButton)colorPanel.getComponent(j+2)).doClick();
    							break;
    						}
    					}
    					colorPanel.setVisible(false);
    				}
    			}
    			// Setting diagrams borders and listeners
    			DiagramListener listener=new DiagramListener();
    			JComponent firstDiagram=(JComponent)internalComponents[internalComponents.length-2],secondDiagram=(JComponent)internalComponents[internalComponents.length-1];
    			firstDiagram.addMouseListener(listener);
    			firstDiagram.addMouseMotionListener(listener);
    			secondDiagram.addMouseListener(listener);
    			secondDiagram.addMouseMotionListener(listener);
    			firstDiagram.setBorder(new CompoundBorder(new EmptyBorder(0,4,0,4),new LineBorder(Color.GRAY)));
    			secondDiagram.setBorder(new LineBorder(Color.GRAY));
    			// removing previous preview panel and adding a new one
    			colorChooser.setPreviewPanel(new JPanel());
    			pane.add(bottomPanel=new BottomPanel(defaultColor),BorderLayout.SOUTH);
    		}
    		catch(Exception ex){
    			colorChooser=new JColorChooser();
    		}
    		pane.add(colorChooser,BorderLayout.NORTH);
    		pack();
    		setLocationRelativeTo(null);
        }
    	private class DiagramListener implements MouseListener,MouseMotionListener
    	{
    		public void mouseDragged(MouseEvent e){
    			bottomPanel.setLabelValues();
    			bottomPanel.getPreviewPanel().repaint();
    		}
    		public void mousePressed(MouseEvent e){
    			bottomPanel.setLabelValues();
    			bottomPanel.getPreviewPanel().repaint();
    		}
    		public void mouseClicked(MouseEvent e){}
    		public void mouseReleased(MouseEvent e){}
    		public void mouseEntered(MouseEvent e){}
    		public void mouseExited(MouseEvent e){}
    		public void mouseMoved(MouseEvent e){}
    	}
    	private class BottomPanel extends JPanel
    	{
    		private Color oldColor;
    		private JFormattedTextField red,green,blue;
    		private JPanel previewPanel;
    		public BottomPanel(Color color)
    		{
    			super(new BorderLayout());
    			oldColor=color;
    			if(oldColor==null)oldColor=Color.WHITE;
    			Font labelFont=new Font("Arial",0,11);
    			JLabel redLabel=new StyledLabel("Red :",labelFont),greenLabel=new StyledLabel("Green :",labelFont),blueLabel=new StyledLabel("Blue :",labelFont);
    			Dimension preferred=greenLabel.getPreferredSize();
    			redLabel.setPreferredSize(preferred);
    			blueLabel.setPreferredSize(preferred);
    			JPanel labelsPanel=new JPanel(new BorderLayout());
    			labelsPanel.add(labelledSpinnerPanel(redLabel,oldColor.getRed(),1),BorderLayout.NORTH);
    			labelsPanel.add(labelledSpinnerPanel(greenLabel,oldColor.getGreen(),2),BorderLayout.CENTER);
    			labelsPanel.add(labelledSpinnerPanel(blueLabel,oldColor.getBlue(),3),BorderLayout.SOUTH);
    			add(labelsPanel,BorderLayout.WEST);
    			add(previewPanel=new PreviewPanel(),BorderLayout.CENTER);
    		}
    		public void changeColor()
    		{
    			colorChooser.setColor(new Color((int)red.getValue(),(int)green.getValue(),(int)blue.getValue()));
    			previewPanel.repaint();
    		}
    		public JPanel getPreviewPanel()
    		{
    			return previewPanel;
    		}
    		public JPanel labelledSpinnerPanel(JLabel label,int initialValue,int order)
    		{
    			JPanel panel=new JPanel(new FlowLayout(FlowLayout.LEFT,10,5));
    			panel.setBackground(Color.WHITE);
    			panel.add(label);
    			JSpinner spinner=new JSpinner(new SpinnerNumberModel(initialValue,0,255,1));
    			spinner.setBorder(new LineBorder(Color.GRAY));
    			spinner.addChangeListener(new SpinnerListener());
    			JFormattedTextField field=((NumberEditor)spinner.getEditor()).getTextField();
    			if(order==1)red=field;
    			else if(order==2)green=field;
    			else blue=field;
    			field.setFormatterFactory(new DefaultFormatterFactory(new Formatter(field)));
    			field.setPreferredSize(new Dimension(25,18));
    			panel.add(spinner);
    			return panel;
    		}
    		public void setLabelValues()
    		{
    			Color selected=colorChooser.getColor();
    			red.setValue(selected.getRed());
    			green.setValue(selected.getGreen());
    			blue.setValue(selected.getBlue());
    		}
    		private class Formatter extends DefaultFormatter
    		{
    			DocumentFilter filter;
    			JFormattedTextField field;
    			public Formatter(JFormattedTextField field)
    			{
    				this.field=field;
    				filter=new Filter();
    			}
    			protected DocumentFilter getDocumentFilter()
    			{
    				return filter;
    			}
    			private class Filter extends DocumentFilter
    			{
    				public void replace(FilterBypass fb,int offset,int length,String text,AttributeSet attrs)throws BadLocationException
    				{
    					Document document=fb.getDocument();
    					String oldText=document.getText(0,document.getLength()),newText=oldText.substring(0,offset)+text+oldText.substring(offset+length);
    					try{
    						int value=Integer.valueOf(newText);
    						if(value>=0&&value<=255)
    						{
    							super.replace(fb,offset,length,text,attrs);
    							changeColor();
    						}
    					}
    					catch(NumberFormatException e){
    						Toolkit.getDefaultToolkit().beep();
    					}
    				}
    				public void remove(FilterBypass fb,int offset,int length)throws BadLocationException
    				{
    					Document document=fb.getDocument();
    					String text=document.getText(0,document.getLength());
    					if(text.length()-length==0)field.setValue(0);
    					else super.remove(fb,offset,length);
    					changeColor();
    				}
    			}
    		}
    		private class PreviewPanel extends JPanel
    		{
    			public PreviewPanel()
    			{
    				setBackground(Color.WHITE);
    			}
    			protected void paintComponent(Graphics g)
    			{
    				super.paintComponent(g);
    				g.setColor(Color.GRAY);
    				g.drawRect(20,20,51,51);
    				g.setColor(oldColor);
    				g.fillRect(21,21,50,25);
    				g.setColor(colorChooser.getColor());
    				g.fillRect(21,46,50,25);
    			}
    		}
    		private class SpinnerListener implements ChangeListener
    		{
    			public void stateChanged(ChangeEvent e){
    				changeColor();
    			}
    		}
    		private class StyledLabel extends JLabel
    		{
    			public StyledLabel(String text,Font f)
    			{
    				super(text,RIGHT);
    				setFont(f);
    			}
    		}
    	}
    	public static void main(String[] a)
    	{
    		SwingUtilities.invokeLater(new Runnable(){
                public void run(){
                    new ColorChooserDemo().setVisible(true);
                }
            });
        }
    }

    Ed ecco un'immagine di come appare il frame su windows 7:

    ColorChooser.jpg


    Vorrei qualche consiglio su come migliorare il codice usato, in particolare su questi punti (ma qualsiasi osservazione è bene accetta) :


    - Il difetto/carenza principale che riscontro finora è la corrispondenza tra i valori degli RGB presenti nei JFormattedTextField e l'aggiornarsi del colore del JColorChooser.
    Infatti trascinando il cursore sul diagramma o utilizzando i bottoncini del JSpinner il colore si aggiorna sempre, mentre digitando le cifre sul JFormattedTextField questo non avviene sempre.
    Eppure il metodo richiamato è sempre lo stesso... Come posso sincronizzare meglio tutti i vari cambiamenti?
    - Va bene la parte riguardante il filtro dei JFormattedTextField e la creazione del colorchooser customizzato? Per il color chooser il ciclo interno di selezione dei componenti potrebbe non essere il massimo, spero che inserire il tutto in un blocco try-catch e creare un JColorChooser standard in caso di errore sia sufficiente.
    - Come valutate l'incapsulamento delle varie classi private?

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.