2013-06-07 31 views
9

TableView của JavaFX có thuộc tính placeholder về cơ bản là Node được hiển thị trong TableView bất cứ khi nào nó trống. Nếu thuộc tính này được đặt thành null (giá trị mặc định của nó), nó xuất hiện dưới dạng Label hoặc một số văn bản khác dựa trên Node có nội dung "Không có nội dung nào trong bảng".Làm cách nào tôi có thể bỏ qua "Trình giữ chỗ" của JavaFX?

Nhưng nếu có bất kỳ hàng dữ liệu nào trong bảng, thì trình giữ chỗ Node biến mất và toàn bộ không gian dọc trong TableView được lấp đầy với các hàng, kể cả hàng trống nếu không có đủ dữ liệu để lấp đầy toàn bộ bảng.

Những hàng trống này là những gì tôi muốn, ngay cả khi bảng trống. Nói cách khác, tôi không muốn sử dụng trình giữ chỗ ở tất cả. Có ai biết làm thế nào tôi có thể làm điều này?

Tôi không muốn làm điều gì đó kludgey như đặt hàng trống trong TableView bất cứ khi nào nó được cho là thực sự trống.

+0

+1 Tôi đang tìm kiếm một cách để làm điều này quá. Trong mã hóa nó ra, bạn có thể tự đặt placeHolder, tôi đã thường đặt nó vào một hình ảnh: 'table.setPlaceholder (new ImageView (new Image (new FileInputStream (" someImage.png "))));' Nhưng tôi ' d muốn có nó chỉ hiển thị hàng trống ... cũng là một cách để làm điều đó trong FXML sẽ là tiện dụng – SnakeDoc

+0

Yêu cầu tính năng trong trình theo dõi vấn đề JavaFX: [RT-31086 Không buộc tôi sử dụng 'trình giữ chỗ' mặc định trong một trống TableView] (https://javafx-jira.kenai.com/browse/RT-31086). – jewelsea

+0

tọa độ lỗi trong đại diện openjdk: https://bugs.openjdk.java.net/browse/JDK-8090949 - vẫn chưa được giải quyết trong fx9/10 – kleopatra

Trả lời

1

Dưới đây là một cách khó khăn để thực hiện nhiệm vụ của mình,

HBox box = new HBox(); 
    box.setDisable(true); 
    for (TableColumn column : patientsTable.getColumns()) { 
     ListView<String> listView = new ListView<>(); 
     listView.getItems().add(""); 
     listView.setPrefWidth(column.getWidth()); 
     box.getChildren().add(listView); 
    } 

    tableView.setPlaceholder(box); 
+0

Ha! Đó là lén lút. Thật không may, nó không hoàn toàn nhìn "đúng" trong bảng của tôi, trong đó có rất nhiều cột có thể thay đổi kích thước. Đối với một bảng đơn giản với các cột tĩnh, điều này có thể làm việc, mặc dù. – Xanatos

2

Tôi nghĩ rằng tôi đã tìm thấy một giải pháp. Nó chắc chắn không tốt, vì nó đang truy cập API theo cách không muốn, và tôi cũng có thể sử dụng không mong muốn của visibleProperty, nhưng ở đây bạn đi:

Bạn có thể thử hack TableViewSkin. Về cơ bản làm điều này để lấy một da bị tấn công:

public class ModifiedTableView<E> extends TableView<E> { 
    @Override 
    protected Skin<?> createDefaultSkin() { 
     final TableViewSkin<E> skin = new TableViewSkin<E>(this) { 
      // override method here 
     } 
     // modifiy skin here 
     return skin; 
    } 
} 

Đối với TableViewSkin sau đó bạn cần phải ghi đè phương pháp sau đây:

@Override 
protected VirtualFlow<TableRow<E>> createVirtualFlow() { 
    final VirtualFlow<TableRow<E>> flow = new VirtualFlow<TableRow<E>>(); 
    // make the 'scroll-region' always visible: 
    flow.visibleProperty().addListener((invalidation) -> { 
     flow.setVisible(true); 
    }); 
    return flow; 
} 

Và cho da sử dụng phản ánh ngừng hiển thị trình giữ chỗ:

final Field privateFieldPlaceholderRegion = TableViewSkinBase.class.getDeclaredField("placeholderRegion"); 
privateFieldPlaceholderRegion.setAccessible(true); 
final StackPane placeholderRegion = (StackPane) privateFieldPlaceholderRegion.get(skin); 

// make the 'placeholder' never visible: 
placeholderRegion.visibleProperty().addListener((invalidation) -> { 
    placeholderRegion.setVisible(false); 
}); 

Có thể bạn có thể thay đổi mức độ hiển thị của luồng trong cùng một phương pháp để làm cho mã ngắn hơn ... Nhưng tôi nghĩ bạn sẽ có được khái niệm

1

Tôi tìm thấy một giải pháp cho javafx8. Nó sử dụng api không công khai, nhưng nó không sử dụng sự phản chiếu (may mắn). Về cơ bản bạn cần phải thiết lập (hoặc thay thế) da của TableView và trả về giá trị khác 0 trong phương thức getItemCount(). Giống như vậy:

(TableView)t.setSkin(new TableViewSkin<Object>(t) 
    { 
     @Override 
     public int getItemCount() 
     { 
      int r = super.getItemCount(); 
      return r == 0 ? 1 : r; 
     } 
    }); 

Phương pháp này cũng có thể được sử dụng để thêm hàng bổ sung ở cuối mục cuối cùng của bạn (nếu bạn muốn bao gồm nút thêm). Về cơ bản, trả về luôn cao hơn một mặt hàng thực tế.

Cho dù đây là câu hỏi cũ, hy vọng điều này hữu ích cho người khác.

+1

xác minh rằng điều này có vẻ tốt trong fx8 - trục trặc tôi nhận thấy là chỉ fx9. 1 cho một giải pháp đơn giản :) – kleopatra

2

Thật không may, old issue vẫn không được sửa trong fx9 hoặc fx10. Vì vậy, xem lại các hacks trong bối cảnh của fx9.Đã có những thay đổi, tốt và xấu người:

  • Skins di chuyển vào một gói phần mềm nào mà bây giờ cho phép phân lớp chúng mà không cần truy cập vào các lớp ẩn (tốt)
  • di chuyển introduced a bug mà không cho phép cài đặt một tùy chỉnh VirtualFlow (cố định trong fx10)
  • truy cập phản chiếu cho các thành viên ẩn sẽ được khấu trừ mạnh mẽ (đọc: không thể) đôi khi trong tương lai

trong khi đào, tôi nhận thấy bao giờ trục trặc nên nhẹ với hacks (lưu ý: tôi đã làm không chạy chúng với fx8, ese có thể có sự khác biệt do ở FX8 vs fx9!)

  • các buộc trong-/tầm nhìn của placeholder/lưu lượng làm việc tốt, trừ khi khởi động với một bảng trống (placeholder được hiển thị) và mở rộng bàn trong khi rỗng (các Vùng "mới" trông trống)
  • giả mạo mụcCount to not-empty cho phép các hàng biến mất khi nhấn phím điều hướng (có thể không phải là vấn đề lớn vì người dùng có xu hướng không điều hướng một bảng trống) - điều này chắc chắn được giới thiệu trong fx9 , hoạt động tốt trong fx8

Vì vậy, tôi quyết định đi với khả năng hiển thị mức độ nhẹ: lý do cho sự lờ mờ nhẹ ches là layoutChildren không bố trí dòng chảy nếu nó nghĩ rằng giữ chỗ có thể nhìn thấy. Đó là xử lý bằng cách bao gồm các dòng chảy trong bố trí nếu siêu không.

Da tùy chỉnh:

/** 
* TableViewSkin that doesn't show the placeholder. 
* The basic trick is keep the placeholder/flow in-/visible at all 
* times (similar to https://stackoverflow.com/a/27543830/203657). 
* <p> 
* 
* Updated for fx9 plus ensure to update the layout of the flow as 
* needed. 
* 
* @author Jeanette Winzenburg, Berlin 
*/ 
public class NoPlaceHolderTableViewSkin<T> extends TableViewSkin<T>{ 

    private VirtualFlow<?> flowAlias; 
    private TableHeaderRow headerAlias; 
    private Parent placeholderRegionAlias; 
    private ChangeListener<Boolean> visibleListener = (src, ov, nv) -> visibleChanged(nv); 
    private ListChangeListener<Node> childrenListener = c -> childrenChanged(c); 

    /** 
    * Instantiates the skin. 
    * @param table the table to skin. 
    */ 
    public NoPlaceHolderTableViewSkin(TableView<T> table) { 
     super(table); 
     flowAlias = (VirtualFlow<?>) table.lookup(".virtual-flow"); 
     headerAlias = (TableHeaderRow) table.lookup(".column-header-background"); 

     // startet with a not-empty list, placeholder not yet instantiatet 
     // so add alistener to the children until it will be added 
     if (!installPlaceholderRegion(getChildren())) { 
      installChildrenListener(); 
     } 
    } 


    /** 
    * Searches the given list for a Parent with style class "placeholder" and 
    * wires its visibility handling if found. 
    * @param addedSubList 
    * @return true if placeholder found and installed, false otherwise. 
    */ 
    protected boolean installPlaceholderRegion(
      List<? extends Node> addedSubList) { 
     if (placeholderRegionAlias != null) 
      throw new IllegalStateException("placeholder must not be installed more than once"); 
     List<Node> parents = addedSubList.stream() 
       .filter(e -> e.getStyleClass().contains("placeholder")) 
       .collect(Collectors.toList()); 
     if (!parents.isEmpty()) { 
      placeholderRegionAlias = (Parent) parents.get(0); 
      placeholderRegionAlias.visibleProperty().addListener(visibleListener); 
      visibleChanged(true); 
      return true; 
     } 
     return false; 
    } 


    protected void visibleChanged(Boolean nv) { 
     if (nv) { 
      flowAlias.setVisible(true); 
      placeholderRegionAlias.setVisible(false); 
     } 
    } 


    /** 
    * Layout of flow unconditionally. 
    * 
    */ 
    protected void layoutFlow(double x, double y, double width, 
      double height) { 
     // super didn't layout the flow if empty- do it now 
     final double baselineOffset = getSkinnable().getLayoutBounds().getHeight()/2; 
     double headerHeight = headerAlias.getHeight(); 
     y += headerHeight; 
     double flowHeight = Math.floor(height - headerHeight); 
     layoutInArea(flowAlias, x, y, 
       width, flowHeight, 
       baselineOffset, HPos.CENTER, VPos.CENTER); 
    } 


    /** 
    * Returns a boolean indicating whether the flow should be layout. 
    * This implementation returns true if table is empty. 
    * @return 
    */ 
    protected boolean shouldLayoutFlow() { 
     return getItemCount() == 0; 
    } 


    /** 
    * {@inheritDoc} <p> 
    * 
    * Overridden to layout the flow always. 
    */ 
    @Override 
    protected void layoutChildren(double x, double y, double width, 
      double height) { 
     super.layoutChildren(x, y, width, height); 
     if (shouldLayoutFlow()) { 
      layoutFlow(x, y, width, height); 

     } 
    } 

    /** 
    * Listener callback from children modifications. 
    * Meant to find the placeholder when it is added. 
    * This implementation passes all added sublists to 
    * hasPlaceHolderRegion for search and install the 
    * placeholder. Removes itself as listener if installed. 
    * 
    * @param c the change 
    */ 
    protected void childrenChanged(Change<? extends Node> c) { 
     while (c.next()) { 
      if (c.wasAdded()) { 
       if (installPlaceholderRegion(c.getAddedSubList())) { 
        uninstallChildrenListener(); 
        return; 
       } 

      } 
     } 
    } 


    /** 
    * Installs a ListChangeListener on the children which calls 
    * childrenChanged on receiving change notification. 
    * 
    */ 
    protected void installChildrenListener() { 
     getChildren().addListener(childrenListener); 
    } 

    /** 
    * Uninstalls a ListChangeListener on the children: 
    */ 
    protected void uninstallChildrenListener() { 
     getChildren().removeListener(childrenListener); 
    } 


} 

Cách sử dụng Ví dụ:

public class EmptyPlaceholdersInSkin extends Application { 

    private Parent createContent() { 
     // initially populated 
     //TableView<Person> table = new TableView<>(Person.persons()) { 
     // initially empty 
     TableView<Person> table = new TableView<>() { 

      @Override 
      protected Skin<?> createDefaultSkin() { 
       return new NoPlaceHolderTableViewSkin<>(this); 
      } 

     }; 
     TableColumn<Person, String> first = new TableColumn<>("First Name"); 
     first.setCellValueFactory(new PropertyValueFactory<>("firstName")); 

     table.getColumns().addAll(first); 

     Button clear = new Button("clear"); 
     clear.setOnAction(e -> table.getItems().clear()); 
     clear.disableProperty().bind(Bindings.isEmpty(table.getItems())); 
     Button fill = new Button("populate"); 
     fill.setOnAction(e -> table.getItems().setAll(Person.persons())); 
     fill.disableProperty().bind(Bindings.isNotEmpty(table.getItems())); 
     BorderPane pane = new BorderPane(table); 
     pane.setBottom(new HBox(10, clear, fill)); 
     return pane; 
    } 


    @Override 
    public void start(Stage stage) throws Exception { 
     stage.setScene(new Scene(createContent())); 
     stage.show(); 
    } 

    public static void main(String[] args) { 
     Application.launch(args); 
    } 

    @SuppressWarnings("unused") 
    private static final Logger LOG = Logger 
      .getLogger(EmptyPlaceholdersInSkin.class.getName()); 

} 
+0

Hàng 'biến dạng trên điều hướng mũi tên' khi giả mạo số mục phải là lỗi fx9/fx10, vì tôi không thể tạo lại nó trong fx8. Dù sao, một cái nhìn tổng quan tốt đẹp về vấn đề này/lỗi! – n247s

Các vấn đề liên quan