モジュール 2 タスク 2: スクロールコントローラ機能の追加
- 概要
-
javafx.sceneクラスを使用してスクロールコントローラ機能を描画する。- スクロール動作を制御できるマウスイベントを追加する。
scrollOffsetを計算する数式を作成する。これによってサムネイルの列が水平に移動するタイミングを決められます。
はじめに
このタスクは、スクロールコントローラを表示して、壁に表示されるサムネイルのサブセットをユーザーが選択できるようにします。スクロールコントローラは、カスタムの ScrollControl コンポーネントを作成することで開発されています。スクロールコントローラエレベータは、使用可能なコンテンツを表します。これは一連の垂直リッジで構成されます。リッジは壁にあるサムネイルの各列ごとに 1 つずつです。エレベータをクリックして、壁を目的の位置までスクロールします。半透明の矩形がエレベータにオーバーレイ表示され、現在表示されている列が強調表示されます。エレベータは移動しませんが、使用可能なコンテンツの量が変化するにつれてにサイズが変わります。オーバーレイの矩形をドラッグして、壁をスクロールします。
プロジェクトの実行
- モジュール 2 タスク 2 NetBeans プロジェクトをダウンロードして、NetBeans で開きます。
- プロジェクトを実行します。
- スクロールコントローラをクリックして、サムネイルの壁が水平にスクロールするのを観察します。
図 1 に、このタスクのアーキテクチャーを示します。
図 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 に含まれています。マウスイベントには、onMouseClicked、onMousePressed、onMouseDragged、および 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 の先頭のマイナス記号 (-) は、サムネイルの壁をスクロールコントローラの動きとは反対方向に移動します。
scrollPosition は scrollGallery 関数によって維持されます。この関数は、スクロール機能の変数 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 を使用する必要があります。
