2017-11-12 33 views
-1

작업 : 항목이 ListView의 보이는 영역에 있는지 확인하십시오.JavaFX ListView 셀 팩터 리 렌더링 완료 이벤트

솔루션 : 렌더링 할 항목이 포함 된 JavaFX ListView가 있습니다. ListView의 가시 영역에있는 항목을 파악하기 위해 사용자에게 표시되는 항목의 수를 계산하는 셀 팩터 리를 구현했습니다.
1. 항목 추가를
2.을은 ListView를 보이는 경우 :

그래서, 기본적으로, 나는 무엇을해야합니다.

문제 : 항목을 계산하기 위해 , 항목 추가 스레드 항목이 작동하고 렌더링을 추가 완료 전지 공장까지 기다려야합니다 (스레드 호출). 그러나 JavaFX UI 스레드가 내부 Quantum Toolkit 기법을 사용하여 렌더링을 마치는 시점을 호출 스레드가 알 수 없으므로이를 구현하는 방법을 모르겠습니다. 셀 팩터 리 항목이 동기화 할 수없는 JavaFX 내부의 별도 스레드로 렌더링됩니다.

거친 호출 스레드 지연을 추가하면 스레드 동기화 문제를 명확하게 나타내는 문제가 해결되지만 더 우아하고 명확한 솔루션이 필요합니다. 크로스 스레드 관리를 엉망으로 며칠 후

public class MessengerServiceContext { 
     @Override 
     public void messageReceived(final MessageReceivedEvent messageReceivedEvent) { 
      ...   

      //Calling thread method 
      messengerServiceControl.receiveMessage(messengerMessageData); 

      //Main thread is paused for several seconds to wait JavaFX UI threads. 
      //Ugly and erroneous 
      //Demonstrates the cause of the problem 
      try { 
       Thread.sleep(2000); 
      } catch (InterruptedException ex) { Logger.getLogger(MessengerServiceControl.class.getName()).log(Level.SEVERE, null, ex); 
      } 

      if (!messengerServiceControl.getMessageElementControlVisibility(messengerMessageData)) { 
       int newMessagesCount = getNewMessagesCount().get(); 
       getNewMessagesCount().set(++newMessagesCount); 
      } 
     } 
    } 

    public class MessengerServiceControl implements Initializable { 
     ... 
     private TrackingListCellFactory<MessageElementControl> messengerOutputWindowListViewCellFactory; 
     ... 

     //Calling (message processing) thread method which inserts ListView item 
     public void receiveMessage(final MessengerMessageData messengerMessageData) { 
      //Calling MessengerServiceControl model method to insert items in ListView using JavaFX UI thread 
      MessageElementControl messageElementControl = model.createMessage(messengerMessageData, false); 

      //Tried scene and parent property 
      messageElementControl.sceneProperty().addListener((observable, oldValue, newValue) -> { 
      if (newValue != null) { 
       if (!getMessageElementControlVisibility(messengerMessageData)) { 
        int newMessagesCount = getNewMessagesCount().get();        
        getNewMessagesCount().set(++newMessagesCount); 
       } 
      } 
     } 

     boolean getMessageElementControlVisibility(final MessengerMessageData messengerMessageData) { 
      return messengerOutputWindowListViewCellFactory.getItemVisibility(messengerMessageData); 
     } 

     //Cell factory class which is responsible for items rendering: 
     private static class TrackingListCellFactory<T extends MessageElementControl> implements Callback<ListView<T>, ListCell<T>> { 
      //Items which have cells visible to the user 
      private final Set<T> visibleItems = new HashSet(); 

      TrackingListCellFactory() {  
      } 

      boolean getItemVisibility(final MessengerMessageData messengerMessageData) { 
       synchronized (this) { 
        Optional<T> messageElementControlOptional = visibleItems.stream().filter((item) -> { 
         return item.getMessageData().getMessageCreatedDate().isEqual(messengerMessageData.getMessageCreatedDate()); 
        }).findFirst(); 

        return messageElementControlOptional.isPresent(); 
       } 
      } 

      @Override 
      public ListCell<T> call(ListView<T> param) { 
       //Create cell that displays content 
       ListCell<T> cell = new ListCell<T>() { 
        @Override 
        protected void updateItem(T item, boolean empty) { 
         super.updateItem(item, empty); 

         if (!empty && item != null) { 
          setGraphic(item); 
         } 
        } 
       };    

       //Add and remove item when cell is reused for different item 
       cell.itemProperty().addListener((observable, oldItem, newItem) -> { 
        synchronized (TrackingListCellFactory.this) { 
         if (oldItem != null) { 
          visibleItems.remove(oldItem); 
         } 

         if (newItem != null) { 
          visibleItems.add(newItem); 
         }     
        } 
       }); 

       //Update set when bounds of item change 
       ChangeListener<Object> boundsChangeHandler = (observable, oldValue, newValue) -> { 
        synchronized (TrackingListCellFactory.this) { 
         T item = cell.getItem(); 

         if (item != null) { 
          visibleItems.add(item); 
         } 
        } 
       }); 

       //Must update either if cell changes bounds, or if cell moves within scene (e.g.by scrolling) 
      cell.boundsInLocalProperty().addListener(boundsChangeHandler); 
    cell.localToSceneTransformProperty().addListener(boundsChangeHandler);      

      return cell;            
      } 
     } 
    } 
+0

https://stackoverflow.com/questions/46474385/how-to-find-the-indices-of-the-visible-rows-in-a-tableview-in-javafx-9에서 살펴보십시오. 테이블 뷰 행에 대해 유사한 기능을 제공합니다. –

+0

답장을 보내 주셔서 감사합니다. 내가 제공 한 샘플에 대한 빠른 코드 리뷰는 TableView에서 사용 된 것과 거의 동일한 기술을 보여줍니다. 그러나, 제 문제는 계산 논리보다는 멀티 스레딩 문제에 있습니다. 그래서 기본적으로 호출 스레드에서 getItemVisibility() 팩토리 메서드를 호출하기 전에 항목이 추가되고 공장에서 렌더링되었는지 확인해야합니다. – Andreas

+0

http://stackoverflow.com/help/mcve – kleopatra

답변

-1

, 나는이 문제를 해결하는 최선의 방법은 참으로 자체 따라서 UI 스레드에서 모든 일을 세포 공장 내부의 계산 논리를 이동 결론을 내렸다. 그래서, 기본적으로 마법처럼 작동합니다

//Add and remove item when cell is reused for different item 
      final ChangeListener<T> itemChangedEventHandler = (observable, oldValue, newValue) -> { 
       synchronized (TrackingListCellFactory.this) { 
        if (oldValue != null) { 
         visibleItems.remove(oldValue); 
        } 

        if (newValue != null) { 
         visibleItems.add(newValue); 
         updateMessengerServiceControlModel(newValue, MessageStatus.MessageStatusEnum.SEEN);   
        }     
       } 
      }; 

      //Update set when bounds of item change 
      final ChangeListener<Object> boundsChangedHandler = (observable, oldValue, newValue) -> { 
       synchronized (TrackingListCellFactory.this) { 
        T item = cell.getItem();      

        if (item != null) { 
         visibleItems.add(item); 
         updateMessengerServiceControlModel(item, MessageStatus.MessageStatusEnum.SEEN); 
        }      
       } 
      }; 

      cell.itemProperty().addListener(itemChangedEventHandler); 

      //Must update either if cell changes bounds, or if cell moves within scene (e.g. by scrolling): 
      cell.boundsInLocalProperty().addListener(boundsChangedHandler); 
      cell.localToSceneTransformProperty().addListener(boundsChangedHandler); 

      return cell; 

이럴이 더 표시되는 항목이 다른 스레드에서 읽을 경우 동기화 블록을 제거 할 수 있습니다 등 항목의 인덱스를 사용하는 것보다 깨끗하고 우아한 솔루션입니다. 이렇게하면 공장 성능이 향상됩니다.