はじめに
Scroll BoxにあるOn User Scrolledイベントと同じように、List Viewでもスクロールを検知できるようにしました。
List Viewの改造に関してはこちらの記事も書きました。
今回もエンジンの改造は行わず、C++でListViewを継承する形で実現しています。
環境
- Windows 10
- Unreal Engine 4.27
実装
思うように処理が作れなかったので、無理やりな実装になってしまいました。
もっときれいに作れるやり方があれば教えてください。
作りたかった処理の流れ
下記の流れで実装するつもりでしたが、3のところでBindUObjectを使って通知を受け取るところが上手くいかず、断念しました。
エディタ上での実行だと動くのですが、パッケージングするとクラッシュして動きませんでした。
(AccessViollationが発生、クラッシュした細かな原因までは追えていません。)
- OnTableViewScrolled(SListViewクラスに元々あるProtectedのデリゲート)でスクロールを検知
- SListViewを継承したクラスで1の通知を受け取って、Publicなデリゲートを呼び出し
- UListViewを継承したクラスで2の通知を受け取って、BluePrintに通知するデリゲートを呼び出し
実際に作った処理の流れ
- UListViewを継承したクラスの参照をSListViewを継承したクラスに持たせておく
- OnTableViewScrolled(SListViewクラスに元々あるProtectedのデリゲート)でスクロールを検知
- SListViewを継承したクラスで2の通知を受け取って、UListViewを継承したクラスのBluePrintに通知するデリゲートを呼び出し
SListViewCustom.h
#pragma once
#include "CoreMinimal.h"
#include "Widgets/Views/SListView.h"
/**
* SListViewを継承したクラス
*/
template <typename ItemType>
class TEST_API SListViewCustom : public SListView<ItemType>
{
public:
/**
* スクロールの通知を受け取るクラスが継承するインターフェース
*/
class IListViewCustomBindable {
public:
virtual void ExecuteOnScrolled(double)=0;
};
SListViewCustom(ETableViewMode::Type InListMode = ETableViewMode::List)
: SListView(InListMode)
{
}
/** OnTableViewScrolled(元々UEに用意されているデリゲート)にバインド */
void BindOnScrolled(IListViewCustomBindable* list)
{
MyCustomizableListView = list;
this->OnTableViewScrolled.BindRaw(this, &SListViewCustom::ExecuteOnScrolled);
}
private:
IListViewCustomBindable* MyCustomizableListView;
/** スクロールを検知したときに呼ばれる関数 */
UFUNCTION()
void ExecuteOnScrolled(double ScrollOffset)
{
MyCustomizableListView->ExecuteOnScrolled(ScrollOffset);
}
};
CustomizableListView.h
#pragma once
#include "CoreMinimal.h"
#include "Widgets/Views/SListView.h"
#include "Components/ListView.h"
#include "SListViewCustom.h"
#include "CustomizableListView.generated.h"
/**
* ListViewを継承したクラス
*/
UCLASS()
class TEST_API UCustomizableListView : public UListView, public SListViewCustom<UObject*>::IListViewCustomBindable
{
GENERATED_BODY()
public:
UCustomizableListView(const FObjectInitializer& Initializer) : UListView(Initializer){}
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListViewScrolled, float, ScrollOffset); /** Scroll offset from the beginning of the list in items */
/** BPにスクロールを通知 */
UPROPERTY(BlueprintAssignable, Category = Events, meta = (DisplayName = "On List View Scrolled"))
FOnListViewScrolled BP_OnListViewScrolled;
/** SListViewCustomから呼び出してスクロールを通知するインターフェース関数 */
UFUNCTION()
void ExecuteOnScrolled(double ScrollOffset) override;
/** Bind Scroll Event */
UFUNCTION(BlueprintCallable)
void BindScrollEvent();
};
CustomizableListView.cpp
#include "CustomizableListView.h"
void UCustomizableListView::BindScrollEvent()
{
TSharedPtr<SListViewCustom<UObject*>> MyListViewCustom = StaticCastSharedPtr<SListViewCustom<UObject*>>(MyListView);
// SListViewCustomでデリゲートを作ってスクロール時に呼び出すつもりだったが、
// BindUObjectではエディタでの実行時は動いたが、パッケージングするとクラッシュした
// MyListViewCustom->OnListViewScrolled.BindUObject(this, &UCustomizableListView::ExecuteOnScrolled);
// そのため、thisを引数で渡してSListViewCustomから直接ExecuteOnScrolledを呼び出している
MyListViewCustom->BindOnScrolled(this);
}
void UCustomizableListView::ExecuteOnScrolled(double ScrollOffset)
{
BP_OnListViewScrolled.Broadcast(static_cast<float>(ScrollOffset));
}
BindScrollEvent関数をBluePrint側でConstructに接続してバインドさせます。
参考サイト