duilib自绘制圆形进度条

圆形进度条在duilib框架中是非基础控件,我们需要基于Progress控件进行自绘制,核心代码包括UICircleProgress.h和UICircleProgress.cpp两个文件,其代码如下:

UICircleProgress.h

#ifndef __UICIRCLE_PROGRESS_H__
#define __UICIRCLE_PROGRESS_H__

#pragma once

namespace DuiLib
{
    class CCircleProgressUI :
        public CProgressUI
    {
    public:
        CCircleProgressUI(void);
        ~CCircleProgressUI(void);

        LPCTSTR GetClass() const;
        LPVOID GetInterface(LPCTSTR pstrName);

        void SetCircular(BOOL bCircular = TRUE);
        void SetClockwiseRotation(BOOL bClockwise = TRUE);
        void SetCircleWidth(DWORD dwCircleWidth);
        void SetBgColor(DWORD dwBgColor);
        DWORD GetBgColor() const;
        void SetFgColor(DWORD dwBgColor);
        DWORD GetFgColor() const;
        void SetIndicator(LPCTSTR lpIndicatorImage);
        void SetEnableCircleEffect(BOOL bEnableCircleEffect = FALSE);
        void SetCircleGradientColor1(DWORD dwColor);
        void SetCircleGradientColor2(DWORD dwColor);
        void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);
        void PaintBkColor(HDC hDC);

    private:
        BOOL        m_bCircular;
        BOOL        m_bClockwise;
        DWORD       m_dwCircleWidth;
        DWORD       m_dwBgColor;
        DWORD       m_dwFgColor;
        BOOL        m_bEnableCircleEffect;
        DWORD       m_dwGradientColor1;
        DWORD       m_dwGradientColor2;
        Image*      m_pIndicator;
        CDuiString     m_sIndicatorImage;
    };

}       //namespace Duilib

#endif // !CIRCLE_PROGRESS_H

UICircleProgress.cpp

#include "stdafx.h"
#include "UICircleProgress.h"

namespace DuiLib
{
    extern int GetEncoderClsid(const WCHAR* format, CLSID *pClsid);
    extern Color ARGB2Color(DWORD dwColor);

    CCircleProgressUI::CCircleProgressUI(void)
        : m_bCircular(FALSE)
        , m_bClockwise(TRUE)
        , m_dwCircleWidth(10.0)
        , m_dwFgColor()
        , m_dwBgColor()
        , m_bEnableCircleEffect(FALSE)
        , m_dwGradientColor1()
        , m_dwGradientColor2()
        , m_sIndicatorImage("")
        , m_pIndicator(NULL)
    {

    }

    CCircleProgressUI::~CCircleProgressUI(void)
    {
        if (m_pIndicator)
        {
            delete m_pIndicator;
        }
    }

    void CCircleProgressUI::SetCircular(BOOL bCircular /* = TRUE */)
    {
        m_bCircular = bCircular;
        Invalidate();
    }

    void CCircleProgressUI::SetClockwiseRotation(BOOL bClockwise)
    {
        if (bClockwise != m_bClockwise)
        {
            m_bClockwise = bClockwise;
            if (m_pIndicator)
            {
                //已经旋转了图片,旋转到相反的方向
                m_pIndicator->RotateFlip(Rotate180FlipNone);
            }

        }
    }

    void CCircleProgressUI::SetCircleWidth(DWORD dwCircleWidth)
    {
        m_dwCircleWidth = dwCircleWidth;
        Invalidate();
    }

    void CCircleProgressUI::SetBgColor(DWORD dwBgColor)
    {
        m_dwBgColor = dwBgColor;
        Invalidate();
    }

    DWORD CCircleProgressUI::GetBgColor() const
    {
        return m_dwBgColor;
    }

    void CCircleProgressUI::SetFgColor(DWORD dwFgColor)
    {
        m_dwFgColor = dwFgColor;
        Invalidate();
    }

    DWORD CCircleProgressUI::GetFgColor() const
    {
        return m_dwFgColor;
    }

    void CCircleProgressUI::SetIndicator(LPCTSTR lpIndicatorImage)
    {
        ASSERT(lpIndicatorImage);
        if (m_sIndicatorImage != lpIndicatorImage)
        {
            m_sIndicatorImage = lpIndicatorImage;
            const TImageInfo* imgInfo = GetManager()->GetImageEx(m_sIndicatorImage);
            BITMAP bmp;
            GetObject(imgInfo->hBitmap, sizeof(BITMAP), &bmp);

            m_pIndicator = new Bitmap(imgInfo->nX, imgInfo->nY, imgInfo->nX * 4, PixelFormat32bppARGB, (BYTE*)bmp.bmBits);
            Status state = m_pIndicator->GetLastStatus();
            if (Ok == state)
            {
                // 假定图片指向上
                m_pIndicator->RotateFlip(m_bClockwise ? Rotate90FlipNone : Rotate270FlipNone);
                Invalidate();
            }
        }
    }

    void CCircleProgressUI::SetEnableCircleEffect(BOOL bEnableCircleEffect)
    {
        m_bEnableCircleEffect = bEnableCircleEffect;
    }

    void CCircleProgressUI::SetCircleGradientColor1(DWORD dwColor)
    {
        m_dwGradientColor1 = dwColor;
        Invalidate();
    }

    void CCircleProgressUI::SetCircleGradientColor2(DWORD dwColor)
    {
        m_dwGradientColor2 = dwColor;
        Invalidate();
    }

    void CCircleProgressUI::PaintBkColor(HDC hDC)
    {
        CProgressUI::PaintBkColor(hDC);
        if (m_bCircular)
        {
            if (m_nMax <= m_nMin) m_nMax = m_nMin + 1;
            if (m_nValue > m_nMax) m_nValue = m_nMax;
            if (m_nValue < m_nMin) m_nValue = m_nMin;

            int direction = m_bClockwise ? 1 : -1;
            Gdiplus::REAL bordersize = 1.0;
            Graphics graphics(hDC);
            graphics.SetSmoothingMode(SmoothingModeAntiAlias);

            // 圆形中心
            PointF center;
            center.X = m_rcItem.left + (m_rcItem.right - m_rcItem.left) / 2;
            center.Y = m_rcItem.top + (m_rcItem.bottom - m_rcItem.top) / 2;

            // 控件矩形内的最大正方形的边界
            int side = min(m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top);
            RectF rcBorder;
            rcBorder.X = center.X - side / 2;
            rcBorder.Y = center.Y - side / 2;
            rcBorder.Width = side;
            rcBorder.Height = side;

            // 进度弧形的边界
            Gdiplus::RectF outer = rcBorder;
            if (m_pIndicator)
            {
                outer.Inflate(-1.0F * m_pIndicator->GetWidth() / 2, -1.0F * m_pIndicator->GetWidth() / 2);
            }
            else
            {
                outer.Inflate(-0.5 * m_dwCircleWidth, -0.5 * m_dwCircleWidth);
            }

            outer.Inflate(-1.0F, -1.0F);

            Pen borderPen(Color::White, bordersize);
            Pen bgPen(m_dwBgColor, m_dwCircleWidth);

            //graphics.DrawEllipse(&borderPen, outer);      

            RectF rcArk;
            rcArk.X = outer.X + outer.Width / 2 - m_dwCircleWidth / 2;
            rcArk.Y = outer.Y - m_dwCircleWidth / 2;
            rcArk.Width = rcArk.Height = m_dwCircleWidth;

            if (!m_bEnableCircleEffect)
            {
                Pen fgPen(m_dwFgColor, m_dwCircleWidth);
                graphics.DrawArc(&bgPen, outer, 270, 360);
                graphics.DrawArc(&fgPen, outer, 270, direction * 360 * (m_nValue - m_nMin) / (m_nMax - m_nMin));
            }
            else
            {
                REAL factors[4] = { 0.0f, 0.9f, 0.0f };
                REAL position[4] = { 0.0f, 0.5f, 1.0f };

                LinearGradientBrush lgbrush(rcBorder, ARGB2Color(m_dwGradientColor1), ARGB2Color(m_dwGradientColor2), LinearGradientModeVertical);
                lgbrush.SetBlend(factors, position, 3);
                //graphics.FillRectangle(&lgbrush, rcBorder); 
                graphics.DrawArc(&bgPen, outer, 270, 360);

                Pen fgPen(&lgbrush, m_dwCircleWidth);
                graphics.DrawArc(&fgPen, outer, 270, direction * 360 * (m_nValue - m_nMin) / (m_nMax - m_nMin));
            }

            Matrix matrix;
            matrix.RotateAt(direction * 360 * (m_nValue - m_nMin) / (m_nMax - m_nMin), center, MatrixOrderAppend);
            graphics.SetTransform(&matrix);

            if (m_pIndicator)
            {
                RectF rectf;
                rectf.X = center.X - m_pIndicator->GetWidth() / 2;
                rectf.Y = outer.Y + bordersize / 2 - m_pIndicator->GetHeight() / 2;
                rectf.Width = m_pIndicator->GetWidth();
                rectf.Height = m_pIndicator->GetHeight();
                graphics.DrawImage(m_pIndicator, rectf);
            }

        }
    }

    void CCircleProgressUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
    {
        if (_tcscmp(pstrName, _T("circular")) == 0) {
            SetCircular(_tcscmp(pstrValue, _T("true")) == 0);
        }
        else if (_tcscmp(pstrName, _T("clockwise")) == 0) {
            SetClockwiseRotation(_tcscmp(pstrValue, _T("true")) == 0);
        }
        else if (_tcscmp(pstrName, _T("circlewidth")) == 0) SetCircleWidth(_ttoi(pstrValue));
        else if (_tcscmp(pstrName, _T("bgcolor")) == 0) {
            if (*pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);
            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetBgColor(clrColor);
        }
        else if (_tcscmp(pstrName, _T("fgcolor")) == 0) {
            if (*pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);
            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetFgColor(clrColor);
        }
        else if (_tcscmp(pstrName, _T("indicator")) == 0)
        {
            SetIndicator(pstrValue);
        }
        else if (_tcscmp(pstrName, _T("enablecircleeffect")) == 0) {
            SetEnableCircleEffect(_tcscmp(pstrValue, _T("true")) == 0);
        }
        else if (_tcscmp(pstrName, _T("gradientcolor1")) == 0) {
            if (*pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);
            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetCircleGradientColor1(clrColor);
        }
        else if (_tcscmp(pstrName, _T("gradientcolor2")) == 0) {
            if (*pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);
            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetCircleGradientColor2(clrColor);
        }
        else CProgressUI::SetAttribute(pstrName, pstrValue);
    }

    LPCTSTR CCircleProgressUI::GetClass() const
    {
        return DUI_CTR_CIRCLE_PROGRES;
    }
    LPVOID CCircleProgressUI::GetInterface(LPCTSTR pstrName)
    {
        if (_tcscmp(pstrName, DUI_CTR_CIRCLE_PROGRES) == 0) return static_cast<CCircleProgressUI*>(this);
        return CProgressUI::GetInterface(pstrName);
    }

}       // namespace Duilib

除了增加以上两个部分外,还需要增加其他配套的代码:

在r\DuiLib\UIlib.h中增加头文件包含:

#include "Control/UIProgress.h"
//新增加代码
#include "Control/UICircleProgress.h"
#include "Control/UISlider.h"

在Core\UIDefine.h中增加宏定义:

#define  DUI_CTR_CIRCLE_PROGRES       (_T("CircleProgress"))

在DuiLib\Core\UIDlgBuilder.cpp文件中增加判断分支:

CControlUI* CDialogBuilder::_Parse(CMarkupNode* pRoot, CControlUI* pParent, CPaintManagerUI* pManager)
{
    case 11:
        if (_tcsicmp(pstrClass, DUI_CTR_CHILDLAYOUT) == 0) pControl = new CChildLayoutUI;
        break;
    case 14:
        if( _tcsicmp(pstrClass, DUI_CTR_VERTICALLAYOUT) == 0 )     pControl = new CVerticalLayoutUI;
        else if( _tcsicmp(pstrClass, DUI_CTR_LISTHEADERITEM) == 0 )  pControl = new CListHeaderItemUI;
        //新增加代码
        else if (_tcsicmp(pstrClass, DUI_CTR_CIRCLE_PROGRES) == 0)   pControl = new CCircleProgressUI;

    break;
}

通过以上步骤就可以在xml文件中进行控件布局了,示例布局代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<Window mininfo="200,260" size="260,320">
    <Font shared="true" id="0" name="幼圆" size="12" default="true" />
    <Font shared="true" id="1" name="微软雅黑" size="18" />
    <VerticalLayout >
        <CircleProgress name="progress1" circular="true" clockwise="true" enablecircleeffect="true" 
        gradientcolor1="#ff90EE90" gradientcolor2="#ff90EE90" 
        float="true" pos="10,10,0,0" height="125" width="125" circlewidth="20" 
        min="1" max="100" value="75" bgcolor="#ffA9A9A9" fgcolor="#ff90EE90" 
        valign="vcenter" textcolor="0xFFFFFFFF" font="2"  text="在线:70![这里写图片描述](https://img-blog.csdn.net/20180527213749757?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW8zNDA0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)%"/> 

    </VerticalLayout>
</Window>

运行效果如下:
这里写图片描述

参考资料:

https://blog.csdn.net/fighton/article/details/72681889

猜你喜欢

转载自blog.csdn.net/xiao3404/article/details/80472937