16.列表框UIListBox类
列表框要实现的主要功能如下:
- 绘制背景;
- 依次绘制文字项数组中的文字;
- 鼠标移动到文字项上时绘制文字项的高亮背景;
- 鼠标点击文字项时绘制文字项的高亮背景并加以保留,直到选中的文字项发生改变;
- 如果文字项的总高度大于列表框高度,自动显示竖直滚动条;
- 如果文字项的宽度大于宽度,自动显示水平滚动条,这个功能还没有实现,它比较难,看一下xWinForms 3.0,你会发现他的方法是使用一个渲染目标将列表框中的内容进行截图,显示的是截图的图像。
列表框使用的背景图像和文字项高亮图像如下:
代码如下:
using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using StunEngine.Controllers; using StunEngine.SceneManagement; namespace StunEngine.SceneNodes.UI { ////// 列表框 /// public class UIListBox : UISceneNode { #region 成员变量和构造函数 //默认值 private static readonly Vector2 DefaultSize = new Vector2(160, 92); private static readonly Rectangle DefaultListboxRect = new Rectangle(1, 1, 6, 6); private static readonly float DefaultScrollBarWidth = 16.0f; private static readonly Rectangle DefaultSelectionRect = new Rectangle(0, 24, 128, 16); ////// 文字项数组 /// private Listitems = new List (); /// /// 列表框中可见的文字项数量,它是由列表框高度和文字项高度计算得出的 /// int visibleItemsCount; ////// 列表框图像矩形 /// Rectangle listboxRect; ////// 滚动条宽度,默认为16。高度无需指定,等于列表框高度。 /// private float scrollBarWidth; ////// 选择项的背景的目标矩形 /// Rectangle selectionDestinationRect; ////// 选择项的背景图像矩形 /// Rectangle selectionRect; ////// 鼠标所处的文字项索引,初始值为-1。 /// int hoverIndex = -1; ////// 选中的文字项的索引,初始值为-1,表示没有选中。 /// int selectedIndex = -1; ////// 选中的文字项的文字,默认为空字符。 /// string selectedItem = string.Empty; ////// 文字使用的字体 /// protected SpriteFont font; ////// 文字颜色 /// protected Color fontColor; ////// 竖直滚动条 /// UIVScrollBar VScrollBar; ////// 文字项选中时引发的事件 /// public event EventHandler Select = null; ////// 选中的文字项改变时引发的事件 /// public event EventHandler ChangeSelection = null; ////// 创建一个默认列表框,不包含文字项 /// /// 引擎 /// 所属场景 public UIListBox(StunXnaGE engine, Scene setScene) : this(engine, setScene, null) { } ////// 创建一个列表框,需要指定包含的文字项 /// /// 引擎 /// 所属场景 /// 文字项数组 public UIListBox(StunXnaGE engine, Scene setScene, string[] setItems) : this(engine, setScene, "Textures/UI/UIListbox", Vector2.Zero, DefaultSize, DefaultListboxRect, engine.DefaultFont, Color.White, setItems, DefaultScrollBarWidth, DefaultSelectionRect) { } ////// 创建一个列表框,内部方法,只能在引擎内部调用。 /// /// 引擎 /// 所属场景 /// 纹理文件名称 /// 2D屏幕位置 /// 列表框大小 /// 字体 /// 文字颜色 /// 文字项数组 /// 竖直滚动条宽度 /// 被选择的文字项背景图像矩形 internal UIListBox(StunXnaGE engine, Scene setScene, string setTextureName, Vector2 setPosition, Vector2 setSize, Rectangle setListboxRect, SpriteFont setFont, Color setTextColor, string[] setItems, float setScrollBarWidth, Rectangle setSelectionRect) : base(engine, setScene, setPosition, setTextureName) { this.size = setSize; this.listboxRect = setListboxRect; this.font = setFont; this.fontColor = setTextColor; this.selectionRect = setSelectionRect; this.scrollBarWidth = setScrollBarWidth; if (setItems != null) { for (int i = 0; i < setItems.Length; i++) this.items.Add(setItems[i]); } VScrollBar = new UIVScrollBar(engine, setScene); VScrollBar.TabStop = false; scene.AddNode(VScrollBar); //绘制列表框 Redraw(); } #endregion #region 属性 ////// 获取或设置列表框图像矩形 /// public Rectangle ListboxRect { get { return listboxRect; } set { listboxRect = value; } } ////// 获取或设置滚动条宽度,默认为16。高度无需指定,等于列表框高度。 /// public float ScrollBarWidth { get { return scrollBarWidth; } set { scrollBarWidth = value; Redraw(); } } ////// 获取或设置选中文字项的文字 /// public string SelectedItem { get { if (selectedIndex != -1) return items[selectedIndex]; else return "null"; } set { for (int i = 0; i < items.Count; i++) if (items[i] == value) { selectedIndex = i; break; } } } ////// 获取或设置文字项数组 /// public ListItems { get { return items; } set { items = value; } } /// /// 获取或设置文字使用的字体,字体改变时需要重新绘制列表框 /// public SpriteFont Font { get { return font; } set { font = value; Redraw(); } } ////// 获取或设置文字的颜色 /// public Color FontColor { get { return fontColor; } set { fontColor = value; } } ////// 获取选中的文字项的索引 /// public int SelectedIndex { get { return selectedIndex; } set { selectedIndex = value; } } ////// 索引器 /// /// ///public string this[int index] { get { if (index > items.Count) return "null"; return items[index]; } set { if (index > items.Count) return; items[index] = value; } } #endregion public override void Update(GameTime gameTime) { //如果鼠标在控件内部,则计算当前所处的文字项索引 if (isMouseInside) { hoverIndex = VScrollBar.Value + (int)Math.Floor((Input.MousePos.Y - position.Y) / font.LineSpacing); if (hoverIndex >= System.Math.Min(items.Count, VScrollBar.Value + visibleItemsCount) || hoverIndex < VScrollBar.Value) hoverIndex = -1; } //如果鼠标位于一个文字项之上并且点击鼠标左键 if (hoverIndex != -1 && Input.MouseLeftButtonPressed) { //存储上一帧选中的文字项索引 int previousIndex = selectedIndex; //将hoverIndex的值传递给selectedIndex selectedIndex = hoverIndex; //引发Select事件 if (Select != null) Select(items[selectedIndex], null); //如果改变了选中的文字项则引发ChangeSelection事件 if (selectedIndex != previousIndex && ChangeSelection != null) ChangeSelection(items[selectedIndex], null); } base.Update(gameTime); } public override void Draw(GameTime gameTime, bool useReflection) { //获取图像淡入淡出的透明颜色 alphaTextureColor = new Color(color, scene.TransitionAlpha); //绘制背景 StunXnaGE.SpriteBatch.Draw(material.Textures[0], new Rectangle((int)position.X, (int)(position.Y), (int)size.X, (int)size.Y), listboxRect, alphaTextureColor, rotation, origin, spriteEffect, layerDepth); //则绘制文字项的高亮背景 if (hoverIndex >= VScrollBar.Value && hoverIndex < System.Math.Min(items.Count, VScrollBar.Value + visibleItemsCount)) { selectionDestinationRect.Y = (int)(position.Y + font.LineSpacing * (hoverIndex - VScrollBar.Value)); StunXnaGE.SpriteBatch.Draw(material.Textures[0], selectionDestinationRect, selectionRect, alphaTextureColor, rotation, origin, spriteEffect, layerDepth); } //绘制选中的文字项的高亮背景 if (selectedIndex >= VScrollBar.Value && selectedIndex < System.Math.Min(items.Count, VScrollBar.Value + visibleItemsCount)) { selectionDestinationRect.Y = (int)(position.Y + font.LineSpacing * (selectedIndex - VScrollBar.Value)); StunXnaGE.SpriteBatch.Draw(material.Textures[0], selectionDestinationRect, selectionRect, alphaTextureColor, rotation, origin, spriteEffect, layerDepth); } //绘制文字项 if (items.Count > 0) { for (int i = VScrollBar.Value; i < System.Math.Min(items.Count, VScrollBar.Value + visibleItemsCount); i++) { Vector2 textOffset = Vector2.Zero; textOffset.X = position.X; textOffset.Y = position.Y + (i - VScrollBar.Value) * font.LineSpacing; StunXnaGE.SpriteBatch.DrawString(font, items[i], textOffset, fontColor); } } } #region 处理文字项数组的公有方法 /// /// 在文字项数组中添加一个数据项 /// /// 文字项 public void AddItem(string item) { //在文字项数组中添加给定文字 items.Add(item); Redraw(); } ////// 根据给定文字项文字移除一个文字项 /// /// 给定的文字项文字 public void Remove(string item) { //如果文字项数组中不包含给定的文字则返回 if (!items.Contains(item)) return; items.Remove(item); Redraw(); } ////// 根据索引移除一个文字项 /// /// 文字项索引 public void RemoveAt(int index) { if (index < 0 || index >= items.Count) return; items.RemoveAt(index); Redraw(); } ////// 清除文字项数组 /// public void Clear() { items.Clear(); Redraw(); } #endregion #region 事件相关处理程序 ////// 重新绘制列表框 /// private void Redraw() { //根据列表框高度计算列表框可见的文字项数量 visibleItemsCount = (int)System.Math.Ceiling(size.Y / font.LineSpacing); //重新设置列表框大小 this.size = new Vector2(size.X, visibleItemsCount * font.LineSpacing); //重新设置竖直滚动条的相关属性 VScrollBar.MaxStep = items.Count - visibleItemsCount; VScrollBar.Size = new Vector2(scrollBarWidth, size.Y); VScrollBar.Position = new Vector2(position.X + size.X - scrollBarWidth, position.Y); VScrollBar.LargeChange = visibleItemsCount; //重新设置高亮图像的目标矩形 this.selectionDestinationRect = new Rectangle((int)position.X, (int)position.Y, VScrollBar.IsVisible ? (int)(size.X - VScrollBar.Size.X) : (int)size.X, font.LineSpacing); } ////// 列表框位置变化时需要重新绘制 /// protected override void OnLocationChanged() { Redraw(); base.OnLocationChanged(); } ////// 列表框大小变化时需要重新绘制 /// protected override void OnSizeChanged() { Redraw(); base.OnSizeChanged(); } protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); VScrollBar.IsVisible = isVisible; } #endregion #region 单元测试 #if DEBUG ////// 测试UIListbox类 /// public static void TestUIListbox() { UIListBox listbox = null; UILabel lblMessage1 = null; UILabel lblMessage2 = null; TestGame.Start("测试listbox类,可按空格键,下右键,数字键123", delegate { listbox = new UIListBox(TestGame.engine, TestGame.scene); TestGame.scene.AddNode(listbox); listbox.AddItem("First"); listbox.AddItem("Second"); listbox.AddItem("Third"); listbox.AddItem("Four"); listbox.AddItem("Five"); listbox.Position = new Vector2(100, 100); lblMessage1 = new UILabel(TestGame.engine, TestGame.scene); lblMessage1.Position = new Vector2(280, 100); TestGame.scene.AddNode(lblMessage1); lblMessage2 = new UILabel(TestGame.engine, TestGame.scene); lblMessage2.Position = new Vector2(280, 150); TestGame.scene.AddNode(lblMessage2); //关闭相机控制器 TestGame.scene.fpsCamCtrl.Enabled = false; }, delegate { lblMessage1.Text = "SelectItem:" + listbox.hoverIndex.ToString(); lblMessage2.Text = "SelectIndex:" + listbox.SelectedIndex.ToString(); if (Input.KeyboardSpaceJustPressed) listbox.IsVisible = !listbox.IsVisible; if (Input.KeyboardDownJustPressed) listbox.Position += new Vector2(5, 5); if (Input.KeyboardRightJustPressed) listbox.Size += new Vector2(5, 5); if (Input.KeyboardKeyJustPressed(Keys.D1)) listbox.AddItem("NewItem"); if (Input.KeyboardKeyJustPressed(Keys.D2)) listbox.Remove("NewItem"); if (Input.KeyboardKeyJustPressed(Keys.D3)) listbox.RemoveAt(listbox.items.Count - 1); }, null); } #endif #endregion } }
单元测试截图如下:
发布时间:2010/2/23 下午2:54:48 阅读次数:6324