1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[UE4]ListViewでスクロールを検知する

Posted at

はじめに

Scroll BoxにあるOn User Scrolledイベントと同じように、List Viewでもスクロールを検知できるようにしました。
image.png

List Viewの改造に関してはこちらの記事も書きました。
今回もエンジンの改造は行わず、C++でListViewを継承する形で実現しています。

環境

  • Windows 10
  • Unreal Engine 4.27

実装

思うように処理が作れなかったので、無理やりな実装になってしまいました。
もっときれいに作れるやり方があれば教えてください。

作りたかった処理の流れ

下記の流れで実装するつもりでしたが、3のところでBindUObjectを使って通知を受け取るところが上手くいかず、断念しました。
エディタ上での実行だと動くのですが、パッケージングするとクラッシュして動きませんでした。
(AccessViollationが発生、クラッシュした細かな原因までは追えていません。)

  1. OnTableViewScrolled(SListViewクラスに元々あるProtectedのデリゲート)でスクロールを検知
  2. SListViewを継承したクラスで1の通知を受け取って、Publicなデリゲートを呼び出し
  3. UListViewを継承したクラスで2の通知を受け取って、BluePrintに通知するデリゲートを呼び出し

実際に作った処理の流れ

  1. UListViewを継承したクラスの参照をSListViewを継承したクラスに持たせておく
  2. OnTableViewScrolled(SListViewクラスに元々あるProtectedのデリゲート)でスクロールを検知
  3. 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に接続してバインドさせます。
image.png

参考サイト

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?