65.9K
CodeProject 正在变化。 阅读更多。
Home

Android 风格(Toast)通知 for .NET

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (19投票s)

2012年8月20日

公共领域

5分钟阅读

viewsIcon

44410

downloadIcon

5340

.NET Framework 的 Android 风格通知组件。

介绍  

在使用我运行 Android 3.2 Honeycomb 的 Galaxy Tab 时,我喜欢出现在屏幕底部的小通知/状态对话框。 由于我目前正在 .NET 上尝试创建自定义用户绘制控件,因此我尝试这样做,以便在将来的软件开发中可以使用它。   

一个示例 Android 状态对话框,称为 Toast Notification: 

 

幕后

为了更好地理解正在发生的事情,请遵循本文档中包含的源代码。

代码写在静态类或 module 中,因此我们无需创建它的实例,这与 .NET 的 MessageBox 类非常相似。

这是我们的 NotificationManager 模块的类图。 

  

 

对象名称  描述 
alphaval  保存通知窗口透明度值的变量。(0 - 1) 
displaycontrol  我们将要绘制内容的控件。 
GlowColor  通知窗口边框的颜色。 
incr  alpha 值增量。淡入具有正值,淡出具有负值。 
isVisible  一个决定通知当前是否可见/显示的开关。 
msg  保存要显示的的消息的变量。 
prnt  通知窗口的父对象/控件。 
textSize  消息的测量大小。 
tmrAnimation  一个用于淡入/淡出我们通知窗口的计时器对象。 
tmrDelay  一个用于在指定延迟后隐藏通知的计时器对象。 

 

 

公共方法  描述 
<code>Show(<code>ByRef Parent As Control, ByVal Message As String, ByVal glw As Color, ByVal delay As Integer) 

这会显示通知对话框。位于指定父控件的中心底部。建议您将 Form 对象设置为此组件的父级。 

 

控件  (displaycontrol

我创建的自定义对话框显示在一个容器控件内 - 这意味着它不是窗口或对话框这样的顶级控件。它将包含显示给用户的内容。 

检查普通 WinForms 控件的属性,将其 BackColor 属性值设置为 Transparent 会使其与父控件的 BackColorBackgroundImage 融合,但不会使下方的控件可见。 要解决此问题,除了使 BackColor 透明外,我们还应该绘制下方的所有控件,使其看起来真正透明。 有关此逻辑如何工作的更多信息,请参阅此 CodeProject 文章。 

在附加的源代码中,请参阅 ExtendedControl 类。此类继承了 .NET Framework 的 Control 类。 

ExtendedControl PaintBackground 事件覆盖以使其透明

    'Override the default paint background event.
    'Append here the true transparency effect.
    Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaintBackground(e)
        If (IsTransparent) Then
            If Not (Parent Is Nothing) Then
                Dim myIndex As Integer = Parent.Controls.GetChildIndex(Me)
                For i As Integer = Parent.Controls.Count - 1 To myIndex + 1 Step -1
                    Dim ctrl As Control = Parent.Controls(i)
                    If (ctrl.Bounds.IntersectsWith(Bounds)) Then
                        If (ctrl.Visible) Then
                            Dim bmp As Bitmap = New Bitmap(ctrl.Width, ctrl.Height)
                            ctrl.DrawToBitmap(bmp, ctrl.ClientRectangle)
                            e.Graphics.TranslateTransform(ctrl.Left - Left, ctrl.Top - Top)
                            e.Graphics.DrawImage(bmp, Point.Empty)
                            e.Graphics.TranslateTransform(Left - ctrl.Left, Top - ctrl.Top)
                            bmp.Dispose()
                        End If
                    End If
                Next
            End If
        End If
    End Sub 

定位对话框控件  

对话框控件的位置将位于父控件底部区域的中心。对话框控件的宽度不应超出父控件的可见区域。控件的大小也相对于使用 Graphics.MeasureString 测量的字符串/消息的大小。 

下面的代码将对话框控件居中,并调整其高度和宽度

'Set the control dimensions.
textSize = displaycontrol.CreateGraphics().MeasureString(Message, Parent.Font)
displaycontrol.Height = 25 + textSize.Height
displaycontrol.Width = 35 + textSize.Width
If (textSize.Width > Parent.Width - 100) Then
     displaycontrol.Width = Parent.Width - 100
     Dim hf As Integer = CInt(textSize.Width) / (Parent.Width - 100)
     displaycontrol.Height += (textSize.Height * hf)
End If    
'Position control in parent
displaycontrol.Left = (Parent.Width - displaycontrol.Width) / 2
displaycontrol.Top = (Parent.Height - displaycontrol.Height) - 50 

 

绘制控件 

要生成内容,我们需要处理将显示消息的控件(即 displaycontrol 对象)的 Paint 事件。 在这一部分,我们将绘制发光效果、消息本身,然后修改其透明度。

首先,我们需要在 Bitmap 对象中绘制组件。我们还应该从我们刚刚创建的图像对象中创建一个 Graphics。 

'This BITMAP object will hold the appearance of the notification dialog.
'Why paint in bitmap? because we will set its opacity and paint it on the control later with a specified alpha.
Dim img As New Bitmap(displaycontrol.Width, displaycontrol.Height)
Dim e As Graphics = Graphics.FromImage(img)
'Set smoothing. 
e.SmoothingMode = SmoothingMode.AntiAlias  

 

之后,准备绘图所需的​​对象。 

'Prepare drawing tools.
Dim bru As Brush = New SolidBrush(Color.FromArgb(50, GlowColor))
Dim pn As Pen = New Pen(bru, 6)
Dim gp As New GraphicsPath()
'Make connecting edges rounded.
pn.LineJoin = LineJoin.Round 

我们将使用 GraphicsPathPen 来绘制发光边框和文本区域。将 PenLineJoin 设置为 LineJoin.Round,这将使边缘看起来圆润。 

在这一部分,渲染边框,应用发光效果

'Draw borders
'Outmost, 50 alpha
gp.AddRectangle(New Rectangle(3, 3, displaycontrol.Width - 10, displaycontrol.Height - 10))
e.DrawPath(pn, gp)
'level 3, A bit solid
gp.Reset()
gp.AddRectangle(New Rectangle(5, 5, displaycontrol.Width - 14, displaycontrol.Height - 14))
e.DrawPath(pn, gp)
'level 2, a bit more solid
gp.Reset()
gp.AddRectangle(New Rectangle(7, 7, displaycontrol.Width - 18, displaycontrol.Height - 18))
e.DrawPath(pn, gp)
'level 1, more solidness
gp.Reset()
gp.AddRectangle(New Rectangle(9, 9, displaycontrol.Width - 22, displaycontrol.Height - 22))
e.DrawPath(pn, gp) 

如果您注意到我们在创建 graphics 对象时,使用 alpha 值为 50 的选定 Color 声明了 bru。 当我们渲染边框时,我们叠加了几个 alpha 值为 50 的矩形以产生“发光”效果。 我建议您阅读这篇文章以了解类似的逻辑。 

接下来,我们在通知窗口的内部创建黑色区域,我们将在其中写入文本。 

'Draw Content Rectangle.
gp.Reset()
bru = New SolidBrush(Color.FromArgb(7, 7, 7))
pn = New Pen(bru, 5)
pn.LineJoin = LineJoin.Round
gp.AddRectangle(New Rectangle(8, 8, displaycontrol.Width - 20, displaycontrol.Height - 20))
e.DrawPath(pn, gp)
e.FillRectangle(bru, New Rectangle(9, 9, displaycontrol.Width - 21, displaycontrol.Height - 21)) 

我们需要先用画笔绘制矩形以获得圆角,然后填充空的部分。 

我们将所有这些都渲染到一个 Bitmap 对象中。接下来,为了实现透明效果,我们需要创建一个 ImageAttributes 并设置其 ColorMatrix,然后再将渲染的 bitmap 绘制到控件本身上。 

'Set COLORMATRIX (RGBAw).
'Matrix [3,3] will be the Alpha. Alpha is in float, 0(transparent) - 1(opaque).
Dim cma As New ColorMatrix()
cma.Matrix33 = alphaval
Dim imga As New ImageAttributes()
imga.SetColorMatrix(cma)

我们可以在 ColorMatrix 的 [3,3] 位置设置图像的 alpha 值(0 - 1)。 有关如何在 .NET 中实现 Alpha 混合的更多想法,这可能会对您有所帮助。 

绘制消息。 

'Draw the notification message..
Dim sf As New StringFormat()
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Center
e.DrawString(msg, prnt.Font, New SolidBrush(Color.FromArgb(247, 247, 247)), New Rectangle(9, 9, displaycontrol.Width - 21, displaycontrol.Height - 21), sf) 

最后,将图像绘制到 displaycontrol 上。 

'Now, draw the content on the control.
pe.Graphics.DrawImage(img, New Rectangle(0, 0, displaycontrol.Width, displaycontrol.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imga)

并且不要忘记处理我们使用的对象。 

'Free the memory.
cma = Nothing
sf.Dispose()
imga.Dispose()
e.Dispose()
img.Dispose()
bru.Dispose()
pn.Dispose()
gp.Dispose()

 

动画 - 淡入/淡出  (tmrAnimation) 

对于淡入/淡出或透明度效果,我们使用 Timer 对象。对于每个滴答,Timer 会将 alphaval(如果您之前注意到,ColorMatrix 矩阵 3,3 等于 alphaval)变量与 incr 的值进行递增/递减,然后刷新 displaycontrol 以重新绘制它。 

对于淡入,一旦通知窗口完全不透明,它就会触发 tmrDelay 来延迟隐藏通知。然后当 tmrDelay 滴答时,它会禁用自身然后再次运行 tmrAnimation 以进行淡出动画。 

两个计时器的事件

'Handles the window animation.
Private Sub tmr_tick(ByVal sender As Object, ByVal e As EventArgs) Handles tmrAnimation.Tick
        If (incr > 0) Then
            If (alphaval < 1) Then
                If (alphaval + incr <= 1) Then
                    alphaval += incr
                    displaycontrol.Refresh()
                Else
                    alphaval = 1
                    displaycontrol.Refresh()
                    tmrAnimation.Enabled = False
                    tmrDelay.Enabled = True
                End If
            End If
        Else
            If (alphaval > 0) Then
                If (alphaval + incr >= 0) Then
                    alphaval += incr
                    displaycontrol.Refresh()
                Else
                    alphaval = 0
                    tmrAnimation.Enabled = False
                    tmrAnimation.Dispose()
                    tmrDelay.Dispose()
                    displaycontrol.Dispose()
                    incr = 0.1
                    isVisible = False
                End If
            End If
        End If
End Sub 
'handles the delay.
Private Sub tmrDelay_tick(ByVal sender As Object, ByVal e As EventArgs) Handles tmrDelay.Tick
        incr = -0.1
        tmrAnimation.Enabled = True
        tmrDelay.Enabled = False
End Sub  

使用代码     

由于该组件是一个模块,只需调用模块名称和函数即可: 

NotificationManager.Show(<parent name>, <string/prompt>, <Color value>, <delay in milliseconds>)  

更多示例截图: 

 

关注点

创建自己的控件很有趣,特别是如果您需要让您的应用程序看起来很漂亮。 .NET 框架在创建用户绘制的组件方面功能强大,但在进行动画处理方面却不那么出色。 

源代码的使用 

您可以在您的项目中​​使用包含的代码(如果您这样做,请添加一些署名)。但是我不能保证它们没有错误。 

历史

[2012/8/20] - 这是本文的第一个版本。
[2012/8/20] - 编辑了下载链接并编辑了一些小地方。
[2024/9/1] - 将许可证更新为公共领域。

© . All rights reserved.