PDA

View Full Version : [JAVA]


julianross1983
30-10-2006, 00:55
NON RIESCO AD IMPLEMENTARE IL "TABLECELLEDITOR",PER GESTIRE IL TRASCINAMENTO DI UNA JLABEL CONTENUTA IN PANNELLO CHE A SUA VOLTA E' CONTENUTO IN UNA CELLA DI UNA JTABLE.....COME SI PUO' RISOLVERE?ESEMPIO!GRAZIE

PGI-Bis
30-10-2006, 13:27
Come dissi nell'altro thread, passare per l'editor è una delle soluzioni. L'altra, che propongo qui, usa l'intera tabella come sorgente e destinazione di eventi drag and drop.

L'esempio è molto specifico e meccanicamente inutile al tuo problema. Inutile perchè il trasferimento dipende dai componenti concretamente coinvolti e, in particolare, dalla distribuzione degli spazi nella tabella e nei pannelli contenuti come celle di quella tabella.

Dovresti comunque essere in grado di applicare un modello simile alla tua situazione concreta.

package tab;

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

/** Proietta una cella il cui valore è un JPanel usando lo stesso
JPanel come proiettore */
public class PanelRenderer implements TableCellRenderer {

public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
{
return (JPanel)value;
}
}

package tab;

import java.awt.*;
import javax.swing.*;

/** Un pannello che contiene tre JLabel */
public class TriLabel extends JPanel {
private JLabel nameLabel = new JLabel();
private JLabel surnameLabel = new JLabel();
private JLabel roleLabel = new JLabel();

/** Inizializza un TriLabel assegnando alle tre etichette
i testi name, surnam e role */
public TriLabel(String name, String surname, String role) {
super(new GridLayout(3, 1, 2, 2));
nameLabel.setText(name);
surnameLabel.setText(surname);
roleLabel.setText(role);
add(nameLabel);
add(surnameLabel);
add(roleLabel);
}

/** Restituisce l'etichetta che occupa la regione di
spazio a cui appartiene il punto p. */
public JLabel getLabelAt(Point p) {
/* Il layout è un GridLayout. L'altezza di ogni
etichetta è uguale a circa un terzo dell'altezza
di tutto il pannello. */
Dimension dim = getPreferredSize();
/* La divisione intera p.y / (altezza di un singolo elemento)
restituisce l'indice (da 0 a 2 estremi inclusi) dell'etichetta.
0 è nameLabel (perchè è la prima inserita nel pannello)
1 è surnameLabel(perchè è la seconda inserita nel pannello)
2 è roleLabe (perchè è la terza inserita nel pannello) */
int labelIndex = p.y / (dim.height / 3);
return
labelIndex == 0 ?
nameLabel :
labelIndex == 1 ?
surnameLabel :
roleLabel;
}
}

package tab;

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class MyTable extends JTable {
private PanelRenderer panelRenderer = new PanelRenderer();

/** Se la cella in posizione (row, column) contiene un JPanel,
restituisce un panelRenderer (uno per tutte). Altrimenti
restituisce il proiettore predefinito da JTable */
public TableCellRenderer getCellRenderer(int row, int column) {
Object value = getValueAt(row, column);
return
value instanceof JPanel ?
panelRenderer :
super.getCellRenderer(row, column);
}

/** L'altezza delle righe è pari all'altezza della più alta
delle righe. Alcune celle possono (dovrebbero) contentere
dei componenti. In questo caso l'altezza esaminata è l'altezza
preferita di quel componente. Il conto è particolarmente
dispendioso e andrebbe affrontato in modo diverso (un caching
del valore ed un nuovo calcolo solo se intervengano mutazioni
di un componente appartenente al modello sarebbe già meglio) */
public int getRowHeight() {
int height = super.getRowHeight();
/* Non sapendo in che colonna ed in quali righe si trovino
i JPanel devo esaminarle tutte. */
for(int row = 0; row < getRowCount(); row++) {
for(int col = 0; col < getColumnCount(); col++) {
Object value = getValueAt(row, col);
if(value instanceof Component) {
Component comp = (Component)value;
Dimension dim = comp.getPreferredSize();
if(dim.height > height) {
height = dim.height;
}
}
}
}
return height;
}

/** Per semplicità rendo insensibili tutte le celle al
sistema predefinito di mutazione del valore (il doppio click) */
public boolean isCellEditable(int row, int col) {
return false;
}
}

package tab;

import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;

public class DNDManager {
private JTable table;

/* Il metodo dragGestureRecognized di questo
DragGestureListener è invocato quando la piattaforma
rilevi un tentativo di trascinamento */
private DragGestureListener dragHandler = new DragGestureListener() {

public void dragGestureRecognized(DragGestureEvent e) {
/* Il componente registrato come "attivatore" del trascinamento
è la tabella intera. Noi dobbiamo verificare se il punto da cui
parte il trascinamento corrisponda ad una cella che contiene un
TriLabel */
Point origin = e.getDragOrigin();
int row = table.rowAtPoint(origin);
int col = table.columnAtPoint(origin);
TriLabel component = getTriLabelAt(row, col);

if(component != null) {
/* Nel punto di trascinamento c'è un TriLabel */
/* Ora dobbiamo prendere l'etichetta del TriLabel che
si trova nel punto di trascinamento */
Point p = convertToCellPoint(origin, row, col);
JLabel label = component.getLabelAt(p);
/* Trovata l'etichetta trasciniamo il suo testo */
startTextDrag(e, label.getText());
}
}
};

/* Il metodo drop di questo DropTargetAdapter è invocato quando la
piattaforma rilevi un tentativo di rilascio */
private DropTargetListener dropHandler = new DropTargetAdapter() {
public void drop(DropTargetDropEvent e) {
/* Il componente registrato come "attivatore" del rilascio è
la tabella. Noi dobbiamo verificare se il punto in cui si
verifica il rilascio corrisponda ad una cella che contiene
un TriLabel */
Point destination = e.getLocation();
int row = table.rowAtPoint(destination);
int col = table.columnAtPoint(destination);
TriLabel component = getTriLabelAt(row, col);
if(component != null && e.isDataFlavorSupported(DataFlavor.stringFlavor)) {
/* Nel punto di rilascio c'è un TriLabel e i dati rilasciati
sono di un tipo testo: possiamo iniziare il rilascio */
e.acceptDrop(DnDConstants.ACTION_COPY);
/* Dal punto di rilascio relativo alla tabella passiamo
al punto di rilascio relativo al pannello contenuto nella
cella della tabella */
Point p = convertToCellPoint(destination, row, col);
/* Iniziamo il trasferimento del testo */
startTextDrop(e, p, component, row, col);
} else {
/* Nel punto di rilascio non c'è un TriLabel o i dati non sono
di tipo testo: rifiutiamo il rilascio */
e.rejectDrop();
}
}
};

/* Restituisce un punto che rappresenta le coordinate di p
nello spazio di coordinate della cella (row, col) */
private Point convertToCellPoint(Point p, int row, int col) {
Rectangle cellRect = table.getCellRect(row, col, true);
return new Point(
p.x - cellRect.x,
p.y - cellRect.y);
}

/* Restituisce il TriLabel corrispondente alla cella (row, col)
o null se quella cella non contenga un TriLabel */
private TriLabel getTriLabelAt(int row, int col) {
TriLabel result = null;
Object value = table.getValueAt(row, col);
if(value instanceof TriLabel) {
result = (TriLabel)value;
}
return result;
}

/* Tenta di rilasciare il testo contenuto nel transferable
associato a dropEvent. Il punto dropPoint è relativo
all'origine del componente destination. I valori row e col
sono gli indici della cella in cui è contenuto il componente
destination e servono per costringere la tabella ad aggiornare
il contenuto di quella cella una volta che il trascinamento
sia completato. L'invocazione di questo metodo presuppone che
il tipo di dato trasferito sia compatibile con
DataFlavor.stringFlavor */
private void startTextDrop(DropTargetDropEvent dropEvent,
Point dropPoint, TriLabel destination, int row, int col)
{
/* Un "drop" termina con una notifica di completamento
avvenuto con o senza successo. Questo valore è usato al
termine di questo metodo per notificare l'esito del
rilascio. */
boolean success = false;
try {
/* Ottiene la capsula dei dati che si vorrebbero
rilasciare */
Transferable t = dropEvent.getTransferable();
/* In accordo alla precondizione di compatibilità con
DataFlavor.stringFlavor, ottiene i dati trasferiti. Qui
può (ma non dovrebbe) prodursi un UnsupportedFlavorException
ed un ClassCastException. */
String text = (String)t.getTransferData(DataFlavor.stringFlavor);
/* Ottiene dal TriLabel destination l'etichetta che si
trova nel punto dropPoint (che è relativo alla posizione del
componente nella tabella e non all'origine della tabella) */
JLabel label = destination.getLabelAt(dropPoint);
/* Imposta il testo di quell'etichetta */
label.setText(text);
/* A questo punto deve far si che la tabella aggiorni la
proiezione della cella (row, col). Per farlo passa attraverso
il modello: imposta per la cella (row, col) lo stesso valore
che c'era prima (lo stesso TriLabel). Come conseguenza di un
setValueAt, anche se il valore è lo stesso di prima, il modello
notifica alla vista-tabella che qualcosa è cambiato e la
vista-tabella è responsabile dell'aggiornamento visivo
di quella cella. */
Object value = table.getValueAt(row, col);
DefaultTableModel model = (DefaultTableModel)table.getModel();
model.setValueAt(value, row, col);
/* A questo punto possiamo ritenere completato con
successo il trasferimento */
success = true;
} catch(UnsupportedFlavorException ex) {
success = false;
} catch(ClassCastException ex) {
success = false;
} catch(java.io.IOException ex) {
success = false;
} finally {
dropEvent.dropComplete(success);
}
}

/* Crea uno StringSelection (un Transferable per String) e
notifica l'inizio di un evento di trascinamento che trasporta
il testo text */
private void startTextDrag(DragGestureEvent e, String text) {
StringSelection transferable = new StringSelection(text);
e.startDrag(DragSource.DefaultCopyDrop, transferable);
}

/** Attiva questo gestore del drag and drop per il MyTable in
argomento. */
public void bind(MyTable table) {
/* Crea e connette un attivatore degli eventi "drag". Questo
fa sì che quando la piattaforma rilevi un tentativo di trascinamento
che interessi (per posizione) la tabella "table" sia invocato il metodo
dragGestureRecognized del DragGestureListener "dragHandler" */
DragSource ds = DragSource.getDefaultDragSource();
ds.createDefaultDragGestureRecognizer(
table,
DnDConstants.ACTION_COPY,
dragHandler);

/* Crea e connette un attivatore degli eventi "drop". Questo
fa sì che quando la piattaforma rilevi un tentativo di
rilascio che interessi (per posizione) la tabella "table"
sia invocato il metodo "dropt" del DropTargetListener
"dropHandler" */
DropTarget dt = new DropTarget(table, dropHandler);
this.table = table;
}
}

package tab;

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class Main {

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
start();
}
});
}

public static void start() {
/* Un po' di stringhe e un po di JPanel con etichette */
Object[][] data = {
{ "Barcamena Spa", new TriLabel("Mario", "Rossi", "Scafista"), },
{ new TriLabel("Franco", "Neri", "Estorsore"), "Taglieggio Srl", },
{ "Zompo scarl", "Nessuno", },
};
Object[] headers = {
"A", "B",
};

/** Creo una tabella con un DefaultTableModel contenente
i dati su generati */
MyTable table = new MyTable();
TableModel model = new DefaultTableModel(data, headers);
table.setModel(model);

/* JTable usa il mouse per selezionare le celle. Il DragAndDrop
usa gli stessi eventi per il trascinamento, lo spostamento e il
rilascio. Uno dei due sistemi deve essere modificato. Escludo
in toto quelli di JTable */
table.setCellSelectionEnabled(false);
table.setRowSelectionAllowed(false);

/** Creo un gestore del drag and drop per questo particolare
tipo di tabella e lo "attivo" */
new DNDManager().bind(table);

JScrollPane scroller = new JScrollPane(table);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(scroller, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}

L'esempio propone una tabella in cui alcune celle sono dei TriLabel. Un TriLabel è un JPanel che contiene tre JLabel. Le stringhe presentate dalle etichette contenute in questi TriLabel sono mutabili attraverso il drag and drop.