2009-12-10 43 views
5

Tôi đang gặp vấn đề và hy vọng, ai đó biết điều gì đang xảy ra và tại sao tôi có thể giải thích những gì tôi đang bỏ lỡ ngay bây giờ để làm việc đó đề nghị.Câu hỏi Swing/JTree/mô hình cây tùy chỉnh

Tôi có một JTree được xây dựng dựa trên TreeModel tùy chỉnh ("WRTreeModel", xem bên dưới). Cấu trúc dữ liệu mô hình này sẽ được sử dụng để xây dựng một đối tượng gốc chứa một số trường và hơn nữa danh sách được hỗ trợ bởi "ArrayListModel" được hiển thị bên dưới. Cây trông đẹp khi tôi xây dựng nó bằng WRTreeModel. Tôi có thể mở rộng và thu gọn các nút đại diện cho các danh sách và các trường chứa trong các đối tượng. Tôi có thể mở rộng và thu gọn các danh sách này cũng như xem nội dung của chúng và v.v.

Bây giờ tôi muốn xóa con của một trong các danh sách và - như tôi đã biết - hãy làm điều đó bằng cách xóa nó khỏi mô hình gọi phương thức remove của ArrayListModel. Để làm cho WRTreeModel nhận biết được điều đó, điều đầu tiên là gọi phương thức fireIntervalRemoved của nó được gọi, cho đến nay rất tốt.

Trong WRTreeModels lớp bên ArrayModelListener phương pháp intervalRemoved chuẩn bị tiếng gọi của fireTreeNodesRemoved mà sau đó xây dựng một TreeEvent được chuyển tiếp đến tất cả các đăng ký TreeModelListeners (và do đó JTree mà tự đăng kí automaticall khi nó được kết nối với mô hình).

Bây giờ tôi hy vọng rằng cây phản ánh thay đổi và cập nhật biểu diễn nội bộ và trực quan để hiển thị trạng thái mới. Thật không may điều này dường như không hoạt động theo cách đó. Có chuyện gì đó xảy ra. Nhưng khi tôi nhấp vào nút tôi vừa thay đổi một số EventHandler-Exceptions được ném. Rõ ràng là một cái gì đó đã thực sự bối rối.

Tôi biết việc trả lời câu hỏi như vậy không dễ dàng nhưng tôi thực sự đánh giá cao câu trả lời nhanh. Nó cũng sẽ giúp ích, nếu ai đó biết các trang web giải thích việc sử dụng các mô hình cây tùy chỉnh (không phải trên DefaultMutableTreeNode hoặc bất kỳ lớp dựa trên thực hiện nào) và cách xử lý sự kiện và cập nhật các tác phẩm JTree.

Với Trân,

Thomas Arts


public class ArrayListModel<E> extends ArrayList<E> implements ListModel { 

... 

public E remove(int index) { 
    fireIntervalRemoved(index, index); 
    E removedElement = super.remove(index); 
    return removedElement; 
    } 

... 

} 

public class WRTreeModel extends LogAndMark implements TreeModel { 


    class ArrayModelListener implements ListDataListener { 

    ... 

    @Override 
    public void intervalRemoved(ListDataEvent e) { 
     int[] indices = new int[e.getIndex1() - e.getIndex0() + 1]; 
     for (int i = e.getIndex0(); i < e.getIndex1(); i++) 
     indices[i - e.getIndex0()] = i; 
     fireTreeNodesRemoved(e.getSource(), getPathToRoot(e.getSource()), indices,  ((ArrayListModel<?>)e.getSource()).subList(e.getIndex0(), e.getIndex1()+1).toArray()); 
    } 

    ... 

    } 

    public Object[] getPathToRoot(Object child) { 
    ArrayList<Object> ret = new ArrayList<Object>(); 
    if (child == null) 
     return ret.toArray(); 
    ret.add(root); 
    if (child == root) 
     return ret.toArray(); 
    int childType = 0; 
    if (child instanceof List<?> && ((List) child).get(0) instanceof Einleitungsstelle) { 
     childType = 1; 
    } 
    if (child instanceof Einleitungsstelle) { 
     childType = 2; 
    } 
    if (child instanceof List<?> && ((List) child).get(0) instanceof Messstelle) { 
     childType = 3; 
    } 
    if (child instanceof Messstelle) { 
     childType = 4; 
    } 
    if (child instanceof List<?> && ((List) child).get(0) instanceof  Ueberwachungswert) { 
     childType = 5; 
    } 
    if (child instanceof Ueberwachungswert) { 
     childType = 6; 
    } 
    if (child instanceof List<?> && ((List) child).get(0) instanceof  Selbstueberwachungswert) { 
     childType = 7; 
    } 
    if (child instanceof Selbstueberwachungswert) { 
     childType = 8; 
    } 
    switch (childType) { 
    // List of ESTs 
    case 1: { 
     ret.add(child); 
     break; 
    } 
    // EST 
    case 2: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     ret.add(child); 
     break; 
    } 
    // List of MSTs 
    case 3: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
     if (child == einleitungsstelle.getListOfMST()) { 
      ret.add(einleitungsstelle); 
      break; 
     } 
     } 
     ret.add(child); 
     break; 
    } 
    // MST 
    case 4: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
      if (child == einleitungsstelle.getListOfMST()) { 
      ret.add(einleitungsstelle.getListOfMST()); 
      break; 
      } 
     } 
     ret.add(child); 
     break; 
    } 
    // List of UEWs 
    case 5: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
     ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST(); 
     if (child == listOfMST) { 
      ret.add(listOfMST); 
      for (Messstelle messstelle : listOfMST) { 
      if (child == messstelle.getListOfUEW()) { 
       ret.add(messstelle.getListOfUEW()); 
       break; 
      } 
      } 
      break; 
     } 
     } 
    break; 
    } 
    // UEW 
    case 6: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
     ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST(); 
     if (child == listOfMST) { 
      ret.add(listOfMST); 
      for (Messstelle messstelle : listOfMST) { 
      if (child == messstelle.getListOfUEW()) { 
       ret.add(messstelle.getListOfUEW()); 
       break; 
      } 
      } 
      break; 
     } 
     } 
     ret.add(child); 
     break; 
    } 
    // List of SUEWs 
    case 7: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
     ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST(); 
     if (child == listOfMST) { 
      ret.add(listOfMST); 
      for (Messstelle messstelle : listOfMST) { 
      if (child == messstelle.getListOfSUEW()) { 
       ret.add(messstelle.getListOfSUEW()); 
       break; 
      } 
      } 
      break; 
     } 
     } 
     break; 
    } 
    // SUEW 
    case 8: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
      ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST(); 
      if (child == listOfMST) { 
      ret.add(listOfMST); 
      for (Messstelle messstelle : listOfMST) { 
      if (child == messstelle.getListOfSUEW()) { 
       ret.add(messstelle.getListOfSUEW()); 
       break; 
      } 
      } 
      break; 
     } 
     } 
     ret.add(child); 
     break; 
     } 
     default: 
     ret = null; 
    } 
    return ret.toArray(); 
    } 
    } 

... 

    protected void fireTreeNodesRemoved(Object changed, Object path[], int  childIndecies[], Object children[]) { 
     TreeModelEvent event = new TreeModelEvent(this, path, childIndecies, children); 
     synchronized (listeners) { 
     for (Enumeration e = listeners.elements(); e.hasMoreElements();) { 
     TreeModelListener tml = (TreeModelListener) e.nextElement(); 
     tml.treeNodesRemoved(event); 
     } 
     } 
    } 

... 

} 
+0

Không biết nếu đó là trợ giúp thêm nhưng khi tôi cố gắng thu gọn và sau đó mở rộng nút cha của nút đã xóa, tôi nhận được ngoại lệ sau: Ngoại lệ trong chuỗi "AWT-EventQueue-0" java.lang. NullPointerException \t tại javax.swing.plaf.basic.BasicTreeUI.ensureRowsAreVisible (BasicTreeUI.java:1881) \t tại javax.swing.plaf.basic.BasicTreeUI.toggleExpandState (BasicTreeUI.java:2208) \t tại javax.swing. plaf.basic.BasicTreeUI.handleExpandControlClick (BasicTreeUI.java:2191) \t tại javax.swing.plaf.basic.BasicTreeUI.checkForClickInExpandControl (BasicTreeUI.java:2149) \t tại ... và cứ thế –

+0

Tất cả các sự cố đều khẩn cấp theo định nghĩa :-) Tôi sẽ không đề cập đến "áp lực thời gian" trong một câu hỏi. Và câu hỏi của bạn có nghĩa là ở lại và giúp đỡ người khác khi bạn từ lâu quên nó. Làm cho nó kinh điển hơn. – raoulsson

Trả lời

2

Bạn cần thực hiện xóa nút và sau đó kích hoạt sự kiện TreeModelListener.treeNodesRemoved.

Để thực hiện điều này sử dụng:

SwingUtilities.invokeLater(
    new Runnable() 
    { 
    public void run() 
    { 
     //Delete and event firing logic goes here. 
     ... 
    } 
    } 
); 

Việc làm này ngăn Swing sử dụng EDT để cập nhật cây ở giữa xóa và bắn kiện bảo sự kiểm soát JTree của bạn (mà đã được thêm vào người nghe) rằng mô hình đã thay đổi.

+0

Điều đó và ngoài ra Tree.updateUI() với expandToPath (đường dẫn) sau đây với đường dẫn đến các phần tử đã loại bỏ cha mẹ thực hiện chính xác công việc mà tôi muốn. Cảm ơn rất nhiều vì gợi ý đó! BTW: Dường như việc sử dụng SwingUtilities.invokeLater rất phổ biến đến nỗi không ai nghĩ rằng người khác quên đặt sự kiện và giao diện người dùng của họ vào bên trong. –

+0

+1 để xác định ý tưởng của tôi. –

+0

@Carl: cảm ơn :-) –

0

Bởi vì chúng ta đang ở trong một vội vàng Tôi đã không nhìn mã của bạn được nêu ra. Mô tả của bạn có vẻ như là bạn đang làm mọi thứ chính xác và đã nghĩ về những gì cần thiết.

Đoán của tôi về một cái gì đó mà bạn có thể không cân nhắc: Sự thay đổi mô hình cây này có xảy ra trong Chủ đề công văn sự kiện (bí danh Swing thread worker) không? Nếu thay đổi đến từ một chuỗi khác thì có khả năng nó sẽ không được xử lý đúng cách.

Chỉ cần đâm vào bóng tối, tất nhiên.

+0

Tôi nghĩ rằng nó đang xảy ra trong đúng chủ đề. Khi tôi gỡ lỗi có điểm ngắt trong phương thức fireTreeNodesRemoved, nó tạm dừng trong chuỗi AWT-EventQueue. mà có vẻ đúng. –

+0

Bạn nói đúng. Xem câu trả lời ở trên mà gợi ý rằng, quá và trong thực tế, nó hoạt động như một nét duyên dáng :-) –

+0

Tuyệt vời! Tôi xin lỗi tôi cũng vội vàng (đã phải bảo lãnh cho một cuộc họp) và không có cơ hội để đề nghị sửa chữa. –

0

Có vẻ như bạn có một lỗi off-by-one trong intervalRemoved.

Bạn không khởi tạo giá trị cuối cùng trong mảng chỉ mục. Nó sẽ tự động khởi động thành 0.

@Override 
public void intervalRemoved(ListDataEvent e) { 
    int[] indices = new int[e.getIndex1() - e.getIndex0() + 1]; 
    for (int i = e.getIndex0(); i < e.getIndex1(); i++) 
    indices[i - e.getIndex0()] = i; 
    fireTreeNodesRemoved(e.getSource(), getPathToRoot(e.getSource()), indices,  ((ArrayListModel<?>)e.getSource()).subList(e.getIndex0(), e.getIndex1()+1).toArray()); 
} 

Hãy thử thay vì "i < = e.getIndex1()":

for (int i = e.getIndex0(); i <= e.getIndex1(); i++) { 
    indices[i - e.getIndex0()] = i; 
} 
+0

Bạn hoàn toàn đúng với điều đó. Tương tự được nêu trong phần mô tả phương thức. Tôi cố định rằng mặc dù nó không ảnh hưởng đến vấn đề của tôi vì phần tử danh sách mà tôi cố gắng loại bỏ là phần tử đầu tiên và do đó chỉ mục 0 là tốt. –

0

Tên phương pháp là fireIntervalRemoved, vì vậy hãy thử gọi nó sau loại bỏ:

public E remove(int index) { 
    E removedElement = super.remove(index); 
    fireIntervalRemoved(index, index); 
    return removedElement; 
} 

Đó là cách tôi đã làm và luôn làm việc (có thể thêm một số kiểm tra).
(xin lỗi nếu tôi bỏ lỡ điều gì đó, không có thời gian để kiểm tra/kiểm tra mã của bạn ...)

+0

Tôi có cần biết yếu tố đã xóa để xây dựng TreeEvent của mình không? Nếu tôi gọi fireIntervalRemoved (chỉ mục, chỉ mục) sau khi xóa phần tử thì tôi không thể lấy nó nữa. Đó là lý do tại sao tôi đã chuyển cuộc gọi trước khi thực sự bị xóa khỏi danh sách. –

+0

không chắc chắn, chưa chọn tất cả mã và không thể tìm thấy 'fireIntervalRemoved'. TreeModelEvent không cần phần tử, như tôi biết ... Tôi nghĩ nếu bạn kích hoạt sự kiện và GUI được cập nhật (đủ nhanh) trước khi bạn xóa phần tử, cây sẽ KHÔNG phản ánh việc xóa (vì nó chưa xảy ra). Có lẽ bạn có thể xây dựng sự kiện trước khi xóa phần tử và kích hoạt sự kiện sau khi xóa ... ((* Gruss aus Stuttgart *)) –