[UE4]UMG CheckBox Group实现

学习UE4的时候,发现UMG居然没有CheckBoxGroup,CheckBox分组需要自己管理。相比于Unity3D来说,UE4在UI方面确实欠缺很多。无奈之下只好自己实现一个。

实现效果如下:
这里写图片描述

编辑界面如下:
这里写图片描述

代码实现如下:

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);
}

这里写图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值