Unity で、固定ヘッダー付きのScrollViewを作成します。
スクロールバーでコンテンツを移動させても、上側、左側のヘッダーの位置は固定となってます。
やり方はいろいろあると思いますが、私が実装した方法を書きます。
大事なことは、スクロールバー移動のイベントを受け取ったとき、ヘッダーの位置が動かないようにポジションを上書きすることです。
今回作成したプロジェクトは以下のリンクからダウンロードできます。
- Windows10 Home 64bit
- Unity Version 2020.3.17f1 Personal
- Visual Studio Community 2019 16.11.2
ScrollView の設定
ScrollView を使用します。GameObject→UI→ScrollView で追加できます。
ScrollView のパラメータ
- Rect Transform のWidth、Heightを設定します。
- Scroll Rect コンポーネントのMovement Type をClamped に変更します。
ゲームオブジェクトの順番は、Panel_Cell、Panel_ColumnHeader、Panel_RowHeader、Panel_Header の順番は変更しません。スクロールしたとき、GameObjectの上から順に上書きするからです。
Content 以下の構成
Scroll View のContent 以下の構成です。Contentの子Panel、それ以降の子はすべてGameObject→UI→Panel で設定します。
Panel
Contentと同じ領域。背景を白にする。
Panel_Cell
全セルのルート。
Panel_Cell_Org
Cell のコピー元です。スクリプトでコピーして使います。
Panel_ColumnHeader
列ヘッダーのルートです。スクリプトにて、このオブジェクトの子にヘッダーを追加します。
Panel_ColumnHeader_Org
列ヘッダーのパネルのコピー元です。スクリプトでコピーして使います。
Panel_RowHeader
Panel_RowHeader_Org
行ヘッダーのパネルのコピー元です。スクリプトでコピーして使います。
Panel_Header
左上の黒いところのパネルです。
ScrollView のコンポーネントに設定するスクリプト
ScrollView に設定するコードです。
このスクリプトは、ScrollView オブジェクトにコンポーネントとして追加します。
また、ScrollView_Horizontal、ScrollView_Vertical 関数は、それぞれScrollbar Horizontal、ScrollView_Vertical オブジェクトのScrollbar コンポーネント→On Value Changed に設定します。
using UnityEngine;
using UnityEngine.UI;
namespace Assets
{
// スクロールビューコントローラー
class ScrollViewController : MonoBehaviour
{
private GameObject viewport;
private GameObject content;
private GameObject panel;
private GameObject header;
private GameObject columnHeader;
private GameObject rowHeader;
private GameObject cell;
// 開始
private void Start()
{
viewport = transform.Find("Viewport").gameObject;
content = viewport.transform.Find("Content").gameObject;
panel = content.transform.Find("Panel").gameObject;
header = panel.transform.Find("Panel_Header").gameObject;
columnHeader = panel.transform.Find("Panel_ColumnHeader").gameObject;
rowHeader = panel.transform.Find("Panel_RowHeader").gameObject;
cell = panel.transform.Find("Panel_Cell").gameObject;
float cellWidth = header.GetComponent<RectTransform>().sizeDelta.x;
float cellHeight = header.GetComponent<RectTransform>().sizeDelta.y;
// 列ヘッダー数10
for ( int i = 0; i<10; i++)
{
GameObject columnHeaderChild = GameObject.Instantiate(columnHeader.transform.Find("Panel_ColumnHeader_Org")).gameObject;
columnHeaderChild.transform.parent = columnHeader.transform;
columnHeaderChild.transform.localPosition = new Vector2(i * cellWidth, 0);
Text text = columnHeaderChild.transform.Find("Text").GetComponent<Text>();
text.text = "Column" + (i + 1);
columnHeaderChild.name = "Column" + (i + 1);
columnHeaderChild.SetActive(true);
}
// 行ヘッダー数15
for (int j = 0; j < 15; j++)
{
GameObject rowHeaderChild = GameObject.Instantiate(rowHeader.transform.Find("Panel_RowHeader_Org")).gameObject;
rowHeaderChild.transform.parent = rowHeader.transform;
rowHeaderChild.transform.localPosition = new Vector2(0, -j * cellHeight);
Text text = rowHeaderChild.transform.Find("Text").GetComponent<Text>();
text.text = "Row" + (j + 1);
rowHeaderChild.name = "Row" + (j + 1);
rowHeaderChild.SetActive(true);
}
// セル 10×15
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 15; j++)
{
GameObject cellChild = GameObject.Instantiate(cell.transform.Find("Panel_Cell_Org")).gameObject;
cellChild.transform.parent = cell.transform;
cellChild.transform.localPosition = new Vector2(i * cellWidth, -j * cellHeight);
Text text = cellChild.transform.Find("Text").GetComponent<Text>();
text.text = "Cell" + (i + 1) + (j + 1);
cellChild.name = "Cell" + (i + 1) + (j + 1);
cellChild.SetActive(true);
}
}
content.GetComponent<RectTransform>().sizeDelta = new Vector2(cellWidth * (1 + 10), cellHeight * (1 + 15));
}
// 水平スクロールバー移動
public void ScrollView_Horizontal(float value)
{
float yContent = content.GetComponent<RectTransform>().anchoredPosition.y;
float contentWidth = content.GetComponent<RectTransform>().rect.width;
float viewportWidth = viewport.GetComponent<RectTransform>().rect.width;
float diff = (contentWidth - viewportWidth) * value;
if (diff < 0)
{
diff = 0;
}
float cellHeight = header.GetComponent<RectTransform>().sizeDelta.y;
rowHeader.GetComponent<RectTransform>().anchoredPosition = new Vector2(diff, -cellHeight);
header.GetComponent<RectTransform>().anchoredPosition = new Vector2(diff, -yContent);
}
// 垂直スクロールバー移動
public void ScrollView_Vertical(float value)
{
float xContent = content.GetComponent<RectTransform>().anchoredPosition.x;
float contactHeight = content.GetComponent<RectTransform>().rect.height;
float viewportHeight = viewport.GetComponent<RectTransform>().rect.height;
float diff = (contactHeight - viewportHeight) * (1-value);
if (diff < 0)
{
diff = 0;
}
float cellWidth = header.GetComponent<RectTransform>().sizeDelta.x;
columnHeader.GetComponent<RectTransform>().anchoredPosition = new Vector2(cellWidth, -diff);
header.GetComponent<RectTransform>().anchoredPosition = new Vector2(-xContent, -diff);
}
}
}
Start 関数
実行時に呼ばれる関数です。
ここでは、ヘッダー、セルを生成しています。今回は、10列×15行のヘッダー、セルを作成しました。Content の幅、高さをテーブル全体と一致させます。
Content の領域が、スクロールバーでスクロールできる範囲となります。
Start関数実行後は、↓の表が表示されます。
ScrollView_Horizontal、ScrollView_Vertical 関数
それぞれ、水平スクロールバー、垂直スクロールバーが移動したときに呼ばれます。
ここでは、スクロールバーの位置に合わせて、ヘッダーの位置がViewport の位置から変化しないようにしてます。なので、スクロールバーの位置に対し、ヘッダーの固定位置を再計算し、ヘッダー位置を上書きしています。
この方法で、ヘッダー位置を固定して見せることができます。
まとめ
今回は、Unity のScrollView で固定ヘッダー付きのScrollViewを作成する方法を説明しました。大事なことは、スクロールバーのイベントを受け取ったとき、ヘッダーの位置が動かないようにポジションを上書きすることです。
コメント