Android 风格(Toast)通知 for .NET






4.60/5 (19投票s)
.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) | 这会显示通知对话框。位于指定父控件的中心底部。建议您将 |
控件 (displaycontrol
)
我创建的自定义对话框显示在一个容器控件内 - 这意味着它不是窗口或对话框这样的顶级控件。它将包含显示给用户的内容。
检查普通 WinForms 控件的属性,将其 BackColor
属性值设置为 Transparent
会使其与父控件的 BackColor
和 BackgroundImage
融合,但不会使下方的控件可见。 要解决此问题,除了使 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
我们将使用 GraphicsPath
和 Pen
来绘制发光边框和文本区域。将 Pen
的 LineJoin
设置为 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] - 将许可证更新为公共领域。