JXTable + GlazedLists +拖动选择期间更新=问题

发布于 2024-10-15 19:05:11 字数 6450 浏览 2 评论 0原文

我在使用 JXTable 时遇到表行选择问题。

如果我运行以下程序,我会得到两个正在查看相同 EventList 的帧。一种有 JTable,另一种有 JXTable。

每隔 1000 毫秒,我就会更新所有名称以 foo 开头的项目(这发生在 tweakList() 中)。此更新似乎会干扰 JXTable 中的鼠标选择,但不会干扰 JTable 中的操作:如果我在 JXTable 中单击并向下拖动以选择多行,则更新发生时选择锚点会消失。 (单击并向上拖动效果很好)

为什么会这样做,如何解决这个问题?

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.Timer;
import org.jdesktop.swingx.JXTable;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.swing.EventTableModel;

public class TableSelectionExample {
    static public enum ItemKey {
        NAME("name") {
            @Override public String getStringFromItem(Item item) {
                return item.getName();
            }
        }, 
        NUMBER("#") {
            @Override public String getStringFromItem(Item item) {
                return Integer.toString(item.getNumber());
            }
        }, 
        PARENT("parent") {
            @Override public String getStringFromItem(Item item) {
                Item p = item.getParent();
                return (p == null) ? null : p.getName();
            }           
        };

        final private String name;
        ItemKey(String name) { this.name = name; }
        public String getName() { return this.name; }
        abstract public String getStringFromItem(Item item);

        static private ItemKey[] columns = { NAME, NUMBER, PARENT }; 
        static public ItemKey[] getColumns() { return columns; }
    }
    static public class ItemTableFormat implements TableFormat<Item> {
        @Override public int getColumnCount() { 
                return ItemKey.getColumns().length; 
            }
        @Override public String getColumnName(int col) { 
            return ItemKey.getColumns()[col].getName(); }
        @Override public Object getColumnValue(Item item, int col) {
            return ItemKey.getColumns()[col].getStringFromItem(item); 
        }       
    }

    static class Item {
        final private String name;
        private int number;
        final private Item parent;

        private Item(String name, int number, Item parent) {
            this.name=name; this.number=number; this.parent=parent;
        }
        static public Item create(String name, int number, Item parent) {
            return new Item(name, number, parent); 
        }

        public String getName() { return this.name; }
        public int getNumber() { return this.number; }
        public void setNumber(int number) { this.number = number; }
        public Item getParent() { return this.parent; }
    }

    static public void main(String[] args)
    {
        final EventList<Item> items = new BasicEventList<Item>();
        Item x1,x2,x3;
        x1 = Item.create("foo", 1, null);
        x2 = Item.create("bar", 2, x1);
        x3 = Item.create("baz", 3, x1);
        items.add(x1);
        items.add(x2);
        items.add(x3);
        for (int i = 0; i < 20; ++i)
        {
            items.add(Item.create(String.format("bar%02d",i), 100+i, x2));
            items.add(Item.create(String.format("baz%02d",i), 200+i, x3));
            if (isprime(i))
                items.add(Item.create(
                        String.format("foo%02d", i), 300+i, x1));
        }

        EventList<Item> sortedItems = new SortedList<Item>(items, null);

        doit(sortedItems, new JTable(), "JTable selection example");
        doit(sortedItems, new JXTable(), "JXTable selection example");

        Timer timer = new Timer(1000, new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                tweakList(items);               
            }           
        });
        timer.start();
    }

    private static void doit(EventList<Item> displayItems,
            JTable table, String title) {
        TableFormat<Item> tf = new ItemTableFormat();
        EventTableModel<Item> etm = 
            new EventTableModel<Item>(displayItems, tf);

        table.setModel(etm);        
        if (table instanceof JXTable)
        {
            ((JXTable)table).setColumnControlVisible(true);
        }

        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(new JScrollPane(table), BorderLayout.CENTER);

        JFrame frame = new JFrame(title);
        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.pack();
    }

    private static boolean isprime(int i) {
        // works for i < 400
        if (i < 2)
            return false;
        else if (i > 2 && (i % 2) == 0)
            return false;
        else if (i > 3 && (i % 3) == 0)
            return false;
        else if (i > 5 && (i % 5) == 0)
            return false;
        else if (i > 7 && (i % 7) == 0)
            return false;
        else if (i > 11 && (i % 11) == 0)
            return false;
        else if (i > 13 && (i % 13) == 0)
            return false;
        else if (i > 17 && (i % 17) == 0)
            return false;
        else if (i > 19 && (i % 19) == 0)
            return false;
        else
            return true;
    }

    protected static void tweakList(EventList<Item> items) {
        int L = items.size();
        Set<Integer> modifiedItems = new HashSet<Integer>();
        for (int i = 0; i < L; ++i)
        {
            Item item = items.get(i);
            if (item.getName().startsWith("foo"))
            {
                item.setNumber(item.getNumber()+1000);
                modifiedItems.add(i);
            }
        }
        refreshModifiedItems(items, modifiedItems);
    }

    private static void refreshModifiedItems(EventList<Item> items, 
            Set<Integer> modifiedItems) {
        for (int i : modifiedItems)
        {
            items.set(i, items.get(i));
        }
    }
}

I'm having a table row selection problem with JXTable.

If I run the following program, I get two frames that are viewing the same EventList<Item>. One has a JTable and one has a JXTable.

Every 1000 milliseconds I update all the items with names that start with foo (this happens in tweakList()). This update seems to interfere with selection-by-mouse in the JXTable, but not in the JTable: if I click and drag downwards in the JXTable to select multiple rows, the selection anchor disappears when the update happens. (click and drag upwards works fine)

Why does it do this, and how do I fix this?

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.Timer;
import org.jdesktop.swingx.JXTable;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.swing.EventTableModel;

public class TableSelectionExample {
    static public enum ItemKey {
        NAME("name") {
            @Override public String getStringFromItem(Item item) {
                return item.getName();
            }
        }, 
        NUMBER("#") {
            @Override public String getStringFromItem(Item item) {
                return Integer.toString(item.getNumber());
            }
        }, 
        PARENT("parent") {
            @Override public String getStringFromItem(Item item) {
                Item p = item.getParent();
                return (p == null) ? null : p.getName();
            }           
        };

        final private String name;
        ItemKey(String name) { this.name = name; }
        public String getName() { return this.name; }
        abstract public String getStringFromItem(Item item);

        static private ItemKey[] columns = { NAME, NUMBER, PARENT }; 
        static public ItemKey[] getColumns() { return columns; }
    }
    static public class ItemTableFormat implements TableFormat<Item> {
        @Override public int getColumnCount() { 
                return ItemKey.getColumns().length; 
            }
        @Override public String getColumnName(int col) { 
            return ItemKey.getColumns()[col].getName(); }
        @Override public Object getColumnValue(Item item, int col) {
            return ItemKey.getColumns()[col].getStringFromItem(item); 
        }       
    }

    static class Item {
        final private String name;
        private int number;
        final private Item parent;

        private Item(String name, int number, Item parent) {
            this.name=name; this.number=number; this.parent=parent;
        }
        static public Item create(String name, int number, Item parent) {
            return new Item(name, number, parent); 
        }

        public String getName() { return this.name; }
        public int getNumber() { return this.number; }
        public void setNumber(int number) { this.number = number; }
        public Item getParent() { return this.parent; }
    }

    static public void main(String[] args)
    {
        final EventList<Item> items = new BasicEventList<Item>();
        Item x1,x2,x3;
        x1 = Item.create("foo", 1, null);
        x2 = Item.create("bar", 2, x1);
        x3 = Item.create("baz", 3, x1);
        items.add(x1);
        items.add(x2);
        items.add(x3);
        for (int i = 0; i < 20; ++i)
        {
            items.add(Item.create(String.format("bar%02d",i), 100+i, x2));
            items.add(Item.create(String.format("baz%02d",i), 200+i, x3));
            if (isprime(i))
                items.add(Item.create(
                        String.format("foo%02d", i), 300+i, x1));
        }

        EventList<Item> sortedItems = new SortedList<Item>(items, null);

        doit(sortedItems, new JTable(), "JTable selection example");
        doit(sortedItems, new JXTable(), "JXTable selection example");

        Timer timer = new Timer(1000, new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                tweakList(items);               
            }           
        });
        timer.start();
    }

    private static void doit(EventList<Item> displayItems,
            JTable table, String title) {
        TableFormat<Item> tf = new ItemTableFormat();
        EventTableModel<Item> etm = 
            new EventTableModel<Item>(displayItems, tf);

        table.setModel(etm);        
        if (table instanceof JXTable)
        {
            ((JXTable)table).setColumnControlVisible(true);
        }

        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(new JScrollPane(table), BorderLayout.CENTER);

        JFrame frame = new JFrame(title);
        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.pack();
    }

    private static boolean isprime(int i) {
        // works for i < 400
        if (i < 2)
            return false;
        else if (i > 2 && (i % 2) == 0)
            return false;
        else if (i > 3 && (i % 3) == 0)
            return false;
        else if (i > 5 && (i % 5) == 0)
            return false;
        else if (i > 7 && (i % 7) == 0)
            return false;
        else if (i > 11 && (i % 11) == 0)
            return false;
        else if (i > 13 && (i % 13) == 0)
            return false;
        else if (i > 17 && (i % 17) == 0)
            return false;
        else if (i > 19 && (i % 19) == 0)
            return false;
        else
            return true;
    }

    protected static void tweakList(EventList<Item> items) {
        int L = items.size();
        Set<Integer> modifiedItems = new HashSet<Integer>();
        for (int i = 0; i < L; ++i)
        {
            Item item = items.get(i);
            if (item.getName().startsWith("foo"))
            {
                item.setNumber(item.getNumber()+1000);
                modifiedItems.add(i);
            }
        }
        refreshModifiedItems(items, modifiedItems);
    }

    private static void refreshModifiedItems(EventList<Item> items, 
            Set<Integer> modifiedItems) {
        for (int i : modifiedItems)
        {
            items.set(i, items.get(i));
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

段念尘 2024-10-22 19:05:11

没关系,这是 SwingX Issue 370,它已在SwingX 1.6。

(我正在运行 SwingX 1.0;我下载了 SwingX 1.6.2,问题就消失了)

Never mind, this is SwingX Issue 370 and it got fixed in SwingX 1.6.

(I was running SwingX 1.0; I downloaded SwingX 1.6.2 and the problem went away)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文