Swing Unteren Abstand für JTable in JScrollPane festlegen

  • Themenstarter Themenstarter Jos
  • Beginndatum Beginndatum
J

Jos

Gast
Moin,

ich möchte in JAVA ein Programm schreiben, das mit dem File-Explorer von Windows vergleichbar ist. Dazu verwende ich eine JTable, die in ein JScrollPane eingefügt ist. In der JTable stehen die ganzen Dateieinträge, also z.B. erste Spalte Dateiname, zweite Spalte Dateigröße.

Es soll nun für die Tabelle zwei verschiedene Kontextmenüs geben (JPopupMenu).
Das eine erscheint bei Rechtsklick auf eine Zeile und hat Einträge, die für diese eine Datei wirken sollen, wie z.B. "Datei löschen", "Datei öffnen".
Das andere erscheint bei Rechtsklick auf den leeren Bereich unter der Tabelle und hat allgemeine Einträge für den Explorer wie "Neue Datei anlegen", "Aktualisieren". Dazu habe ich die Höhe der Tabelle mit setFillsViewportHeight(true); auf das JScrollPane angepasst.

Nun das Problem: Wenn die Tabelle viele Einträge hat und das JScrollPane komplett ausfüllt, dann gibt es keinen freien Bereich mehr und man kommt an das zweite Kontextmenü nicht mehr heran. Man müsste irgendwie festlegen, dass unter der letzten Tabellenzeile immer ein Abstand zum JScrollPane bleibt. Weiß jemand, wie ich das machen kann?
 
Mach doch die allgemeinen Menüpunkte mit in das eine Popupmenü für die Dateien mit rein, vielleicht optisch durch einen Separator abgetrennt. Ist vielleicht besser, als immer erst nach unten zu scrollen um den freien Platz unter der Tabelle zu suchen. Und natürlich das auch noch oben in die Menüleiste rein...
 
Mir ist schon klar, dass man das auch anders lösen kann, und habe dazu auch schon Ideen. Aber rein aus Interesse will ich mal wissen, ob meine erste Idee auch irgendwie machbar ist.

@Camino
Guter Punkt aber, dass man dann erst lange scrollen muss. Das habe ich mir noch gar nicht richtig überlegt. Danke dir.
Bei meiner Vorlage (Windows Explorer) funktioniert es zwar nahezu genauso, aber dort ist es noch so, dass das zweite, allgemeine Kontextmenü auch kommt, wenn man _neben_ einen Dateinamen klickt, der die Namenspalte nicht ganz ausfüllt. Das wäre in JAVA aber sicher zu aufwändig. hmmm....
 
Ich hab mir jetzt den Windows Explorer (unter Windows7) mal angeschaut. Wenn die Zeile selektiert (farbig unterlegt) ist, dann erscheint auch das Popup-Menü für die Datei, wenn ich neben den Dateinamen mit der rechten Maustaste klicke. Wenn die Zeile den Fokus (Rahmen aussenrum) hat, dann erscheint das allgemeine Popup-Menü. Wenn ich mit der rechten Maustaste auf einen anderen Dateinamen klicke, wird die Zeile farblich selektiert und es erscheint das Dateimenü. Wie das jetzt in Java umzusetzen wäre, hmmm, keine Ahnung...
 
Schnell gehackt, vom Prinzip her: MouseListener auf die Table, abfragen ob Klickpunkt auf einem Selecteten Eintrag ist und davon abhängig das jeweilige Popupmenü anzeigen.
Java:
import java.awt.BorderLayout;


public class Explorer extends JFrame {

	private JPanel contentPane;
	private JTable table;
	private DefaultTableModel tableModel=new DefaultTableModel();
	private Vector<String> tableColumns=new Vector<String>();
	private JPopupMenu popupDatei = new JPopupMenu();
	private JPopupMenu popupBackground = new JPopupMenu();
	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Explorer frame = new Explorer();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public Explorer() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 340);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		tableColumns.add("Dateiname");
		
		DefaultMutableTreeNode root=new DefaultMutableTreeNode();
		root.setUserObject("Computer");
		File[] roots=File.listRoots();
		for(File f:roots)
		{
			root.add(new DefaultMutableTreeNode(f));
		}
		DefaultTreeModel treeModel=new DefaultTreeModel(root);
		
		JScrollPane scrollPaneTree = new JScrollPane();
		JTree tree = new JTree(treeModel);	
		
		tree.addTreeSelectionListener(new TreeSelectionListener() {
			
			public void valueChanged(TreeSelectionEvent e) 
			{
				DefaultMutableTreeNode node=(DefaultMutableTreeNode)e.getPath().getLastPathComponent();
				if(node.isLeaf())
				{
					Vector<Vector<File>> tableData=new Vector<Vector<File>>();
					File selectedFile=(File) node.getUserObject();
					File[] files=selectedFile.listFiles();
					for(File f:files)
					{
						Vector<File> v=new Vector<File>();
						v.add(f);
						tableData.add(v);
						if(f.isDirectory())
						{
							node.add(new DefaultMutableTreeNode(f));
						}
					}
					
					tableModel.setDataVector(tableData,tableColumns);
				}
			}
		});
		scrollPaneTree.setViewportView(tree);
		
		JScrollPane scrollPaneTable = new JScrollPane();
		
		table = new JTable(tableModel);
		table.setFillsViewportHeight(true);
		scrollPaneTable.setViewportView(table);
		

		
		JMenuItem itemNeu = new JMenuItem("Neue Datei");
		itemNeu.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) 
			{
				System.out.println("Neue Datei");
			}
		});
		popupBackground.add(itemNeu);
		
		JSplitPane splitPane = new JSplitPane();
		splitPane.setResizeWeight(0.3);
		splitPane.setPreferredSize(new Dimension(400, 300));
		splitPane.setLeftComponent(scrollPaneTree);
		splitPane.setRightComponent(scrollPaneTable);
		contentPane.add(splitPane, BorderLayout.NORTH);
		
		JMenuItem itemOefnnen = new JMenuItem("\u00D6ffnen");
		popupDatei.add(itemOefnnen);
		itemOefnnen.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) 
			{
				File f=(File) table.getValueAt(table.getSelectedRow(), 0);
				try {
					Desktop.getDesktop().open(f);
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
		
		
		table.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) 
			{
				int selection=table.rowAtPoint(e.getPoint());
				//table.getSelectionModel().setSelectionInterval(selection, selection);
				if (e.isPopupTrigger()) {
					showMenu(e);
				}
			}
			public void mouseReleased(MouseEvent e) {
				if (e.isPopupTrigger()) {
					showMenu(e);
				}
			}
			private void showMenu(MouseEvent e) {
				int row=table.rowAtPoint(e.getPoint());
				if(row!=-1 && row==table.getSelectedRow())  //-1 -> No row selected
				{
					popupDatei.show(table, e.getX(), e.getY());
				}
				else
				{
					popupBackground.show(table, e.getX(), e.getY());
				}
				
			}
		});		
	}
}
 
Zuletzt bearbeitet:
Hallo.
Mir fallen zwei weitere Möglichkeiten ein, die beide
Code:
JComponent.setComponentPopupMenu()
verwenden:

1. Einen TreeSelectionListener anbinden und bei Selektionsänderung auch das Component-PopupMenu entsprechend ändern.

2. Etwas komplexer: Den TreeCellRenderer erweitern und dort das PopupMenu setzen. Das ist aber wohl eher etwas wenn man sehr viel mehr verschiedene Popupmenüs hat.
 
@2: Das geht meines Wissens nach nicht, der Cell Renderer packt nur ein Abbild des Komponents hin, etwaige Listeners springen nicht an.
 
Ich sehe, es haben sich noch ein paar weitere Menschen Gedanken über mein Problem gemacht. Danke euch für eure Mühe. Besonderer Dank an bERt0r für das ausführliche Code-Beispiel.
Trotzdem muss ich leider sagen, dass meine Frage damit nicht beantwortet ist. Bei Rechtsklick auf eine Zeile, die noch nicht ausgewählt ist, soll diese ausgewählt werden und das Datei-Kontextmenü soll kommen. Es geht mir halt wirklich nur um den leeren Bereich unter den Tabellenzeilen. Es scheint da wohl keine Möglichkeit zu geben, schade.
 
Bei Rechtsklick auf eine Zeile, die noch nicht ausgewählt ist, soll diese ausgewählt werden und das Datei-Kontextmenü soll kommen.
Nichts einfacher als das: MouseListener dran und bei Rechtsklick die Zeile selektieren.

Zu der ursprünglichen Frage "unteren Abstand" eine quick & dirty Lösung:
Einfach dem Viewport der ScrollPane eine Border setzen die unten etwas breiter (zur Verdeutlichung Blau eingefärbt) ist, dann ein Popup für die ScrollPane und eines für die JTable setzen
Java:
	JFrame frame = new JFrame();
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.setBounds(0, 0, 200, 300);
	
	JPopupMenu menu1 = new JPopupMenu();
	menu1.add(new JMenuItem("Menu 1"));
	JPopupMenu menu2 = new JPopupMenu();
	menu2.add(new JMenuItem("Menu 2"));
	
	JTable table = new JTable(new DefaultTableModel(new String[] {"Name", "Datum", "..."}, 20));
	table.setBackground(Color.WHITE);
	JScrollPane pane = new JScrollPane(table);
	pane.setViewportBorder(BorderFactory.createMatteBorder(1, 1, 30, 1, Color.BLUE));
	table.setComponentPopupMenu(menu1);
	pane.setComponentPopupMenu(menu2);
	frame.add(pane);
	frame.setVisible(true);
 
Oder meinen Code durchlesen und die 2 / wegmachen:
public void mousePressed(MouseEvent e)
{
int selection=table.rowAtPoint(e.getPoint());
//table.getSelectionModel().setSelectionInterval(selection, selection);
if (e.isPopupTrigger()) {
showMenu(e);
}
}
 
Hey! Nicht meine Aussagen aus dem Zusammenhang reißen (-;
Ihr habt Lösungen vorgeschlagen, bei denen unterschieden wird, ob der Rechtsklick auf die aktuell selektierte Zeile geht oder nicht, und anhand dessen wird entschieden, welches Kontextmenü aufgeht. Wenn ich nun sagen würde, egal auf welche Zeile der Rechtsklick geht, sie wird immer selektiert und das Dateimenü geht auf, dann hätte ich wieder keinen Zugriff auf das allgemeine Kontextmenü. Nur das wollte ich damit sagen, nicht dass ich nicht weiß, wie man eine Zeile auswählt (-;

@Michael...
Deine Lösung ist nicht ganz 100% das was ich wollte, aber es kommt dem wirklich sehr sehr nah. Und es löst gleichzeitig das Problem, das Camino weiter oben aufgezeigt hat, also irgendwie sogar besser als meine eigentliche Idee. Danke (-: Ich werde das jetzt in dieser Art umsetzen.

Ich habe festgestellt, dass man nichtmal unbedingt setViewportBorder() bei dem JScrollPane machen muss. Es reicht sogar schon, wenn man dem ScrollPane das Kontextmenü anhängt mit setComponentPopupMenu(). Ich werde den Rahmen trotzdem ein wenig vergrößern, damit man nicht so eine Millimeterarbeit mit der Maus machen muss.
 

Neue Themen


Zurück
Oben