我想弄清楚如何在Swing中創建虛擬列表框(或樹或大綱) - 這將是列表框可以在一個大型結果集中顯示「視圖」數據庫而不需要獲取整個結果集的內容;所有它需要給我的是項目N1 - N2將需要很快顯示,所以我可以取他們,並要求的內容項目N.Swing中的虛擬列表框
我知道如何做到這一點Win32(ListView + LVS_OWNERDATA)和XUL(custom treeview),我找到了一些SWT,但不是Swing。
有什麼建議嗎?
更新:啊哈,我不明白,尋找在搜索引擎什麼,&教程似乎並沒有稱之爲「虛擬列表框」或者使用的想法。我發現一個good tutorial,我可以從其中開始,並且其中Sun tutorials似乎也可以。
這裏是我的示例程序,它的工作原理我所期望的方式... 除了好像列表框查詢我的AbstractListModel爲所有行,而不僅僅是可見的行。對於一百萬行的虛擬表,這是不實際的。我怎樣才能解決這個問題? (編輯:好像setPrototypeCellValue修復,但我不明白爲什麼...。)
package com.example.test;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
// based on:
// http://www.java2s.com/Tutorial/Java/0240__Swing/extendsAbstractListModel.htm
// http://www.java2s.com/Tutorial/Java/0240__Swing/SpinnerNumberModel.htm
// http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SpinnerNumberModel.html
// http://www.java2s.com/Tutorial/Java/0240__Swing/ListeningforJSpinnerEventswithaChangeListener.htm
public class HanoiMoves extends JFrame {
public static void main(String[] args) {
HanoiMoves hm = new HanoiMoves();
}
static final int initialLevel = 6;
final private JList list1 = new JList();
final private HanoiData hdata = new HanoiData(initialLevel);
public HanoiMoves() {
this.setTitle("Solution to Towers of Hanoi");
this.getContentPane().setLayout(new BorderLayout());
this.setSize(new Dimension(400, 300));
list1.setModel(hdata);
SpinnerModel model1 = new SpinnerNumberModel(initialLevel,1,31,1);
final JSpinner spinner1 = new JSpinner(model1);
this.getContentPane().add(new JScrollPane(list1), BorderLayout.CENTER);
JLabel label1 = new JLabel("Number of disks:");
JPanel panel1 = new JPanel(new BorderLayout());
panel1.add(label1, BorderLayout.WEST);
panel1.add(spinner1, BorderLayout.CENTER);
this.getContentPane().add(panel1, BorderLayout.SOUTH);
ChangeListener listener = new ChangeListener() {
public void stateChanged(ChangeEvent e) {
Integer newLevel = (Integer)spinner1.getValue();
hdata.setLevel(newLevel);
}
};
spinner1.addChangeListener(listener);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
class HanoiData extends AbstractListModel {
public HanoiData(int level) { this.level = level; }
private int level;
public int getLevel() { return level; }
public void setLevel(int level) {
int oldSize = getSize();
this.level = level;
int newSize = getSize();
if (newSize > oldSize)
fireIntervalAdded(this, oldSize+1, newSize);
else if (newSize < oldSize)
fireIntervalRemoved(this, newSize+1, oldSize);
}
public int getSize() { return (1 << level); }
// the ruler function (http://mathworld.wolfram.com/RulerFunction.html)
// = position of rightmost 1
// see bit-twiddling hacks page:
// http://www-graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
public int rulerFunction(int i)
{
long r1 = (i & (-i)) & 0xffffffff;
r1 *= 0x077CB531;
return MultiplyDeBruijnBitPosition[(int)((r1 >> 27) & 0x1f)];
}
final private static int[] MultiplyDeBruijnBitPosition =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
public Object getElementAt(int index) {
int move = index+1;
if (move >= getSize())
return "Done!";
int disk = rulerFunction(move)+1;
int x = move >> (disk-1); // guaranteed to be an odd #
x = (x - 1)/2;
int K = 1 << (disk&1); // alternate directions for even/odd # disks
x = x * K;
int post_before = (x % 3) + 1;
int post_after = ((x+K) % 3) + 1;
return String.format("%d. move disk %d from post %d to post %d",
move, disk, post_before, post_after);
}
}
更新:
每jfpoilpret的建議,我把一個斷點在getElementData()
功能。
if ((index & 0x3ff) == 0)
{
System.out.println("getElementAt("+index+")");
}
我看着有問題的線程的堆棧跟蹤。這不是真的有用(下面發佈)。但是,從其他一些調整來看,它看起來像是fireIntervalAdded()/ fireIntervalRemoved()和getSize()結果中的變化的罪魁禍首。 fireIntervalxxxx似乎提示Swing會檢查getSize()函數,如果大小發生變化,它會立即(並且至少會將請求放入事件隊列中)重新排列所有行內容。
有必須有一些方式來告訴它不要這樣做!但我不知道是什麼。
com.example.test.HanoiMoves at localhost:3333
Thread [main] (Suspended (breakpoint at line 137 in HanoiData))
HanoiData.getElementAt(int) line: 137
BasicListUI.updateLayoutState() line: not available
BasicListUI.maybeUpdateLayoutState() line: not available
BasicListUI.getPreferredSize(JComponent) line: not available
JList(JComponent).getPreferredSize() line: not available
ScrollPaneLayout$UIResource(ScrollPaneLayout).layoutContainer(Container) line: not available
JScrollPane(Container).layout() line: not available
JScrollPane(Container).doLayout() line: not available
JScrollPane(Container).validateTree() line: not available
JPanel(Container).validateTree() line: not available
JLayeredPane(Container).validateTree() line: not available
JRootPane(Container).validateTree() line: not available
HanoiMoves(Container).validateTree() line: not available
HanoiMoves(Container).validate() line: not available
HanoiMoves(Window).show() line: not available
HanoiMoves(Component).show(boolean) line: not available
HanoiMoves(Component).setVisible(boolean) line: not available
HanoiMoves(Window).setVisible(boolean) line: not available
HanoiMoves.<init>() line: 69
HanoiMoves.main(String[]) line: 37
Thread [AWT-Shutdown] (Running)
Daemon Thread [AWT-Windows] (Running)
Thread [AWT-EventQueue-0] (Running)
更新:我嘗試使用一些FastRenderer.java代碼從Advanced JList Programming article和固定它。但事實證明,這不是渲染器!一行代碼解決了我的問題,我不明白爲什麼:
list1.setPrototypeCellValue(list1.getModel().getElementAt(0));
是的堆棧跟蹤是有用的:你看到它的getPreferredSize(),這匹配我原來的評論; JList計算它的首選大小,但是它需要檢查列表中的所有項目! 使用setPrototypeCellValue(),您告訴列表該值應該用於計算大小! – jfpoilpret 2009-03-09 23:13:55
這就是爲什麼它解決了你的問題。 – jfpoilpret 2009-03-09 23:14:48
所以它不是列表大小,它是列表中每個項目的單元格大小。 – 2009-03-10 00:22:11