UE4中UMG CheckBox Group实现
发表于2017-12-16
学习UE4的时候,发现UMG居然没有CheckBoxGroup,CheckBox分组需要自己管理。相比于Unity3D来说,UE4在UI方面确实欠缺很多,为此,下面就给大家介绍下实现一个CheckBox Group的方法,没接触过的都可以看看。
实现效果如下:
编辑界面如下:
代码实现如下:
CheckBoxGroup.h
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Components/NamedSlot.h" #include "Components/CanvasPanel.h" #include "Components/CheckBox.h" #include "CheckBoxGroup.generated.h" DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCheckBoxGroupStateChangedEvent, const TArray<UCheckBox*>&, childChangedArray); UCLASS() class UCheckBoxGroup : public UNamedSlot { GENERATED_BODY() public: UCheckBoxGroup(); protected: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CheckBox Group") bool _allowSwitchOff; UPROPERTY(BlueprintAssignable, Category = "CheckBox Group") FOnCheckBoxGroupStateChangedEvent _onStateChangedEvent; UPanelWidget* _groupRoot; FScriptDelegate _onStateChangedScriptDelegate; UCheckBox* _lastCheckedChild; protected: virtual void OnWidgetRebuilt() override; bool InitGroup(); bool InitChild(UCheckBox* child); UFUNCTION(Category = "CheckBox Group") void OnStateChanged(bool bIsChecked); public: UFUNCTION(BlueprintCallable, Category = "CheckBox Group") TArray<UCheckBox*> CheckChildsState(); /* UFUNCTION(BlueprintCallable, Category = "CheckBox Group") bool SetChildState(int nID, bool status); */ UFUNCTION(BlueprintCallable, Category = "CheckBox Group") bool AddGroupChild(UCheckBox* child); UFUNCTION(BlueprintCallable, Category = "CheckBox Group") bool RemoveGroupChild(UCheckBox* child); };
CheckBoxGroup.cpp
// Fill out your copyright notice in the Description page of Project Settings. #include "CheckBoxGroup.h" UCheckBoxGroup::UCheckBoxGroup() { _allowSwitchOff = false; _groupRoot = nullptr; _lastCheckedChild = nullptr; _onStateChangedScriptDelegate.BindUFunction(this, TEXT("OnStateChanged")); // _allowSwitchOff = false; } void UCheckBoxGroup::OnWidgetRebuilt() { _groupRoot = Cast<UPanelWidget>(GetChildAt(0)); if (nullptr != _groupRoot) { InitGroup(); } else { //UE_LOG(PeterLog, Error, TEXT("UCheckBoxGroup isn`t have group root!")); } } bool UCheckBoxGroup::InitGroup() { if (nullptr == _groupRoot) { return false; } int childCnt = _groupRoot->GetChildrenCount(); for (int i = 0; i < childCnt; ++i) { InitChild(Cast<UCheckBox>(_groupRoot->GetChildAt(i))); } return true; } bool UCheckBoxGroup::InitChild(UCheckBox* child) { if (nullptr == child) {// return false; } child->OnCheckStateChanged.Add(_onStateChangedScriptDelegate); return true; } void UCheckBoxGroup::OnStateChanged(bool bIsChecked) { TArray<UCheckBox*> childChangedArray = CheckChildsState(); if (0 != childChangedArray.Num()) { _onStateChangedEvent.Broadcast(childChangedArray); } } TArray<UCheckBox*> UCheckBoxGroup::CheckChildsState() { TArray<UCheckBox*> childChangedArray; if (nullptr == _groupRoot) { return childChangedArray; } int childCnt = _groupRoot->GetChildrenCount(); UCheckBox* childWidget = nullptr; UCheckBox* newLastCheckedChild = nullptr; UCheckBox* firstCheckedChild = nullptr; bool hasLastChecked = false; bool hasOtherChecked = false; for (int i = 0; i < childCnt; ++i) { childWidget = Cast<UCheckBox>(_groupRoot->GetChildAt(i)); if (nullptr != childWidget) { if (nullptr == firstCheckedChild) {//保存第一个 firstCheckedChild = childWidget; } if (childWidget->IsChecked()) { if (childWidget != _lastCheckedChild) { if (!hasOtherChecked) { newLastCheckedChild = childWidget; hasOtherChecked = true; } else {//uncheck childWidget,只保留一个选择项 childWidget->SetCheckedState(ECheckBoxState::Unchecked); } childChangedArray.Add(childWidget); } else { hasLastChecked = true; } } } } if (hasOtherChecked) {//uncheck _lastChild if (hasLastChecked && nullptr != _lastCheckedChild) { _lastCheckedChild->SetCheckedState(ECheckBoxState::Unchecked); childChangedArray.Add(_lastCheckedChild);//原有选择项被改变 } _lastCheckedChild = newLastCheckedChild; } else { if (!_allowSwitchOff) { if (nullptr != _lastCheckedChild) { if (!hasLastChecked) { _lastCheckedChild->SetCheckedState(ECheckBoxState::Checked);//还原,保留选择项 } } else {//选择第一个作为默认的选择项 if (nullptr != firstCheckedChild)//选择第一个 { _lastCheckedChild = firstCheckedChild; _lastCheckedChild->SetCheckedState(ECheckBoxState::Checked); childChangedArray.Add(_lastCheckedChild); } } } else { if (nullptr != _lastCheckedChild && 0 == childChangedArray.Num()) {//没有一个被选择且改变的情况,肯定是最后一个被uncheck了 childChangedArray.Add(_lastCheckedChild); } } } return childChangedArray; } /* bool UCheckBoxGroup::SetChildState(int nID, bool status) { if (nullptr == _groupRoot) { return false; } TArray<UCheckBox*> childChangedArray; int childCnt = _groupRoot->GetChildrenCount(); UCheckBox* childWidget = nullptr; UCheckBox* findCheckedChild = nullptr; //查找子物体 for (int i = 0; i < childCnt; ++i) { childWidget = Cast<UCheckBox>(_groupRoot->GetChildAt(i)); if (nullptr != childWidget && nID == childWidget->ID) { findCheckedChild = childWidget; break; } } if (nullptr == findCheckedChild) {//未找到该子物体 return false; } if (findCheckedChild->IsChecked() != status) {//判断值 findCheckedChild->SetCheckedState(status ? ECheckBoxState::Checked : ECheckBoxState::Unchecked); childChangedArray.Add(findCheckedChild); } if (status) { _lastCheckedChild = findCheckedChild; for (int i = 0; i < childCnt; ++i) { childWidget = Cast<UCheckBox>(_groupRoot->GetChildAt(i)); if (nullptr != childWidget) { if (childWidget != findCheckedChild && childWidget->IsChecked()) { childWidget->SetCheckedState(ECheckBoxState::Unchecked); childChangedArray.Add(childWidget); } } } } else { TArray<UCheckBox*> changedArray = CheckChildsState(); //移除合并时可能重复的项 int findIndex = childChangedArray.Find(findCheckedChild); if (INDEX_NONE != findIndex) { findIndex = changedArray.Find(findCheckedChild); if (INDEX_NONE != findIndex) { changedArray.RemoveAt(findIndex); } } //合并 childChangedArray.Append(changedArray); } if (0 != childChangedArray.Num()) {//广播改变 _onStateChangedEvent.Broadcast(childChangedArray); } return true; } */ bool UCheckBoxGroup::AddGroupChild(UCheckBox* child) { if (nullptr == _groupRoot || nullptr == child) { return false; } _groupRoot->AddChild(child); bool bInit = InitChild(child); if (!bInit) { _groupRoot->RemoveChild(child); return false; } return true; } bool UCheckBoxGroup::RemoveGroupChild(UCheckBox* child) { if (nullptr == _groupRoot || nullptr == child) { return false; } return _groupRoot->RemoveChild(child); }