2016-11-11 8 views
0

最初に私の質問は、新たに追加された行をJavaFxでフラッシュさせる方法です。次に、このトピックに関する多くの質問(例:javafx: table row flashing)を調べました。それらのほとんどはsetRowFactoryオーバーライド行の疑似クラスの状態を変更するタイムラインアニメーションを追加することによりupdateItem方法を使用しています。 以下は私のコードですが、私は再利用できるFlashControlを構築しようとしています。tableRowが表示されなくてもフラッシュする方法(JavaFx)

public class TableFlashControl<T> { 

    private PseudoClass flashHighlight = PseudoClass.getPseudoClass("flash-highlight"); 
    private List<T> newAdded = new ArrayList<>(); 
    private boolean isFilterApplied = false; 
    private boolean isSorted = false; 

    public void setIsFilterApplied(boolean isFilterApplied) { 
     this.isFilterApplied = isFilterApplied; 
    } 

    public void add(TableView<T> table){ 
     ListChangeListener<T> change = c -> { 
      while (c.next()) { 
       if (c.wasAdded()) { 
        List<? extends T> added = c.getAddedSubList(); 
        T lastAdded = added.get(0); 
        if (!isFilterApplied) { 
         newAdded.add(lastAdded); 
        } 
       } 
      } 
     }; 
     table.getItems().addListener(change); 
     table.setRowFactory(param -> new TableRow<T>() { 
      @Override 
      protected void updateItem(T item, boolean empty) { 
       super.updateItem(item, empty); 
       if (item == null || empty) { 
        return; 
       } 
       if (newAdded.contains(item)) { 
        if (isSorted) { 
         new Thread(()->{ 
          Timeline flasher = new Timeline(
            new KeyFrame(Duration.seconds(0.4), e -> pseudoClassStateChanged(flashHighlight, true)), 
            new KeyFrame(Duration.seconds(0.8), e -> pseudoClassStateChanged(flashHighlight, false)) 
          ); 
          flasher.setCycleCount(2); 
          flasher.play(); 
         }).start(); 
         if (item == newAdded.get(0)) { 
          newAdded.clear(); 
          isSorted = false; 
         } 

        }else{ 
         if(item == newAdded.get(0)){ 
          isSorted = true; 
         } 

        } 
       } 

      } 
     }); 
    } 
} 

ここListChangeListenerは私が新たに挿入された行を記録するのに役立つtable.getItems()に関連付けられています。

複数の行を1回の操作で追加することができます。つまり、newAdded.size()は1より大きい場合があります。 (私はナンバーでそれを並べ替えるため。)しかも、行はのtableViewのトップから挿入されている

のtableViewではなく、すべての行が表示され、updateItem方法はのみ表示される行を更新します。私の問題は、これらの2つの状況が起こったときです(下記参照)。最初のシナリオで

最初のシナリオ

The first scenario

、唯一の4行は、ユーザが1つの時間内に5行を挿入した場合、私が最後の行の更新(updateItemウォンを記録することができず、表示されていますnew_row_5)を呼び出す必要があります。これにより、私はクリアできないが、第2のシナリオでは

第2のシナリオ

The second scenario

newAdded.clear()を行うことによって)リストをnewAdded、唯一の4行が再び表示されています。しかし、見える行の上と下の両方に目に見えない行があります。ユーザーが2行を挿入すると、が表示され、もう1つが非表示になります。私の場合、new_row_2が点滅し、new_row_1は見えなくなります。 new_row_2が点滅しているときのtableViewアップユーザーがスクロールは、彼がnew_row_1ながらnew_row_2あるを点滅表示されます場合は本当に奇妙であるではありません。

また、表示可能な行の数を見つける方法があるかどうかを知りたい場合もあります。

私はまだJavaFxが新しく、この方法が良いかどうかわかりません。私は誰かが私の問題を解決するのを助けることを願っていますどうもありがとう!

+0

わずかにOTですが、なぜ「タイムライン」をスレッドにラッピングしていますか?新しいスレッドは単にタイムラインを開始し、ただちに終了します。 FXアプリケーションスレッドでこれを完全に実行することができます。 –

+0

@James_D申し訳ありませんが、それは私のせいでした...私は新しいスレッドを削除する必要があります.. – ZanderXu

答えて

0

あなたのアプローチはこれを行うためのきれいな方法のようではありません。アニメーションはTableRowに依存し、アイテムは配置されており、複数のアニメーションを同時にサポートするようには見えません。さらに、それは、項目クラスのメソッドがオーバーライドされず、ユーザがTableViewに項目を何度も追加しないことに依存する。また、Timelineが多数作成される可能性があります(Timeline.play()はブロックされないため、別個のスレッドbtwから開始する必要はありません)。

アニメーションをインデックスに依存させる方が良いです。また、TableRowが作成されたことを追跡することで、アニメーション化する必要があるインデックスを割り当てる必要がある場合、既存のセルにアクセスすることができます。また、適切なデータ構造でデータを保存することで、単一のAnimationTimerを使用してアニメーションを処理することもできます。

また、rowFactoryクラスを使用してこのロジックを実装するのが最も便利です。

次の例では、画面上にあるかどうかにかかわらず行を点滅させます。

public class FlashTableRowFactory<T> implements Callback<TableView<T>, TableRow<T>> { 

    private final static PseudoClass FLASH_HIGHLIGHT = PseudoClass.getPseudoClass("flash-highlight"); 

    public FlashTableRowFactory(TableView<T> tableView) { 
     tableView.getItems().addListener((ListChangeListener.Change<? extends T> c) -> { 
      while (c.next()) { 
       if (c.wasPermutated()) { 
        int from = c.getFrom(); 
        int to = c.getTo(); 
        permutationUpdate(scheduledTasks, c, from, to); 
        permutationUpdate(unscheduledTasks, c, from, to); 
       } 
       if (c.wasReplaced()) { 
        addRange(c.getFrom(), c.getTo()); 
       } else if (c.wasRemoved()) { 
        int from = c.getFrom(); 
        int removed = c.getRemovedSize(); 
        removeRange(scheduledTasks, from, from + removed); 
        removeRange(unscheduledTasks, from, from + removed); 
        modifyIndices(unscheduledTasks, from, -removed); 
        modifyIndices(scheduledTasks, from, -removed); 
       } else if (c.wasAdded()) { 
        int from = c.getFrom(); 
        int to = c.getTo(); 
        modifyIndices(unscheduledTasks, from, to - from); 
        modifyIndices(scheduledTasks, from, to - from); 
        addRange(from, to); 
       } 
      } 

      // remove all flashTasks that are older than the youngest for a 
      // given index 
      Set<Integer> indices = new HashSet<>(); 
      removeDuplicates(unscheduledTasks, indices); 
      removeDuplicates(scheduledTasks, indices); 

      flashingIndices.clear(); 
      updateFlash(lastUpdate); 
      refreshFlash(); 
      if (!unscheduledTasks.isEmpty()) { 
       flasher.start(); 
      } 
     }); 
     this.tableView = tableView; 
    } 

    private static void removeDuplicates(List<FlashTask> list, Set<Integer> found) { 
     for (Iterator<FlashTask> iterator = list.iterator(); iterator.hasNext();) { 
      FlashTask next = iterator.next(); 
      if (!found.add(next.index)) { 
       iterator.remove(); 
      } 
     } 
    } 

    private static void modifyIndices(List<FlashTask> list, int minModify, int by) { 
     for (FlashTask task : list) { 
      if (task.index >= minModify) { 
       task.index += by; 
      } 
     } 
    } 

    private void addRange(int index, int to) { 
     for (; index < to; index++) { 
      unscheduledTasks.add(new FlashTask(index)); 
     } 
    } 

    private static void removeRange(List<FlashTask> list, int from, int to) { 
     for (Iterator<FlashTask> iterator = list.iterator(); iterator.hasNext();) { 
      FlashTask next = iterator.next(); 
      if (next.index >= from && next.index < to) { 
       iterator.remove(); 
      } 
     } 
    } 

    private static void permutationUpdate(List<FlashTask> list, ListChangeListener.Change<?> c, int from, int to) { 
     for (FlashTask task : list) { 
      if (task.index < to && task.index >= from) { 
       task.index = c.getPermutation(task.index); 
      } 
     } 
    } 

    // set of item indices that should flash 
    private final Set<Integer> flashingIndices = new HashSet<>(); 

    // references to every row created by this factory 
    private final List<SoftReference<TableRow<T>>> rows = new LinkedList<>(); 

    // tasks waiting to be scheduled 
    private final List<FlashTask> unscheduledTasks = new LinkedList<>(); 

    // tasks currently being animated 
    private final List<FlashTask> scheduledTasks = new LinkedList<>(); 

    private static class FlashTask { 

     int index; 
     long schedulingTime; 

     public FlashTask(int index) { 
      this.index = index; 
     } 

    } 

    private final TableView<T> tableView; 
    private long lastUpdate; 

    /** 
    * Updates flashingIndices set 
    * @param now the current timestamp 
    * @return true if the set has been modified, otherwise false. 
    */ 
    private boolean updateFlash(long now) { 
     boolean modified = false; 
     for (Iterator<FlashTask> iterator = scheduledTasks.iterator(); iterator.hasNext();) { 
      FlashTask next = iterator.next(); 

      // running time in seconds 
      double runningTime = (now - next.schedulingTime)/(1000d * 1000d * 1000d); 

      // slows down the animation for demonstration 
      final double animationSpeed = 0.1; 

      if (runningTime < 0.4/animationSpeed) { 
       // no need to handle tasks that run for less than 0.4 seconds 
       break; 
      } else if (runningTime > 1.6/animationSpeed) { 
       // end of task reached 
       iterator.remove(); 
       modified |= flashingIndices.remove(next.index); 
      } else if (runningTime > 0.8/animationSpeed && runningTime < 1.2/animationSpeed) { 
       // second "inactive" interval during animation 
       modified |= flashingIndices.remove(next.index); 
      } else { 
       // activate otherwise 
       modified |= flashingIndices.add(next.index); 
      } 
     } 
     return modified; 
    } 

    private final AnimationTimer flasher = new AnimationTimer() { 

     @Override 
     public void handle(long now) { 
      lastUpdate = now; 

      // activate waiting flash tasks 
      for (FlashTask task : unscheduledTasks) { 
       task.schedulingTime = now; 
      } 
      scheduledTasks.addAll(unscheduledTasks); 
      unscheduledTasks.clear(); 

      if (updateFlash(now)) { 
       refreshFlash(); 
       if (scheduledTasks.isEmpty()) { 
        // stop, if there are no more tasks 
        stop(); 
       } 
      } 
     } 

    }; 

    /** 
    * Sets the pseudoclasses of rows based on flashingIndices set 
    */ 
    private void refreshFlash() { 
     for (Iterator<SoftReference<TableRow<T>>> iterator = rows.iterator(); iterator.hasNext();) { 
      SoftReference<TableRow<T>> next = iterator.next(); 
      TableRow<T> row = next.get(); 
      if (row == null) { 
       // remove references claimed by garbage collection 
       iterator.remove(); 
      } else { 
       row.pseudoClassStateChanged(FLASH_HIGHLIGHT, flashingIndices.contains(row.getIndex())); 
      } 
     } 
    } 

    @Override 
    public TableRow<T> call(TableView<T> param) { 
     if (tableView != param) { 
      throw new IllegalArgumentException("This factory can only be used with the table passed to the constructor"); 
     } 
     return new FlashRow(); 
    } 

    private class FlashRow extends TableRow<T> { 

     { 
      rows.add(new SoftReference<>(this)); 
     } 

     @Override 
     public void updateIndex(int i) { 
      super.updateIndex(i); 

      // update pseudoclass based on flashingIndices set 
      pseudoClassStateChanged(FLASH_HIGHLIGHT, flashingIndices.contains(i)); 
     } 

    } 

} 
+0

私の質問に答える時間を与えてくれてありがとう。この方法は完全に機能する:D。それはどのように動作するか理解するのに時間がかかりますが、私は本当にたくさん学びます。再度、感謝します! @fabian – ZanderXu

関連する問題