モジュール 2 タスク 2: スクロールコントローラ機能の追加


JavaFX メディアブラウザアプリケーションを起動する NetBeans プロジェクトをダウンロードする

はじめに

このタスクは、スクロールコントローラを表示して、壁に表示されるサムネイルのサブセットをユーザーが選択できるようにします。スクロールコントローラは、カスタムの ScrollControl コンポーネントを作成することで開発されています。スクロールコントローラエレベータは、使用可能なコンテンツを表します。これは一連の垂直リッジで構成されます。リッジは壁にあるサムネイルの各列ごとに 1 つずつです。エレベータをクリックして、壁を目的の位置までスクロールします。半透明の矩形がエレベータにオーバーレイ表示され、現在表示されている列が強調表示されます。エレベータは移動しませんが、使用可能なコンテンツの量が変化するにつれてにサイズが変わります。オーバーレイの矩形をドラッグして、壁をスクロールします。

プロジェクトの実行

  1. モジュール 2 タスク 2 NetBeans プロジェクトをダウンロードして、NetBeans で開きます。

  2. プロジェクトを実行します。

  3. スクロールコントローラをクリックして、サムネイルの壁が水平にスクロールするのを観察します。

図 1 に、このタスクのアーキテクチャーを示します。

モジュール 2 タスク 2 のアーキテクチャー 図 1

スクロールコントローラ機能の構築

スクロールコントローラ機能 (図 2) は ScrollControl.fx で構築され、Wall.fx に組み込まれます。

ScrollControl.fx は、サムネイルの列の数に基づいてスクロールコントローラエレベータのサイズを決定します。ScrollControl.fx は、マウスのクリックおよびドラッグに基づいてスクロールコントローラの動作を決定します。

Wall.fx は、スクロールコントローラの可視性フラグを (fractionDisplayed < 1) に関連付けることにより、その可視性を制御します。壁はステージよりも広いことが多く、fractionDisplayed は表示される壁の一部です。

scrollPosition 変数をコントローラの位置変数にバインドすることにより、壁の位置がスクロールコントローラに接続されます。scrollPosition をスクロールコントローラの位置変数に逆にバインドすることにより、スクロールコントローラで壁の位置を決めたり、壁でスクロールコントローラの位置を決めることができます。

両方のクラスが連携してオーバーレイの動作を作成し、それはエレベータの範囲に制約されます。その位置は、次の画像に示されているように左端です。スライダの動作の範囲は、次のとおりです。

0 <= po <= 1 (1 は「エレベータの幅 - オーバーレイの幅」を表します)

スクロールバーのデフォルトの位置 図 2

同様に壁の動作も、ウィンドウ全体がサムネイルで埋められるように制約されます。壁の動作の範囲は、次のとおりです。

0 <= pw <= 1 (1 は「壁の幅 - ステージの幅」を表します)

図 3 は、オーバーレイが右端に達すると、壁が左端までスクロールすることを示しています。壁とエレベータの動作は、ウィンドウが常にサムネイルで埋められるように制約されます。

スクロールバーの右端の位置 図 3

スクロールコントローラ機能は、次の 4 つの主な要素で構成されます。

  • スクロールコントローラのグラフィックス
  • エレベータリッジ
  • マウスコントロール
  • 算術計算

スクロールコントローラのグラフィックス

スクロールコントローラ (図 4) は、ScrollControl.fx 内で javafx.scene.paint クラスと javafx.scene.rectangle クラスを使用して描画されます。

ファサード、エレベータリッジ、水平オーバーレイを含む吹き出しが付いたスクロールコントローラの各部 図 4

コードサンプルには、エレベータの背後のファサード、水平オーバーレイ、およびエレベータリッジが含まれています。

ソースコード
    protected override function create(): Node {
        Group {
            content: [
                // The translucent facade behind the elevator.
                Rectangle {
                    x: 0,
                    y: 0,
                    width: bind width,
                    height: Constants.SCROLLCTL_HEIGHT,

					// Make the facade translucent.
                    opacity: 0.2

					// Fade the facade from top to bottom.
                    fill: LinearGradient {
                        startX: 0.0
                        startY: 0.0
                        endX: 0.0
                        endY: 1.0
                        proportional: true
                        stops: [
                            Stop { offset: 0.0 color: Constants.SCROLLCTL_COLOR },
                            Stop { offset: 1.0 color: Color.BLACK }
                        ]
                    }

                    // Draw a soft outline.
                    stroke: Constants.SCROLLCTL_COLOR;
                },

                // The elevator.
                Elevator {
                    scrollCtl: this
                    position: bind position
                }
            ];
        }
    }
}

エレベータリッジ

画像の各列を表すスクロールコントローラ内のエレベータリッジ 図 5

スクロールコントローラのエレベータ (図 5) には、壁に表示されるサムネイルの列の数に対応した垂直リッジが含まれます。この数は ScrollControl.fx クラスで計算されます。

ソースコード
      // The minimum necessary change in the sequence is made when the
            // desired number of ridges is changed.
            if (columns > numRidges) {
                insert for (k in [numRidges..columns-1]) {
                    Rectangle {
                        // Position the ridge based on its index.
                        translateX: ridgeWidth * k

                        // The ridge's size and spacing are based on the
                        // scrollCtl's height.
                        x: ridgeWidth * 0.3
                        y: Constants.SCROLLCTL_HEIGHT * 0.25
                        width: ridgeWidth * 0.4
                        height: Constants.SCROLLCTL_HEIGHT * 0.5
                        arcWidth: ridgeWidth * 0.3
                        arcHeight: ridgeWidth * 0.3
                        fill: Constants.SCROLLCTL_COLOR
                        opacity: 0.5
                        scaleY: bind ridgeScaleFunc(k)

                        // Make sure mouse events get through to the underlying
                        // rectangle when the user clicks on the elevator.
                        blocksMouse: false
                    }
                } into elevatorContent;
            } else {
                delete elevatorContent[columns+2..numRidges+1];
            }
            numRidges = columns;

コードサンプルでは、要素 0 および 1 はエレベータ全体およびオーバーレイの下にあるクリック可能な矩形であるため、リッジのインデックスは 2 から始まります。

マウスコントロール

エレベータをクリックすると、その位置の中央に壁とエレベータのオーバーレイが配置されます。エレベータのオーバーレイも任意の場所にドラッグできます。マウスイベントと、オーバーレイのセンタリングアニメーションは、マウスボタンの解放でアニメーションが開始されるという関係にあります。

マウスコントロールは ScrollControl.fx に含まれています。マウスイベントには、onMouseClickedonMousePressedonMouseDragged、および onMouseReleased の 4 つがあります。このタスク用の基本的なマウスイベントは、onMouseClicked 関数です。その他のマウスイベントは、スクロールコントローラのオーバーレイを移動した後にふたたび中央に配置するアニメーションに関係します。

算術計算

壁を水平に移動する鍵となるのは、エレベータが移動するときは常に Wall.fx 内の thumbs のtranslateX を設定することです。thumbs は var ですが、実際は変数ではないため、var ではなく def として使用されます。thumbs が割り当ての文で初期化されると、その値は変更されません。次の Wall.fx のコードサンプルは、thumbs の定義を示しています。

ソースコード
def thumbs : Group = Group {
        content: thumbnails,
        translateX: bind scrollOffset,
        translateY: bind (maxVisibleHeight -
                          Constants.SCROLLCTL_HEIGHT -
                          thumbs.boundsInLocal.height) / 2

scrollOffset は、scrollPosition 変数に基づいて壁を水平に移動する量です。バインドの式は、scrollPosition の範囲 (0-1) を、壁の移動可能範囲にピクセル単位でマップします。

ソースコード
  def scrollOffset : Number = bind -scrollPosition * (thumbsGroupWidth - maxVisibleWidth);

scrollPosition は、壁の移動量を表す 0 ~ 1 の間の値を持つ変数から派生します。scrollPosition の先頭のマイナス記号 (-) は、サムネイルの壁をスクロールコントローラの動きとは反対方向に移動します。

scrollPositionscrollGallery 関数によって維持されます。この関数は、スクロール機能の変数 ScrollControl に割り当てられます。この関数の割り当てにより、ScrollControl は Wall に対して、Wall の変数に関知せずに自己スクロールするように要求できます。次の Wall.fx のコードを確認してください。

ソースコード
  def scrollCtl = ScrollControl {
        translateX : Constants.SCROLLCTL_RESERVE
        translateY : bind maxVisibleHeight - Constants.SCROLLCTL_HEIGHT
        width : bind maxVisibleWidth - 2 * Constants.SCROLLCTL_RESERVE
        position : bind scrollPosition with inverse
        fractionDisplayed : bind fractionDisplayed
        columns : bind wallColumns()
        visible: bind fractionDisplayed < 1.0
    }

fractionDisplayed は、壁がどのくらいステージに表示されるのかを制御します。サムネイルが少ないほど、エレベータのリッジは少なくなります。fractionDisplayed が 1 よりも小さい場合は、使用可能で非表示になっている画像があります。fractionDisplayed は、壁の幅と、ステージの幅を壁の幅で割った計算値に基づきます。壁の幅は、保持しているサムネイルの列の数に基づいて計算されます。fractionDisplayed が 1 よりも小さい場合は、ScrollControl を作成する必要があります。ScrollControl は必要な場合にのみ作成されます。

ScrollControl は、スクロールコントローラの外観や動作を定義するクラスです。

リッジの背後にある矩形は、マウスクリックを処理します。マウスイベントが発生すると、スクロールコントローラはアニメーションの期間内 (1.5 秒持続) に x 値を変換することにより、それ自身をふたたび中央に配置します。

試してみましょう

ジャンプスクロールの作成

スクロールコントローラは滑らかなスクロールを行います。壁をステップ単位で移動させることにより、スクロールコントローラがジャンプスクロールを行うようにすることができます。これを行うには、壁が各ステップでスクロールする量を変更します。たとえば、壁をサムネイルの幅の 4 分の 1 だけスクロールさせることができます。これを機能させるために、マウスが移動した距離を追跡する必要があります。

垂直スクロール

もう一つのオプションは、スクロールコントローラを水平ではなく垂直にスクロールするように変更することです。高さと幅のすべてのインスタンスを探して、それらが変数にバインドされるようにします。壁が垂直に動いたら、壁を変更して thumbs を 3 列 × n 行に配置できます。fractionDisplayed の計算では、幅ではなく高さを使用する必要があり、scrollGallery では、translateX ではなく translateY を使用する必要があります。

 

English
日本語
한국어
简体中文
Português do Brasil
русский