【问题标题】:java wait cursor display problemjava等待光标显示问题
【发布时间】:2011-05-18 22:24:58
【问题描述】:

我在应用程序中显示等待光标时遇到问题。只要鼠标位于定义了自己的光标的面板上方,等待光标就不会出现。如果面板没有改变光标,则会出现等待光标。

我附上 SSCE 以准确解释我的问题。

public class BusyCursorTest extends javax.swing.JFrame {

public BusyCursorTest() {

    javax.swing.JMenuBar menuBar = new javax.swing.JMenuBar();
    javax.swing.JMenu menu = new javax.swing.JMenu("Menu");
    javax.swing.JMenuItem wait1 = new javax.swing.JMenuItem("Wait 100 ms");
    javax.swing.JMenuItem wait2 = new javax.swing.JMenuItem("Wait 250 ms");
    javax.swing.JMenuItem wait3 = new javax.swing.JMenuItem("Wait 500 ms");
    javax.swing.JMenuItem wait4 = new javax.swing.JMenuItem("Wait 1000 ms");
    menu.add(wait1);
    menu.add(wait2);
    menu.add(wait3);
    menu.add(wait4);
    menuBar.add(menu);
    setJMenuBar(menuBar);
    wait1.addActionListener(getActionListener(this, delayActionListener(100)));
    wait2.addActionListener(getActionListener(this, delayActionListener(250)));
    wait3.addActionListener(getActionListener(this, delayActionListener(500)));
    wait4.addActionListener(getActionListener(this, delayActionListener(1000)));

    cursorPanel = new javax.swing.JPanel();
    cursorPanel.addMouseListener(new java.awt.event.MouseAdapter() {

        public void mouseEntered(java.awt.event.MouseEvent e) {
            cursorPanel.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.CROSSHAIR_CURSOR));
        }

        public void mouseExited(java.awt.event.MouseEvent e) {
            cursorPanel.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
        }

    });

    javax.swing.JTabbedPane tabbedPane = new javax.swing.JTabbedPane();
    tabbedPane.addTab("Default", new javax.swing.JPanel());
    tabbedPane.addTab("Cursor change", cursorPanel);
    getContentPane().add(tabbedPane);

    setTitle("Cursor test");
    setSize(400, 400);
    setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
    setVisible(true);
}

private java.awt.event.ActionListener delayActionListener(final int delay) {
    java.awt.event.ActionListener listener = new java.awt.event.ActionListener() {

        public void actionPerformed(java.awt.event.ActionEvent ae) {
            try {
                Thread.sleep(delay);
            } catch (InterruptedException e) {
            }
        }

    };
    return listener;
}

public static void main(String[] args) {
    new BusyCursorTest();
}

public static java.awt.event.ActionListener getActionListener(final java.awt.Component component,
    final java.awt.event.ActionListener originalActionListener) {

    java.awt.event.ActionListener actionListener = new java.awt.event.ActionListener() {

        public void actionPerformed(final java.awt.event.ActionEvent e) {

            java.util.TimerTask timerTask = new java.util.TimerTask() {

                public void run() {
                    originalCursor = component.getCursor();
                    component.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
                }

            };
            java.util.Timer timer = new java.util.Timer();

            try {
                timer.schedule(timerTask, DELAY_MS);
                originalActionListener.actionPerformed(e);
            } finally {
                timer.cancel();
                component.setCursor(originalCursor);
            }
        }

    };
    return actionListener;
}

private javax.swing.JPanel cursorPanel = null;

public static java.awt.Cursor originalCursor = null;

public static final int DELAY_MS = 250;
}

运行附加的 SSCE。

当第一个选项卡(“默认”)被选中时,点击 1000ms 菜单项将显示忙碌光标。

选择“第二个”选项卡(“光标更改”)时,单击1000ms菜单项未显示繁忙的光标。

我应该如何解决这个问题?

我强烈希望我的代码不必考虑任何面板,因为我很难跟踪哪些面板可能处于最前沿。此外,事件并不总是由于鼠标点击而生成。

推荐的解决方法是什么,以便我可以修改顶级容器的行为?

【问题讨论】:

    标签: java swing busy-cursor


    【解决方案1】:

    在网上搜索后,我找到了我的问题的答案。

    关键是将光标设置在包含要显示忙碌光标的组件的框架的玻璃窗格上。我从网上的以下文章中得到了这个想法。

    Wait, Cursor, Wait!

    An Automatic Wait Cursor: WaitCursorEventQueue

    我修改了我的 SSCE,使其适用于框架内的组件设置自己的光标的情况。这是修改后的 SSCE。

    public class BusyCursorTest extends javax.swing.JFrame {
    
    private javax.swing.JPanel cursorPanel = null;
    
    public BusyCursorTest() {
    
        javax.swing.JMenuBar menuBar = new javax.swing.JMenuBar();
        javax.swing.JMenu menu = new javax.swing.JMenu("Menu");
        javax.swing.JMenuItem wait1 = new javax.swing.JMenuItem("Wait 100 ms");
        javax.swing.JMenuItem wait2 = new javax.swing.JMenuItem("Wait 250 ms");
        javax.swing.JMenuItem wait3 = new javax.swing.JMenuItem("Wait 500 ms");
        javax.swing.JMenuItem wait4 = new javax.swing.JMenuItem("Wait 1000 ms");
        menu.add(wait1);
        menu.add(wait2);
        menu.add(wait3);
        menu.add(wait4);
        menuBar.add(menu);
        setJMenuBar(menuBar);
        wait1.addActionListener(getActionListener(this, delayActionListener(100)));
        wait2.addActionListener(getActionListener(this, delayActionListener(250)));
        wait3.addActionListener(getActionListener(this, delayActionListener(500)));
        wait4.addActionListener(getActionListener(this, delayActionListener(1000)));
    
        cursorPanel = new javax.swing.JPanel();
        cursorPanel.addMouseListener(new java.awt.event.MouseAdapter() {
    
            public void mouseEntered(java.awt.event.MouseEvent e) {
                cursorPanel.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.CROSSHAIR_CURSOR));
            }
    
            public void mouseExited(java.awt.event.MouseEvent e) {
                cursorPanel.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
            }
    
        });
    
        javax.swing.JTabbedPane tabbedPane = new javax.swing.JTabbedPane();
        tabbedPane.addTab("Default", new javax.swing.JPanel());
        tabbedPane.addTab("Cursor change", cursorPanel);
        getContentPane().add(tabbedPane);
    
        setTitle("Cursor test");
        setSize(400, 400);
        setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
        setVisible(true);
    }
    
    private java.awt.event.ActionListener delayActionListener(final int delay) {
        java.awt.event.ActionListener listener = new java.awt.event.ActionListener() {
    
            public void actionPerformed(java.awt.event.ActionEvent ae) {
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException e) {
                }
            }
    
        };
        return listener;
    }
    
    public static void main(String[] args) {
        new BusyCursorTest();
    }
    
    public static java.awt.event.ActionListener getActionListener(final javax.swing.JFrame frame,
        final java.awt.event.ActionListener originalActionListener) {
    
        java.awt.event.ActionListener actionListener = new java.awt.event.ActionListener() {
    
            public void actionPerformed(final java.awt.event.ActionEvent e) {
    
                java.util.TimerTask timerTask = new java.util.TimerTask() {
    
                    public void run() {
                        originalCursor = frame.getCursor();
                        startWaitCursor(frame);
                    }
    
                };
                java.util.Timer timer = new java.util.Timer();
    
                try {
                    timer.schedule(timerTask, DELAY_MS);
                    originalActionListener.actionPerformed(e);
                } finally {
                    timer.cancel();
                    stopWaitCursor(frame);
                }
            }
    
        };
        return actionListener;
    }
    
    private static void startWaitCursor(javax.swing.JFrame frame) {
        frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
        frame.getGlassPane().addMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(true);
    }
    
    private static void stopWaitCursor(javax.swing.JFrame frame) {
        frame.getGlassPane().setCursor(originalCursor);
        frame.getGlassPane().removeMouseListener(mouseAdapter);
        frame.getGlassPane().setVisible(false);
    }
    
    private static java.awt.Cursor originalCursor = null;
    
    private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {
    };
    
    public static final int DELAY_MS = 250;
    

    }

    【讨论】:

    • 只是想指出上面的代码不能捕获鼠标事件。如果我让事件线程保持忙碌,鼠标事件可能无法被捕获。
    • 当 EDT 被阻塞时正确可靠地捕获鼠标事件的 SSCE 发布在线程中:stackoverflow.com/questions/7085239/…
    【解决方案2】:
    import java.awt.*;
    import java.awt.event.*;
    import java.text.SimpleDateFormat;
    import java.util.Random;
    import javax.swing.*;
    import javax.swing.UIManager.LookAndFeelInfo;
    import javax.swing.table.*;
    
    public class TableWithTimer implements ActionListener, Runnable {
    
        private static final long serialVersionUID = 1L;
        private JFrame frame = new JFrame();
        private JScrollPane scroll = new JScrollPane();
        private JTable myTable;
        private JPanel buttonPanel = new JPanel();
        private JButton startButton = new JButton("Start Thread to Update Table");
        private JButton stopButton = new JButton("Stop Thread for Update Table");
        private JButton newButton = new JButton("Load new Data to Table");
        private int count = 0;
        private int delay = 3;
        private javax.swing.Timer timer = null;
        private boolean runProcess;
        private int row = 0;
        private int column = 0;
        private String value = "Amnd";
        private int amndValue = 0;
        private String valueAt = "";
        private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        private String[] head = {"One", "Two", "Three", "Four", "Five", "Six"};
        private String[][] data = new String[25][6];
    
        public TableWithTimer() {
            myTable = new TableBackroundPaint0(data, head);
            myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            myTable.setGridColor(Color.gray);
            myTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            final TableCellRenderer cellRendener = myTable.getTableHeader().getDefaultRenderer();
            myTable.getTableHeader().setDefaultRenderer(new TableCellRenderer() {
    
                @Override
                public Component getTableCellRendererComponent(JTable table, Object value,
                        boolean isSelected, boolean hasFocus, int row, int column) {
                    JLabel label = (JLabel) cellRendener.getTableCellRendererComponent(
                            table, value, isSelected, hasFocus, row, column);
                    label.setBackground(Color.orange);
                    label.setForeground(Color.darkGray);
                    label.setFont(new Font("SansSerif", Font.BOLD, 12));
                    label.setBorder(BorderFactory.createCompoundBorder(label.getBorder(),
                            BorderFactory.createEmptyBorder(0, 5, 0, 0)));
                    label.setHorizontalAlignment(SwingConstants.LEFT);
                    label.setHorizontalAlignment(SwingConstants.CENTER);
                    if ((label.getText().equals("First")) || (label.getText().equals("Second"))) {
                        label.setForeground(Color.red);
                    }
                    if ((label.getText().equals("Day")) || (label.getText().equals("Month")) || (label.getText().equals("Year"))) {
                        label.setForeground(Color.blue);
                    }
                    if ((label.getText().equals("Time"))) {
                        label.setForeground(Color.green);
                    }
                    return label;
                }
            });
            TableColumnModel cm = myTable.getColumnModel();
            for (int column1 = 0; column1 < cm.getColumnCount(); column1++) {
                TableColumn colLeft1 = cm.getColumn(column1);
                cm.getColumn(column1).setWidth(140);
                cm.getColumn(column1).setPreferredWidth(140);
            }
            //myTable.setFillsViewportHeight(true); // apply paintComponent for whole Viewport
            JButton cornerButtonTop = new JButton();
            cornerButtonTop.setBackground(scroll.getViewport().getBackground());
            JButton cornerButtonBottom = new JButton();
            cornerButtonBottom.setOpaque(false);
            scroll.setCorner(JScrollPane.UPPER_RIGHT_CORNER, cornerButtonTop);
            scroll.setCorner(JScrollPane.LOWER_RIGHT_CORNER, cornerButtonBottom);
            scroll.setViewportView(myTable);
            scroll.setMinimumSize(new Dimension(600, 400));
            scroll.setMaximumSize(new Dimension(900, 600));
            scroll.setPreferredSize(new Dimension(850, 430));
            frame.add(scroll, BorderLayout.CENTER);
            buttonPanel.setLayout(new GridLayout(1, 4, 10, 10));
            startButton.addActionListener(this);
            startButton.setEnabled(false);
            stopButton.addActionListener(this);
            stopButton.setEnabled(false);
            JButton hideButton = new JButton();
            newButton.addActionListener(this);
            newButton.setEnabled(false);
            buttonPanel.add(startButton);
            buttonPanel.add(stopButton);
            buttonPanel.add(hideButton);
            buttonPanel.add(newButton);
            hideButton.setVisible(false);
            frame.add(buttonPanel, BorderLayout.SOUTH);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocation(100, 100);
            frame.pack();
            frame.setVisible(true);
            start();
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == startButton) {
                runProcess = true;
                new Thread(this).start();
                myTable.requestFocus();
                startButton.setEnabled(false);
                stopButton.setEnabled(true);
            } else if (e.getSource() == stopButton) {
                scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                runProcess = false;
                startButton.setEnabled(true);
                stopButton.setEnabled(false);
                newButton.setEnabled(true);
            } else if (e.getSource() == newButton) {
                runProcess = false;
                startButton.setEnabled(true);
                stopButton.setEnabled(false);
                newButton.setEnabled(false);
                addNewData();
            }
        }
    
        public void addNewData() {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    TableModel model = myTable.getModel();
                    for (int j = 0; j < model.getRowCount(); j++) {
                        int column = model.getColumnCount();
                        for (int i = 0; i < column; i++) {
                            model.setValueAt("Deleted", j, i);
                        }
                    }
                    startNewData();
                }
            });
        }
    
        private void start() {
            scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            timer = new javax.swing.Timer(delay * 100, updateCol());
            timer.start();
        }
    
        private void startNewData() {
            scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            count = 0;
            timer = new javax.swing.Timer(1500, updateCol());
            timer.start();
        }
    
        @Override
        public void run() {
            scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            count = 0;
            Random random = new Random();
            while (runProcess) {
                row = random.nextInt(myTable.getRowCount());
                column = random.nextInt(myTable.getColumnCount());
                SwingUtilities.invokeLater(new Runnable() {
    
                    @Override
                    public void run() {
                        try {
                            amndValue++;
                            valueAt = ((myTable.getValueAt(row, column)).toString());
                            if (!(valueAt.startsWith("A"))) {
                                count++;
                                if (count == ((25 * 6))) {
                                    JOptionPane.showMessageDialog(myTable, " Update done ");
                                    scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                                    runProcess = false;
                                }
                                java.util.Date date = new java.util.Date();
                                String dateTime = sdf.format(date.getTime());
                                myTable.setValueAt((value + " " + String.valueOf(amndValue) + " at: " + dateTime), row, column);
                                //myTable.setValueAt(new Integer(1), row, column); // please uncoment for generate misstype error on EDT
                                myTable.changeSelection(row, column, false, false);
                                System.out.println("update cycle with value :"
                                        + (value + " " + String.valueOf(amndValue) + " at: " + dateTime) + ", table row :" + row
                                        + ", table column " + column);
                            }
                        } catch (Exception e) {
                            runProcess = false;
                            System.out.println("Error for update JTable cell");
                            e.printStackTrace();
                        }
                    }
                });
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        public Action updateCol() {
            return new AbstractAction("text load action") {
    
                private static final long serialVersionUID = 1L;
    
                @Override
                public void actionPerformed(ActionEvent e) {
    
                    System.out.println("updating row " + (count + 1));
                    TableModel model = myTable.getModel();
                    int cols = model.getColumnCount();
                    int row = 0;
                    for (int j = 0; j < cols; j++) {
                        row = count;
                        myTable.changeSelection(row, 0, false, false);
                        timer.setDelay(200);
                        Object value = "row " + (count + 1) + " item " + (j + 1);
                        model.setValueAt(value, count, j);
                    }
                    count++;
                    if (count >= myTable.getRowCount()) {
                        myTable.changeSelection(0, 0, false, false);
                        timer.stop();
                        System.out.println("update cycle completed");
                        myTable.clearSelection();
                        startButton.setEnabled(true);
                        newButton.setEnabled(true);
                        scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                    }
                }
            };
        }
    
        public static void main(String args[]) {
            try {
                for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                    System.out.println(info.getName());
                    if ("Nimbus".equals(info.getName())) {
                        UIManager.setLookAndFeel(info.getClassName());
                        break;
                    }
                }
            } catch (UnsupportedLookAndFeelException e) {
                // handle exception
            } catch (ClassNotFoundException e) {
                // handle exception
            } catch (InstantiationException e) {
                // handle exception
            } catch (IllegalAccessException e) {
                // handle exception
            }
            TableWithTimer tableWithTimer = new TableWithTimer();
        }
    }
    
    class TableBackroundPaint0 extends JTable {
    
        private static final long serialVersionUID = 1L;
    
        TableBackroundPaint0(Object[][] data, Object[] head) {
            super(data, head);
            setOpaque(false);
            ((JComponent) getDefaultRenderer(Object.class)).setOpaque(false);
        }
    
        @Override
        public void paintComponent(Graphics g) {
            Color background = new Color(168, 210, 241);
            Color controlColor = new Color(230, 240, 230);
            int width = getWidth();
            int height = getHeight();
            Graphics2D g2 = (Graphics2D) g;
            Paint oldPaint = g2.getPaint();
            g2.setPaint(new GradientPaint(0, 0, background, width, 0, controlColor));
            g2.fillRect(0, 0, width, height);
            g2.setPaint(oldPaint);
            for (int row : getSelectedRows()) {
                Rectangle start = getCellRect(row, 0, true);
                Rectangle end = getCellRect(row, getColumnCount() - 1, true);
                g2.setPaint(new GradientPaint(start.x, 0, controlColor, (int) ((end.x + end.width - start.x) * 1.25), 0, Color.orange));
                g2.fillRect(start.x, start.y, end.x + end.width - start.x, start.height);
            }
            super.paintComponent(g);
        }
    }
    

    【讨论】:

    • @trashgod 这个渐变是一件艺术品,对于视口和 SelectedRow(s) 也是如此,Nimbus 非常冷色,但是这个 stackoverflow.com/questions/1249278/… 在 Nimbus L&F 中看起来非常好,
    【解决方案3】:

    嗯你的代码有问题Concurency in Swing有两个方面

    替换 Thread.sleep(delay);和 java.util.Timer 与 java.swing.Timer 因为阻止 EDT

    但在另一方面,有办法故意阻止 EDT(来自 OTN 的 splungebob) 注意:此示例针对所有 Swing 规则,并且仅以这种形式和示例工作

    import java.awt.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.event.*;
    
    public class DelayedComboBoxDemo implements Runnable {
    
        private JCheckBox chkA = new JCheckBox("A");
        private JCheckBox chkB = new JCheckBox("B");
        private JCheckBox chkC = new JCheckBox("C");
        private JComboBox cboItems = new JComboBox();
        private JFrame frame;
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new DelayedComboBoxDemo());
        }
    
        @Override
        public void run() {
            cboItems.addItem("-");
            JPanel p = new JPanel();
            p.add(chkA);
            p.add(chkB);
            p.add(chkC);
            p.add(cboItems);
            frame = new JFrame("Delayed ComboBox Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(p);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            cboItems.addPopupMenuListener(new PopupMenuListener() {
    
                @Override
                public void popupMenuCanceled(PopupMenuEvent e) {
                }
    
                @Override
                public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                }
    
                @Override
                public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                    int items = cboItems.getItemCount();
                    rebuildList();
                    if (items != cboItems.getItemCount()) {
                        cboItems.hidePopup();
                        cboItems.showPopup();
                    }
                }
            });
        }
    
        private void rebuildList() {
            frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            Vector<String> items = new Vector<String>();
            if (chkA.isSelected()) {
                items.add("A");
            } else if (chkB.isSelected()) {
                items.add("B");
            } else if (chkC.isSelected()) {
                items.add("C");
            } else {
                items.add("-");
            }
            cboItems.setModel(new DefaultComboBoxModel(items));
            try {
                Thread.sleep(3000); // simulate a long transaction
            } catch (InterruptedException ex) {
            }
            frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }
    }
    

    【讨论】:

    • 我不确定并发是否是问题(仍在探索这方面)。我正在使用 Thread.sleep() 来模拟一个长事务。这与其中一个选项卡设置了自己的光标有关,该光标以某种方式覆盖了对繁忙光标的切换。
    • 来自 setCursor() 方法的 javadoc:任何具有非空游标的组件都将覆盖其父容器上设置的游标。只有当我必须显示忙碌的光标时,我才需要找到一种方法来覆盖该覆盖。
    • 如果您只创建示例并且没有任何背景任务输出到 GUI,那么您可以通过 Thread.sleep(int) 阻止 GUI,如我在其他示例中发布的示例如果您可能会冒着更新 GUI 无法按预期工作或直接冻结的风险,在这种情况下只是 mousrHover 重新绘制了它,我将向您发送另一个示例,该示例演示了 Timer 和 Runnable 的正确性和用法,但我必须实现您的 BusyCursor ...
    • 同意。我强烈反对阻塞事件线程,除非在非常非常特殊的情况下。如果必须阻止事件线程,我想显示一个忙碌的光标并捕获所有传递给我的应用程序的鼠标事件。当事件线程被阻塞时,我的 GUI 被冻结。
    • @Santosh Tiwari +1 进行自我反思
    【解决方案4】:

    尝试将其设置在框架而不是给定组件上

    【讨论】:

    • 我确实将光标设置在上述SSCE的框架上。
    • 那么:@Santosh-Tiwari 现在怎么样了,行得通吗?我想我也遇到了和你类似的情况......
    • 是的,忙碌的光标运行良好。两个问题:i)如果我阻止 EDT,我仍然无法捕获鼠标事件。一旦 EDT 空闲,它就会开始处理排队的鼠标/键盘事件。 ii) 忙碌光标仍然没有出现在新的对话框/框架上。我知道如何解决这个问题,但还没有打扰。
    猜你喜欢
    • 1970-01-01
    • 2011-01-08
    • 2013-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多