任务栏右下角弹出气球式消息

 

在任务栏右下角弹出气球式消息,指定时间后消失:

void CTrayDemoDlg::ShowBalloon(CString szText)

{

CTBalloon * pballoon = new CTBalloon(150,100);

pballoon->SetText(szText);//文字

pballoon->SetLifeTime(4); //停留时间(秒)

pballoon->CreateAndShow();

}

 

TBalloon.h

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
TBalloon.h

#if !defined(AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_)
#define AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// /
// class CTBalloon

/// //
// CTBalloon window
#include " Gradient.h "

class CTBalloon : public CWnd
{
// Construction
public :
CTBalloon(UINT nWidth, UINT nHeight);
BOOL CreateAndShow();
void SetText(CString str);
void SetLifeTime(UINT secs);


static UINT m_sActiveCount;

// Attributes
private :
CRect m_current_rect;
CRect m_screen_rect;
UINT m_nWidth;
UINT m_nHeight;
UINT m_totaltime;
UINT m_lifetime;
BOOL m_dir;
CString m_text;
CGradient m_grad;

// Overrides
// ClassWizard generated virtual function overrides
// {{AFX_VIRTUAL(CTBalloon)
public :
protected :
virtual void PostNcDestroy();
// }}AFX_VIRTUAL

// Implementation
public :
virtual ~ CTBalloon();

// Generated message map functions
protected :
// {{AFX_MSG(CTBalloon)
afx_msg void OnPaint();
afx_msg
void OnTimer(UINT nIDEvent);
// }}AFX_MSG
DECLARE_MESSAGE_MAP()
};

/// //

// {{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_)

 

TBalloon.cpp 

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
TBalloon.cpp

// /
// class CTBalloon


#include
" stdafx.h "
// #include "TrayBalloon.h"
#include " TBalloon.h "
#include
" Gradient.h "

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/// //
// CTBalloon

#define BALLOON_SHOW_TIMER 0x10
#define TIMER_MS 20
#define BALLOON_LIFETIME (3*1000)
#define STEP_SIZE 5

UINT CTBalloon::m_sActiveCount;


CTBalloon::CTBalloon(UINT nWidth, UINT nHeight): m_nWidth(nWidth), m_nHeight(nHeight)
{
SystemParametersInfo(SPI_GETWORKAREA,
0 , & m_screen_rect, 0 );
m_totaltime
= 0 ;
m_lifetime
= BALLOON_LIFETIME;
m_dir
= TRUE;
}

CTBalloon::
~ CTBalloon()
{
}


BEGIN_MESSAGE_MAP(CTBalloon, CWnd)
// {{AFX_MSG_MAP(CTBalloon)
ON_WM_PAINT()
ON_WM_TIMER()
// }}AFX_MSG_MAP
END_MESSAGE_MAP()

void CTBalloon::SetText(CString str)
{
m_text
= str;
}


void CTBalloon::SetLifeTime(UINT secs)
{
m_lifetime
= secs * 1000 ;
}

BOOL CTBalloon::CreateAndShow()
{

const wchar_t * p = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW );

CRect rect;
rect.top
= m_screen_rect.bottom - m_nHeight;
rect.bottom
= m_screen_rect.bottom;
rect.left
= m_screen_rect.right - m_nWidth - 10 - (m_sActiveCount * 10 ) ;
rect.right
= m_screen_rect.right - 10 - (m_sActiveCount * 10 );

DWORD dwStyle
= WS_POPUP;
DWORD dwExStyle
= WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_TOPMOST | WS_EX_WINDOWEDGE ;


BOOL ret
= CreateEx(dwExStyle,p,NULL,dwStyle,rect,NULL,NULL);

m_sActiveCount
++ ;

m_current_rect.top
= m_current_rect.bottom = m_screen_rect.bottom;
m_current_rect.left
= rect.left;
m_current_rect.right
= rect.right;


COLORREF lightblue
= RGB( 154 , 190 , 255 );
COLORREF white
= RGB( 255 , 255 , 255 );


m_grad.SetDirection(CGradient::RTL);
m_grad.SetGradientColorsX(
3 ,lightblue,lightblue,white);


SetTimer(BALLOON_SHOW_TIMER,TIMER_MS,NULL);
return ret;
}

/// //
// CTBalloon message handlers



void CTBalloon::OnPaint()
{
CPaintDC dc(
this ); // device context for painting

CBrush br(
0xFF0000 );
CRect rect(
0 , 0 ,m_current_rect.Width(),m_current_rect.Height());
m_grad.DrawLinearGradient(
& dc,rect);

dc.SelectStockObject(ANSI_VAR_FONT);
dc.SetBkMode(TRANSPARENT);
CRect temp
= rect;
UINT height
= dc.DrawText(m_text, - 1 ,temp,DT_CENTER | DT_WORDBREAK | DT_CALCRECT);

rect.top
= (rect.Height() - height) / 2 ;
dc.DrawText(m_text,
- 1 ,rect,DT_CENTER | DT_WORDBREAK);

// Do not call CWnd::OnPaint() for painting messages
}


void CTBalloon::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default

m_totaltime
+= TIMER_MS;

if (m_totaltime > m_lifetime)
{
m_dir
= FALSE;
}

if (m_dir)
{
m_current_rect.top
= ((m_current_rect.top - STEP_SIZE) < (m_current_rect.bottom - (LONG)m_nHeight)) ? m_current_rect.bottom - m_nHeight : m_current_rect.top - STEP_SIZE;
}
else
{
m_current_rect.top
= ((m_current_rect.top + STEP_SIZE) > (m_current_rect.bottom)) ? m_current_rect.bottom : m_current_rect.top + STEP_SIZE;
}


MoveWindow(
& m_current_rect);

if (m_current_rect.top == m_current_rect.bottom)
{
KillTimer(BALLOON_SHOW_TIMER);
DestroyWindow();
return ;
}

CWnd
* pWnd = GetFocus();
ShowWindow(SW_RESTORE);
if (pWnd)
pWnd
-> SetFocus();


CWnd::OnTimer(nIDEvent);
}

void CTBalloon::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class

delete
this ;
m_sActiveCount
-- ;

}

 

 Gradient.h

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
Gradient.h

#if !defined(AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_)
#define AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// /
// class CGradient
//

#include
< afxtempl.h >

#ifndef UIBITS_API
#ifdef UIBITS_DLL
#define UIBITS_API __declspec(dllexport)
#else
#define UIBITS_API __declspec(dllimport)
#endif
#endif

struct CGradElement
{
CGradElement(COLORREF color
= 0 , int length = 0 ){ this -> color = color; this -> length = length;}
COLORREF color;
int length;
};

typedef CArray
< CGradElement, CGradElement &> CGradArray;

class /* UIBITS_API */ CGradient
{
// Construction
public :
CGradient();
virtual ~ CGradient();

// Attributes
public :
// linear gradient attributes
enum eDirection{LTR, RTL, TTB, BTT};
void SetDirection(eDirection fDirection){ if (m_fDirection != fDirection){m_fDirection = fDirection;}}
eDirection GetDirection() {
return m_fDirection;}
void SetStretchGradient( float flStretchFactor = 1 ); // useful for animation
float GetStretchGradient() { return m_flStretchGrad;}
// linear gradient operations
virtual void DrawLinearGradient(CDC * pDC, CRect rcGrad, int nClipStart = 0 , int nClipEnd = - 1 , int nShift = 0 );

// attributes
void SetGradientColors(COLORREF clrStart, COLORREF clrEnd) { SetGradientColorsX( 2 , clrStart, clrEnd);}
void GetGradientColors(COLORREF & clrStart, COLORREF & clrEnd);

void SetGradientColorsX( int nCount, COLORREF clrFirst, COLORREF clrNext, ...);
const CDWordArray & GetGradientColorsX() { return m_ardwGradColors; }
void AddColor(COLORREF clr);
void SetColorsStretch( double flFirst, ...); // in percent, num of arguments should be one less then num of colors

void SetCreatePalette(BOOL fCreate = TRUE) {m_fCreatePalette = fCreate;}
BOOL GetCreatePalette() {
return m_fCreatePalette;}
CPalette
& GetPalette() {CreatePalette(); return m_Pal;}

// operations
virtual void CalcShiftedGradient(CGradArray & arElements, int nShift, int nGradWidth, int nClipStart = 0 , int nClipEnd = - 1 , UINT nMaxColors = (UINT) - 1 );
virtual void CalcMultiGradient(CGradArray & arElements, int nGradWidth, int nClipStart = 0 , int nClipEnd = - 1 , UINT nMaxColors = (UINT) - 1 );
virtual void CalcGradient(CGradArray & arElements, COLORREF clrStart, COLORREF clrEnd, int nGradWidth, int nClipStart = 0 , int nClipEnd = - 1 , UINT nMaxColors = (UINT) - 1 );
protected :
void CreatePalette();
void NormalizeColorsStretch();

// color atributes
CDWordArray m_ardwGradColors;
BOOL m_fCreatePalette;
CPalette m_Pal;
private :
CArray
< double , double &> m_arflGradStretch;
protected :
// linear gradient
eDirection m_fDirection;
CRect ConvertToReal(CRect rcDraw,
int nBandStart, int nBandEnd);
float m_flStretchGrad;
};

#endif // !defined(AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_)

 

Gradient.cpp

 

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
Gradient.cpp

// /
// class CGradient
//

#include
" stdafx.h "
#include
" Gradient.h "

/ /
// Construction/Destruction
/ /

CGradient::CGradient()
{
m_fCreatePalette
= FALSE;
m_fDirection
= LTR;
m_flStretchGrad
= 1 ;
}

CGradient::
~ CGradient()
{

}

void CGradient::DrawLinearGradient(CDC * pDC, CRect rcGrad, int nClipStart, int nClipEnd, int nShift)
{
BOOL f256Color
= pDC -> GetDeviceCaps(RASTERCAPS) & RC_PALETTE;
if ( ! f256Color && (pDC -> GetDeviceCaps(BITSPIXEL) * pDC -> GetDeviceCaps(PLANES) < 8 ))
{
// for 16 colors no gradient
ASSERT(m_ardwGradColors.GetSize() > 0 );
pDC
-> FillSolidRect( & ConvertToReal(rcGrad, nClipStart, nClipEnd), m_ardwGradColors[ 0 ]);
return ;
}

int nGradWidth = 0 ;
if (m_fDirection == TTB || m_fDirection == BTT)
nGradWidth
= rcGrad.Height(); // vert
else
nGradWidth
= rcGrad.Width(); // horz

if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
ASSERT(nGradWidth
>= nClipEnd);

ASSERT(m_flStretchGrad
>= 1 );
nGradWidth
= int (nGradWidth * m_flStretchGrad);

CGradArray arElements;
CalcShiftedGradient(arElements, nShift, nGradWidth, nClipStart, nClipEnd, f256Color
? 236 : - 1 );
int nSteps = arElements.GetSize();

int nBandStart = nClipStart;
int nBandEnd = nClipStart;

// Start filling
CPalette * pOldPal = NULL;
if (f256Color && m_fCreatePalette && GetPalette().GetSafeHandle())
{
pOldPal
= pDC -> SelectPalette( & GetPalette(), FALSE);
pDC
-> RealizePalette();
}

for ( int i = 0 ; i < nSteps; i ++ , nBandStart = nBandEnd)
{
nBandEnd
+= arElements[i].length;

COLORREF nColor
= arElements[i].color;
if (f256Color)
{
if (pOldPal) // in background draw dithered
nColor |= 0x02000000 ; // (PALETTERGB) without it will be dithering

CBrush br(nColor);
// CDC::FillSolidRect is faster, but it does not handle 8-bit color depth
pDC -> FillRect( & ConvertToReal(rcGrad, nBandStart, nBandEnd), & br);
br.DeleteObject();
}
else
pDC
-> FillSolidRect( & ConvertToReal(rcGrad, nBandStart, nBandEnd), nColor);
}
if (pOldPal)
pDC
-> SelectPalette(pOldPal, TRUE);
}

CRect CGradient::ConvertToReal(CRect rcDraw,
int nBandStart, int nBandEnd)
{
BOOL fReverse
= (m_fDirection == TTB || m_fDirection == RTL);
BOOL fVert
= (m_fDirection == TTB || m_fDirection == BTT);

CRect rc(rcDraw);
if (fVert)
{
if (nBandEnd == - 1 ) nBandEnd = rcDraw.Height();
rc.top
= rcDraw.top +
(fReverse
? nBandStart : (rcDraw.Height() - nBandEnd));
rc.bottom
= rc.top + (nBandEnd - nBandStart);
}
else
{
if (nBandEnd == - 1 ) nBandEnd = rcDraw.Width();
rc.left
= rcDraw.left +
(fReverse
? (rcDraw.Width() - nBandEnd) : nBandStart);
rc.right
= rc.left + (nBandEnd - nBandStart);
}
return rc;
}

void CGradient::CalcShiftedGradient(CGradArray & arElements, int nShift, int nGradWidth,
int nClipStart, int nClipEnd, UINT nMaxColors)
{
// normalize shift
if (nGradWidth == 0 )
nShift
= 0 ;
else
{
while (nShift < 0 ) nShift += nGradWidth;
nShift
%= nGradWidth;
}

if (nShift == 0 || m_ardwGradColors.GetSize() < 2 )
{
CalcMultiGradient(arElements, nGradWidth, nClipStart, nClipEnd, nMaxColors);
return ;
}

CGradArray arElementsOrig;
CalcMultiGradient(arElementsOrig, nGradWidth,
0 , - 1 , nMaxColors);
int nCount = arElementsOrig.GetSize();
if (nCount < 2 )
{
// no gradient
arElements.Add(CGradElement(m_ardwGradColors[ 0 ], nClipEnd - nClipStart));
return ;
}

// do shift
CGradArray arElementsShift;
int nLength = 0 ;
for ( int i = nCount - 1 ; i >= 0 ; i -- )
{
CGradElement
& el = arElementsOrig.ElementAt(i);
if (nLength + el.length > nShift)
{
// Separate
arElementsShift.InsertAt( 0 , CGradElement(el.color, nShift - nLength));
el.length
-= nShift - nLength;
break ;
}
else
{
nLength
+= el.length;
arElementsShift.InsertAt(
0 , el);
arElementsOrig.RemoveAt(i);
if (nLength == nShift)
break ;
}
}
// combine shifted
arElementsShift.Append(arElementsOrig);

// do clip
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
nCount
= arElementsShift.GetSize();
nLength
= 0 ;
for ( int i = 0 ; i < nCount; i ++ )
{
CGradElement
& el = arElementsShift.ElementAt(i);
if (nLength + el.length <= nClipStart)
{
nLength
+= el.length;
continue ; // skip before clip start
}
int nStart = max(nLength, nClipStart);
nLength
+= el.length;
int nEnd = min(nLength, nClipEnd);
arElements.Add(CGradElement(el.color, nEnd
- nStart));
if (nLength >= nClipEnd)
break ; // skip after clip end
}
}

void CGradient::CalcMultiGradient(CGradArray & arElements, int nGradWidth,
int nClipStart, int nClipEnd, UINT nMaxColors)
{
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
int nSteps = m_ardwGradColors.GetSize() - 1 ;
if (nSteps < 0 )
{
ASSERT(
0 ); // at least 1 color should be added
return ;
}
if (nSteps == 0 )
{
arElements.Add(CGradElement(m_ardwGradColors[
0 ], nClipEnd - nClipStart));
return ;
}

double flBandGradStart = 0 ;

nMaxColors
/= nSteps;
for ( int i = 0 ; i < nSteps; i ++ )
{
int nBandGradStart = int (( double )nGradWidth * flBandGradStart / 100 );
flBandGradStart
+= m_arflGradStretch[i];
int nBandGradEnd = int (( double )nGradWidth * flBandGradStart / 100 );

if (i == nSteps - 1 ) // last step (because of problems with float)
nBandGradEnd = nGradWidth;

if (nBandGradEnd < nClipStart)
continue ; // skip - band before cliping rect

int nBandClipStart = nBandGradStart;
int nBandClipEnd = nBandGradEnd;
if (nBandClipStart < nClipStart)
nBandClipStart
= nClipStart;
if (nBandClipEnd > nClipEnd)
nBandClipEnd
= nClipEnd;

CalcGradient(arElements, m_ardwGradColors[i], m_ardwGradColors[i
+ 1 ],
nBandGradEnd
- nBandGradStart,
nBandClipStart
- nBandGradStart,
nBandClipEnd
- nBandGradStart,
nMaxColors);

if (nBandClipEnd == nClipEnd)
break ; // stop filling - next band is out of clipping rect
}
}

void CGradient::CalcGradient(CGradArray & arElements, COLORREF clrStart, COLORREF clrEnd,
int nGradWidth, int nClipStart, int nClipEnd,
UINT nMaxColors)
{
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
// Split colors to RGB chanels, find chanel with maximum difference
// between the start and end colors. This distance will determine
// number of steps of gradient
int r = (GetRValue(clrEnd) - GetRValue(clrStart));
int g = (GetGValue(clrEnd) - GetGValue(clrStart));
int b = (GetBValue(clrEnd) - GetBValue(clrStart));
UINT nSteps
= max(abs(r), max(abs(g), abs(b)));
nSteps
= min(nSteps, nMaxColors);
// if number of pixels in gradient less than number of steps -
// use it as numberof steps
UINT nPixels = nGradWidth;
nSteps
= min(nPixels, nSteps);
if (nSteps == 0 ) nSteps = 1 ;

float rStep = ( float )r / nSteps;
float gStep = ( float )g / nSteps;
float bStep = ( float )b / nSteps;

r
= GetRValue(clrStart);
g
= GetGValue(clrStart);
b
= GetBValue(clrStart);

float nWidthPerStep = ( float )nGradWidth / nSteps;
CBrush br;
// Start filling
for (UINT i = 0 ; i < nSteps; i ++ )
{
int nFillStart = ( int )(nWidthPerStep * i);
int nFillEnd = ( int )(nWidthPerStep * (i + 1 ));
if (i == nSteps - 1 ) // last step (because of problems with float)
nFillEnd = nGradWidth;

if (nFillEnd < nClipStart)
continue ; // skip - band before cliping rect

// clip it
if (nFillStart < nClipStart)
nFillStart
= nClipStart;
if (nFillEnd > nClipEnd)
nFillEnd
= nClipEnd;

COLORREF clrFill
= RGB(r + ( int )(i * rStep),
g
+ ( int )(i * gStep),
b
+ ( int )(i * bStep));
// add band
arElements.Add(CGradElement(clrFill, nFillEnd - nFillStart));

if (nFillEnd >= nClipEnd)
break ; // stop filling if we reach current position
}
}

void CGradient::SetGradientColorsX( int nCount, COLORREF clrFirst, COLORREF clrNext, ...)
{
ASSERT(nCount
> 1 );
m_ardwGradColors.SetSize(nCount);

m_ardwGradColors.SetAt(
0 , clrFirst);
m_ardwGradColors.SetAt(
1 , clrNext);

if (nCount > 2 )
{
va_list pArgs;
va_start(pArgs, clrNext);
for ( int i = 2 ; i < nCount; i ++ )
m_ardwGradColors.SetAt(i, va_arg(pArgs, COLORREF));
va_end( pArgs );
}

nCount
-- ;
m_arflGradStretch.SetSize(nCount);
for ( int i = 0 ; i < nCount; i ++ )
m_arflGradStretch[i]
= ( double ) 100 / nCount;

// remove all dependent objects
m_Pal.DeleteObject();
}

void CGradient::GetGradientColors(COLORREF & clrStart, COLORREF & clrEnd)
{
if (m_ardwGradColors.GetSize() > 0 )
{
clrStart
= m_ardwGradColors[ 0 ];
clrEnd
= m_ardwGradColors[m_ardwGradColors.GetSize() > 1 ? 1 : 0 ];
}
else
clrStart
= clrEnd = CLR_NONE;
}

void CGradient::AddColor(COLORREF clr)
{
m_ardwGradColors.Add(clr);
if (m_ardwGradColors.GetSize() > 1 )
{
double flNew = ( double ) 100 / m_arflGradStretch.GetSize();
m_arflGradStretch.Add(flNew);
NormalizeColorsStretch();
}
}

void CGradient::CreatePalette()
{
if ( ! m_fCreatePalette || m_Pal.GetSafeHandle())
return ;

int nNumColors = 236 ;
CGradArray arElements;
CalcMultiGradient(arElements, nNumColors,
0 , nNumColors, nNumColors);
ASSERT(nNumColors
>= arElements.GetSize());
nNumColors
= arElements.GetSize();
if ( ! nNumColors)
return ;

LPLOGPALETTE lpPal
= (LPLOGPALETTE) new BYTE[ sizeof (LOGPALETTE) +
sizeof (PALETTEENTRY) *
nNumColors];

if ( ! lpPal)
return ;

lpPal
-> palVersion = 0x300 ;
lpPal
-> palNumEntries = nNumColors;

for ( int i = 0 ; i < nNumColors; i ++ )
{
COLORREF nColor
= arElements[i].color;

lpPal
-> palPalEntry[i].peRed = GetRValue(nColor);
lpPal
-> palPalEntry[i].peGreen = GetGValue(nColor);
lpPal
-> palPalEntry[i].peBlue = GetBValue(nColor);
lpPal
-> palPalEntry[i].peFlags = 0 ;
}

m_Pal.CreatePalette(lpPal);

delete [](PBYTE)lpPal;
}

void CGradient::SetStretchGradient( float flStretchFactor)
{
ASSERT(flStretchFactor
>= 1 );
if (flStretchFactor < 1 )
flStretchFactor
= 1 ;
m_flStretchGrad
= flStretchFactor;
}

void CGradient::NormalizeColorsStretch()
{
int nCount = m_arflGradStretch.GetSize();
ASSERT(nCount
== m_ardwGradColors.GetSize() - 1 );
double flFull = 0 ;
for ( int i = 0 ; i < nCount; i ++ )
flFull
+= m_arflGradStretch[i];

for ( int i = 0 ; i < nCount; i ++ )
m_arflGradStretch[i]
= m_arflGradStretch[i] * 100 / flFull;
}

void CGradient::SetColorsStretch( double flFirst, ...)
{
int nCount = m_ardwGradColors.GetSize() - 1 ;
if (nCount < 1 )
{
ASSERT(
0 ); // before you use this function add all necessary colors
return ;
}

m_arflGradStretch.SetSize(nCount);
m_arflGradStretch.SetAt(
0 , flFirst);
va_list pArgs;
va_start(pArgs, flFirst);
for ( int i = 1 ; i < nCount; i ++ )
m_arflGradStretch.SetAt(i, va_arg(pArgs,
double ));
va_end( pArgs );

NormalizeColorsStretch();
// normalize
}

 

 

 

转载于:https://www.cnblogs.com/followmyheart/articles/1783005.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值