自定义Struct的Detail界面

本文介绍如何在UE4中针对特定结构体FPaladinNPCPatrolActionInfo自定义编辑器界面,通过插件实现根据不同枚举类型动态显示相关属性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

结构体如下:

USTRUCT(BlueprintType)
struct FPaladinNPCPatrolActionInfo
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	EPaladinNPCActionType ActionType;

	//ActionType为GotoTarget的时候生效,指向NPC需要移动的哪个位置
	UPROPERTY(EditAnywhere, BlueprintReadWrite ,meta = (EnumCondition = 0))
	AActor* targetActor;
	//ActionType为GotoTarget的时候生效,移动到指定位置需要的时间,单位为秒,默认为1.5秒
	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EnumCondition = 0))
	float gotoTime = 1.5f;
	//ActionType为Montage的时候生效,指定多个蒙太奇动画
	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EnumCondition = 1))
	TArray<UAnimMontage*> Montages;
	//ActionType为AnimSequences的时候生效,可以指定多个AnimSequence
	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EnumCondition = 2))
	TArray<UAnimSequence*> AnimSequences;
	//ActionType为Custom的时候生效,Key为人物ID,Value是指定的自定义结构信息
	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EnumCondition = 3))
	TMap<FName, FPaladinNPCCustomEvent> CustomMap;
};

当ActionType为0的时候我们希望仅仅显示属性targetActor和gotoTime,当ActionType为1的时候仅显示Montages....

但是默认的属性界面是所有属性都显示出来,那么我们需要自定义该界面:

1. 创建编辑器插件,在StartupModule调用:

FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomPropertyTypeLayout("PaladinNPCPatrolActionInfo", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FPaladinNPCPatrolActionInfoCustomization::MakeInstance));

2. 新建h和cpp文件如下:

class FPaladinNPCPatrolActionInfoCustomization
	: public IPropertyTypeCustomization
{
public:
	static TSharedRef<IPropertyTypeCustomization> MakeInstance();

	FPaladinNPCPatrolActionInfoCustomization()
	{ }

	virtual ~FPaladinNPCPatrolActionInfoCustomization() {}

	/** Begin IPropertyTypeCustomization interface */
	virtual void CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override {}
	virtual void CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
	/** End IPropertyTypeCustomization interface */
protected:
	EVisibility ArePropertiesVisible(const int32 VisibleType) const;
	TSharedPtr<IPropertyHandle> EnumProperty;
};
TSharedRef<IPropertyTypeCustomization> FPaladinNPCPatrolActionInfoCustomization::MakeInstance()
{
	return MakeShareable(new FPaladinNPCPatrolActionInfoCustomization());
}

void FPaladinNPCPatrolActionInfoCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
	uint32 NumChildren;
	StructPropertyHandle->GetNumChildren(NumChildren);
	FString Name = StructPropertyHandle->GetPropertyDisplayName().ToString();
	IDetailGroup& Group = StructBuilder.AddGroup(FName(*Name), FText::FromString(Name));

	for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
	{
		TSharedRef<IPropertyHandle> ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef();
		//IDetailPropertyRow& Property = StructBuilder.AddProperty(ChildHandle);

		IDetailPropertyRow& Property = Group.AddPropertyRow(ChildHandle);

		if (ChildHandle->GetProperty()->GetFName() == "ActionType")
		{
			EnumProperty = ChildHandle;
		}
		else
		{
			static const FName EditConditionName = "EnumCondition";
			int32 EnumCondition = ChildHandle->GetINTMetaData(EditConditionName);
			Property.Visibility(TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FPaladinNPCPatrolActionInfoCustomization::ArePropertiesVisible, EnumCondition)));
		}

	}
}

EVisibility FPaladinNPCPatrolActionInfoCustomization::ArePropertiesVisible(const int32 VisibleType) const
{
	uint8 CurrentEnumValue = 0;
	EnumProperty->GetValue(CurrentEnumValue);
	return (CurrentEnumValue == VisibleType) ? EVisibility::Visible : EVisibility::Collapsed;
}

End。

<think>嗯,用户想用华为的DevEco Studio 5.0.2开发一个股票应用,具备多界面显示、实时数据抓取、自定义刷新率和保存设置的功能。首先,我需要确定用户的技术背景,可能对HarmonyOS开发有一定了解,但可能需要详细的步骤指导。 首先,多界面显示意味着需要使用Page Ability和导航组件,比如使用tabs或者导航器来切换不同的页面。可能需要创建多个AbilitySlice,每个对应一个界面,比如主界面、设置界面、股票详情界面等。然后,实时数据抓取部分,需要网络请求,从哪个API获取数据?可能需要推荐一个免费的股票数据接口,比如Alpha Vantage或者Yahoo Finance的API,但用户需要注册获取API Key。这部分代码需要处理异步请求,使用HarmonyOS的异步任务或者Promise。 自定义刷新率的话,应该在设置界面中添加一个输入框或者滑动条,让用户输入刷新频率,然后保存到Preferences里。这样每次应用启动时读取设置,并根据这个频率定时请求数据。保存设置部分需要使用HarmonyOS的数据管理模块,比如Preferences API来持久化存储用户的设置。 代码结构方面,主界面可能用ListContainer展示股票列表,每个条目显示股票代码、当前价格等信息。点击条目跳转到详情页面,显示更多数据,比如K线图。实时数据更新可能需要定时器,比如使用@Timer装饰器或者setInterval函数,按照用户设置的间隔触发数据抓取。 数据抓取部分需要考虑网络请求的权限,需要在config.json里添加internet权限。另外,处理JSON数据解析,可能需要使用第三方库或者HarmonyOS自带的JSON解析工具。异步处理数据更新,确保UI线程不被阻塞。 设置界面需要包含输入框和保存按钮,将用户输入的刷新率保存到Preferences,并在返回主界面时应用新的刷新率。这里可能需要用到事件总线或者回调函数来通知主界面设置已更改,从而调整定时器的间隔。 测试方面,用户可能需要在不同设备上测试布局的适应性,以及网络请求的稳定性和错误处理,比如处理API限制或网络不可用的情况。 可能遇到的问题包括API的调用频率限制,需要合理设置默认刷新率,避免频繁请求导致被封禁。此外,多界面之间的数据传递,比如点击列表项传递股票代码到详情页,需要使用Intent或者全局变量。 最后,需要提供完整的代码示例,包括各个界面的布局XML和对应的JavaScript或ArkTS代码,以及权限配置和依赖管理。步骤要详细,从创建项目开始,逐步添加功能和模块,确保用户能一步步跟随操作。</think>我将为您提供基于HarmonyOS的股票应用开发解决方案(使用ArkTS语言)。以下是分步骤实现说明: 一、环境准备 1. 安装DevEco Studio 5.0.2 2. 配置SDK(API Version 9+) 3. 创建Empty Ability项目(选择ArkTS语言) 二、项目结构规划 ``` src/main/ ├── ets/ │ ├── MainAbility/ │ ├── pages/ // 界面文件 │ │ ├── HomePage.ets // 主界面 │ │ ├── DetailPage.ets // 详情页 │ │ └── Settings.ets // 设置页 │ ├── common/ │ │ └── StockApi.ets // 数据接口封装 │ └── model/ │ └── StockData.ets // 数据模型 ├── resources/ // 资源文件 └── config.json // 应用配置 ``` 三、核心代码实现 1. 主界面(HomePage.ets): ```typescript @Entry @Component struct HomePage { @State stocks: Array<StockData> = [] @StorageLink('refreshRate') refreshRate: number = 5 // 默认5秒刷新 private timerId: number = 0 aboutToAppear() { this.startAutoRefresh() } startAutoRefresh() { this.timerId = setInterval(() => { this.fetchStocks() }, this.refreshRate * 1000) } async fetchStocks() { try { let response = await StockApi.getRealTimeData(['AAPL', 'MSFT', 'TSLA']) this.stocks = response } catch (error) { console.error("数据获取失败: " + error) } } build() { Column() { List({ space: 10 }) { ForEach(this.stocks, (item: StockData) => { ListItem() { StockItem({ data: item }) } }) } .onClick((index) => { router.push({ url: 'pages/DetailPage', params: { symbol: this.stocks[index].symbol }}) }) } } } ``` 2. 股票数据接口(StockApi.ets): ```typescript const BASE_URL = 'https://www.alphavantage.co/query' export class StockApi { static async getRealTimeData(symbols: string[]): Promise<StockData[]> { let results = [] for (let symbol of symbols) { let response = await http.get(`${BASE_URL}?function=GLOBAL_QUOTE&symbol=${symbol}&apikey=YOUR_API_KEY`) let data = this.parseResponse(response.result) results.push(new StockData(symbol, data)) } return results } private static parseResponse(data: any): StockData { return { price: data['05. price'], change: data['09. change'], // 其他字段解析... } } } ``` 3. 设置界面(Settings.ets): ```typescript @Entry @Component struct SettingsPage { @StorageProp('refreshRate') refreshRate: number = 5 build() { Column() { TextInput({ placeholder: '输入刷新频率(秒)' }) .onChange((value) => { this.refreshRate = parseInt(value) }) Button('保存设置') .onClick(() => { AppStorage.setOrCreate('refreshRate', this.refreshRate) router.back() }) } } } ``` 四、关键配置 1. config.json添加网络权限: ```json "reqPermissions": [ { "name": "ohos.permission.INTERNET" } ] ``` 2. 定义数据模型(StockData.ets): ```typescript export class StockData { symbol: string price: number change: number // 其他字段... constructor(symbol: string, data: any) { this.symbol = symbol this.price = parseFloat(data.price) this.change = parseFloat(data.change) } } ``` 五、开发步骤: 1. 创建新项目选择"Empty Ability" 2. 配置gradle依赖(添加网络权限) 3. 按项目结构创建文件 4. 实现数据模型和API接口 5. 编写各页面UI组件 6. 配置路由导航 7. 实现设置存储(使用AppStorage) 8. 测试与调试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值