2016-10-07 3 views
1

Meine Hauptklasse ist ein Fenster mit grafischen Komponenten einschließlich einer JTable.JTable + JPopupMenu + ActionListener = Albtraum

Ich habe eine öffentliche Klasse ContextMenu erstellt, die eine benutzerdefinierte Implementierung eines JPopupMenu ist und mehrere JMenuItem enthält.

Ich habe einen MouseListener auf meiner JTable registriert, um eine Instanz von ContextMenu anzuzeigen, wenn ein Rechtsklick erkannt wird.

Das Problem ist das folgende: "Wie übergibt man die ausgewählten Zeilen an verschiedene Funktionen entsprechend der gewählten JMenuItem?"

Die offensichtliche Antwort wäre Action auf meinem JMenuItem zu setzen, aber nicht vergessen, dass JTable ist in einer anderen Klasse/Objekt als die JPopupMenu.

Einige Code-Snipets sind mehr als tausend Worte.

public class Tab implements ITab { 
private ContextMenu contextMenu; 
private JTable table; 
private List<SomeObject> toProcess; 
--- code -- 
private JScrollPane drawScrollTable() { 
     Object columns[] = { 
      "something", 
      "somethingElse" 
     }; 
     Object rows[][] = {}; 
     table = new JTable(new DefaultTableModel(rows, columns)); 
     JScrollPane scrollPane = new JScrollPane(table); 

     table.setSelectionForeground(Color.BLACK); 
     table.addMouseListener(new MouseAdapter() {   
      @Override 
      public void mouseReleased(MouseEvent e) { 
       int selectedRow = table.rowAtPoint(e.getPoint()); 

       if (selectedRow >= 0 && selectedRow < table.getRowCount()) { 
        if (!table.getSelectionModel().isSelectedIndex(selectedRow)) { 
         table.setRowSelectionInterval(selectedRow, selectedRow); 
        } 
       } 

       if (e.isPopupTrigger() && e.getComponent() instanceof JTable) { 
        this.show(e); 
       } 
      } 

      private void show(MouseEvent e){ 
       contextMenu.show(e.getComponent(), e.getX(), e.getY()); 
      } 
     }); 

     return scrollPane; 
    } 
-- code -- 
} 

public class ContextMenu extends JPopupMenu { 
    JMenuItem item; 

    public ContextMenu(IBurpExtenderCallbacks callbacks){ 
     this.item= new JMenuItem("item"); 

     this.item(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) {  
       // Do action involving the selected row, even better if possible action involving the value hold in the column 0 of the selected row and the toProcess private field 
      } 
     }); 

     add(item); 
    } 
} 

Ich weiß nicht, ob das, was ich frage, möglich ist.

+0

Viel besser als Code-Schnipsel veröffentlichen - erstellen und eine gültige [MCVE] veröffentlichen, das ist ein ** klein ** übersetzbar und lauffähiges Programm, das genaue * Problem für uns Ihre * zeigt. Wenn Sie dies tun können, wird es erheblich Ihre Chancen verbessern, eine anständige Antwort zu bekommen. Beachten Sie, dass der gesamte Code und die Kommunikation hier in Ihrer ursprünglichen Frage und nicht in einem Link erfolgen sollten. –

Antwort

1

Die MouseListener sollte für Ihr ContextMenu alle relevanten Informationen sammeln, verpacken sie in einem Transport (Innen privat?) Klasse und übergeben Sie diese Informationen vor dem eigentlichen show in dem Kontextmenü.

table.addMouseListener(new MouseAdapter() {   
     @Override 
     public void mouseReleased(MouseEvent e) { 
      int selectedRow = table.rowAtPoint(e.getPoint()); 

      if (selectedRow >= 0 && selectedRow < table.getRowCount()) { 
       if (!table.getSelectionModel().isSelectedIndex(selectedRow)) { 
        table.setRowSelectionInterval(selectedRow, selectedRow); 
       } 
      } 

      if (e.isPopupTrigger() && e.getComponent() instanceof JTable) { 
       this.show(e); 
      } 
     } 

     private void show(MouseEvent e){ 
      int clickedRow=table.rowAtPoint(e.getPoint()); 
      int clickedCol=table.columnAtPoint(e.getPoint()); 
      Object data=table.getValueAt(row, i); 

      DataClickedOnTable transportMeThere=new DataClickedOnTable(
       table, data, clickedRow, clickedColumn 
      ); 
      contextMenu.setDataFromTable(transportMeThere); 
      contextMenu.show(e.getComponent(), e.getX(), e.getY()); 
     } 
    }); 
    ///.... 
    ///... 


// Just an example of structure transporting the data 
// Add whatever data members are relevant 
private /* inner */ class DataClickedOnTable { 
    public TestTable source; 
    public Object data; 
    public int row; 
    public int column; 

    public DataClickedOnTable(
    TestTable source, Object data, int row, int col 
    ) { 
    this.source=source; 
    this.data=data; 
    this.col=col; 
    this.row=row; 
    } 
} 
public class ContextMenu extends JPopupMenu { 
    JMenuItem item1; 
    JMenuItem item2; 

    Object dataFromTable; // make it an Integer 

    public ContextMenu(IBurpExtenderCallbacks callbacks){ 
     this.item1 = new JMenuItem("item"); 
     this.item2 = new JMenuItem("item"); 

     this.item1(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) {  
       // You already have the relevant data in the dataFromTable 
       // Do want you need in this context 
      } 
     }); 
     this.item2(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) {  
       // You already have the relevant data in the dataFromTable 
       // Do want you need to do in this context 
      } 
     }); 

     add(item1); 
     add(item2); 
    } 

    void setDataFromTable(DataClickedOnTable data) { 
     this.dataFromTable=data; 
     // filter possible actions based on the received data - some 
     // actions are possible, some won't. 
     // Example: 
     this.item1.setEnabled(null!=data && (data.row % 2)==0); 
     this.item2.setEnabled(
      null!=data 
     && ((data.row % 2)==1 || data.data instanceof Number) 
    ); 

    } 
} 
+1

Vielen Dank für diese Lösung, ich denke, es könnte mein Problem lösen und mir erlauben, die Entwicklung meines Projekts fortzusetzen. Ich verstehe jedoch nicht, warum 'DataClickedOnTable' eine private innere Klasse sein sollte. Innere Klasse meiner öffentlichen Klasse 'Tab' oder innere Klasse meiner öffentlichen Klasse 'ContextMenu'? –

+0

@ AresS31 "warum DataClickedOnTable sollte eine private innere Klasse sein." nur ein Vorschlag, nicht zwingend erforderlich. Aber da es Daten nur zwischen Ihrem MouseListener und ContextMenu transportieren soll (das sollten beide inneren Klassen des Rahmens sein, der Ihre Tabelle unterstützt), ist es eine gute Übung, es nicht für anderen Code zugänglich zu machen, der nicht ... ummmm. .. muss wissen. –

+1

Ok, ich sehe mein ContextMenu ist keine innere Klasse Ich habe es zu einer öffentlichen Klasse gemacht Deshalb habe ich die Frage gestellt, denn als ich es privat gemacht habe, hatte das ContextMenu keinen Zugriff darauf. Ich habe alles zu einer öffentlichen Klasse gemacht. Ich werde das mal nach dem Projekt rüberräumen. Danke noch einmal! –

1

Dirty Weg: Referenzen übergeben.
Reiniger Art und Weise: strukturieren Sie Ihr Programm über MCV

Meine Minimal, Complete, and Verifiable example der schmutzige Art und Weise:

import java.awt.BorderLayout; 
import java.awt.event.*; 

import javax.swing.*; 
import javax.swing.table.DefaultTableModel; 

public class TableTest extends JPanel { 
    private TableClass tableClass = new TableClass(); 

    public TableTest() { 
     setLayout(new BorderLayout()); 
     add(tableClass); 
    } 

    private static void createAndShowGui() { 
     JFrame frame = new JFrame("TableTest"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.getContentPane().add(new TableTest()); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
    } 

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

class ContextMenu extends JPopupMenu { 
    private JMenuItem item; 
    private TableClass tableClass; // dirty direct reference ***** 

    public ContextMenu(TableClass tableClass){ 
     this.tableClass = tableClass; 
     this.item= new JMenuItem("item"); 

     this.item.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) {  
       int row = tableClass.getSelectedRow(); 
       JTable table = tableClass.getTable(); 
       System.out.println("row: " + row); 
       StringBuilder sb = new StringBuilder("Data: "); 
       for (int i = 0; i < table.getColumnCount(); i++) { 
        sb.append(table.getValueAt(row, i)); 
        if (i != table.getColumnCount() - 1) { 
         sb.append(", "); 
        } 
       } 
       System.out.println(sb); 
      } 
     }); 

     add(item); 
    } 
} 

class TableClass extends JPanel { 
    // ***** passing **this** into the ContextMenu class 
    private ContextMenu contextMenu = new ContextMenu(this); 
    private static final Integer[][] DATA = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; 
    private static final String[] COLUMN_NAMES = {"A", "B", "C"}; 
    private DefaultTableModel model = new DefaultTableModel(DATA, COLUMN_NAMES); 
    private JTable table = new JTable(model); 

    public TableClass() { 
     table.addMouseListener(new MouseAdapter() { 
      @Override 
      public void mouseReleased(MouseEvent e) { 
       int selectedRow = table.rowAtPoint(e.getPoint()); 
       if (selectedRow >= 0 && selectedRow < table.getRowCount()) { 
        if (!table.getSelectionModel().isSelectedIndex(selectedRow)) { 
         table.setRowSelectionInterval(selectedRow, selectedRow); 
        } 
       } 
       if (e.isPopupTrigger() && e.getComponent() instanceof JTable) { 
        showPopUp(e); 
       } 
      } 

      private void showPopUp(MouseEvent e) { 
       contextMenu.show(e.getComponent(), e.getX(), e.getY());     
      } 
     }); 

     setLayout(new BorderLayout()); 
     add(new JScrollPane(table)); 
    } 

    public int getSelectedRow() { 
     return table.getSelectedRow(); 
    } 

    public int getSelectedColumn() { 
     return table.getSelectedColumn(); 
    } 

    public JTable getTable() { 
     return table; 
    } 
}