14.滑动条UISlider类
滑动条可以用来方便地在一定范围中改变一个值的大小,在Winform中叫做TrackBar。好像在NeoForce Controls(http://www.tomshane.cz/neoforce/default.aspx)中并没有实现滑动条,所以本控件使用的图片来自于DirectSDK:
我只使用了下方的滑动条轨道和滑块的图像。
滑动条需要实现的主要功能是:
- 绘制滑动条轨道和滑块;
- 根据鼠标位置拖动滑块并改变value值,value值并不能平滑改变,它是根据变量tickFrequency跳跃变化的。
水平滑动条和竖直滑动条大部分行为是类似的,所以将它们的共性封装成UISliderBase基类,将逻辑包含在基类中,而界面代码写在继承的UIHSlider类和UIVSlider类中,这样以后也可以方便地实现斜向的滑动条或仪表盘等。注意:Winform中的TrackBar是通过Orientation属性改变水平还是竖直模式的,如果你曾下载过2010年1月6日发布的StunEngine0.3版本,会发现我就是这样做的,只编写了一个UISlider类,设置SliderMode枚举决定是水平滑动条还是竖直滑动条,但后来发现这样做虽然代码量较小,但可读性和扩展性较差,所以进行了重构。
下面是基类UISliderBase的代码:
namespace StunEngine.SceneNodes.UI
...{
/**//// <summary>
/// 滑动条抽象基类,水平滑动条和竖直滑动条从这个类继承。
/// </summary>
public abstract class UISliderBase:UISceneNode
...{
成员变量和构造函数#region 成员变量和构造函数
//默认值
protected static readonly Vector2 DefaultSliderRailSize = new Vector2(150, 4);
protected static readonly Vector2 DefaultSliderButtonSize = new Vector2(24, 24);
protected static readonly Rectangle DefaultSliderRailRect = new Rectangle(1, 205, 91, 6);
protected static readonly Rectangle DefaultSliderButtonRect = new Rectangle(152, 194, 40, 40);
/**//// <summary>
/// 滑块位置
/// </summary>
protected Vector2 sliderButtonPosition;
/**//// <summary>
/// 滑块大小,默认大小24*24
/// </summary>
protected Vector2 sliderButtonSize;
/**//// <summary>
/// 滑块图像初始矩形
/// </summary>
protected Rectangle sliderButtonRectOrigin;
/**//// <summary>
/// 滑块图像高亮状态矩形
/// </summary>
protected Rectangle sliderButtonRectHighlight;
/**//// <summary>
/// 滑块图像点击状态矩形
/// </summary>
protected Rectangle sliderButtonRectClicked;
/**//// <summary>
/// 滑动条轨道位置
/// </summary>
protected Vector2 sliderRailPosition;
/**//// <summary>
/// 滑动条轨道大小,默认大小150*4
/// </summary>
protected Vector2 sliderRailSize;
/**//// <summary>
/// 滑动条轨道图像矩形
/// </summary>
protected Rectangle sliderRailRect;
/**//// <summary>
/// 滑动条的最小值,默认为0
/// </summary>
protected float minValue = 0;
/**//// <summary>
/// 滑动条的最大值,默认为1
/// </summary>
protected float maxValue = 1.0f;
/**//// <summary>
/// 步进频率,默认为10,即从最小值到最大值需要移动10次
/// </summary>
protected int tickFrequency = 10;
/**//// <summary>
/// 滑动条的值,默认为0
/// </summary>
protected float value = 0;
/**//// <summary>
/// 是否处在滑动状态
/// </summary>
protected bool isSliding = false;
/**//// <summary>
/// 创建一个滑动条基类
/// </summary>
/// <param name="engine">引擎</param>
/// <param name="setScene">所属场景</param>
/// <param name="setPosition">2D屏幕位置</param>
/// <param name="setTextureName">使用的纹理名称</param>
/// <param name="setSliderRailSize">轨道大小</param>
/// <param name="setSliderRailRect">轨道图像矩形</param>
/// <param name="setSliderButtonSize">滑块大小</param>
/// <param name="setSliderButtonRectOrigin">滑块普通状态矩形</param>
/// <param name="setSliderButtonRectHighlight">滑块高亮状态矩形</param>
/// <param name="setSliderButtonRectClicked">滑块点击状态矩形</param>
internal UISliderBase(StunXnaGE engine, Scene setScene, Vector2 setPosition, string setTextureName, Vector2 setSliderRailSize, Rectangle setSliderRailRect, Vector2 setSliderButtonSize, Rectangle setSliderButtonRectOrigin, Rectangle setSliderButtonRectHighlight, Rectangle setSliderButtonRectClicked)
: base(engine, setScene, setPosition, setTextureName)
...{
this.sliderRailSize = setSliderRailSize;
this.sliderRailRect = setSliderRailRect;
this.sliderButtonSize = setSliderButtonSize;
this.sliderButtonRectOrigin = setSliderButtonRectOrigin;
this.sliderButtonRectHighlight = setSliderButtonRectHighlight;
this.sliderButtonRectClicked = setSliderButtonRectClicked;
//设置滑动条的大小
ResetSize();
}
#endregion
属性#region 属性
/**//// <summary>
/// 获取或设置滑块大小,滑块大小发生变化时需要重新计算滑动条的大小
/// </summary>
public Vector2 SliderButtonSize
...{
get ...{ return sliderButtonSize; }
set
...{
sliderButtonSize = value;
ResetSize();
}
}
/**//// <summary>
/// 获取或设置滑块图像初始矩形
/// </summary>
public Rectangle SlideButtonRectOrigin
...{
get ...{ return sliderButtonRectOrigin; }
set ...{ sliderButtonRectOrigin = value; }
}
/**//// <summary>
/// 获取或设置滑块图像高亮状态矩形
/// </summary>
public Rectangle SlideButtonRectHighlight
...{
get ...{ return sliderButtonRectHighlight; }
set ...{ sliderButtonRectHighlight = value; }
}
/**//// <summary>
/// 获取或设置滑块图像点击状态矩形
/// </summary>
public Rectangle SlideButtonRectClicked
...{
get ...{ return sliderButtonRectClicked; }
set ...{ sliderButtonRectClicked = value; }
}
/**//// <summary>
/// 获取或设置滑动条轨道大小,滑动条轨道大小发生变化时需要重新计算滑动条的大小
/// </summary>
public Vector2 SliderRailSize
...{
get ...{ return sliderRailSize; }
set
...{
sliderRailSize = value;
ResetSize();
}
}
/**//// <summary>
/// 获取或设置滑动条轨道图像矩形
/// </summary>
public Rectangle SlidreRailRect
...{
get ...{ return sliderRailRect; }
set ...{ sliderRailRect = value; }
}
/**//// <summary>
/// 获取或设置步进频率,默认为10,即从最小值到最大值需要移动10次
/// </summary>
public int TickFrequency
...{
get ...{ return tickFrequency; }
set
...{
tickFrequency = value;
ResetSliderButtonPosition();
}
}
/**//// <summary>
/// 获取或设置滑动条的最小值,minValue的值改变时需要重新设置滑块的位置
/// </summary>
public float MinValue
...{
get ...{ return minValue; }
set
...{
if (value > maxValue)
return;
minValue = value;
ResetSliderButtonPosition();
}
}
/**//// <summary>
/// 获取或设置滑动条的最大值,maxValue的值改变时需要重新设置滑块的位置
/// </summary>
public float MaxValue
...{
get ...{ return maxValue; }
set
...{
if (value < minValue)
return;
maxValue = value;
ResetSliderButtonPosition();
}
}
/**//// <summary>
/// 获取或设置滑动条的值,滑动条的值发生改变时引发ValueChanged事件。
/// </summary>
public float Value
...{
get ...{ return value; }
set
...{
this.value = value;
OnValueChanged();
}
}
#endregion
事件相关处理程序#region 事件相关处理程序
/**//// <summary>
/// 滑动条的值变化时引发的事件
/// </summary>
public event EventHandler ValueChanged;
/**//// <summary>
/// ValueChanged事件的处理程序,需要重新调整滑块的位置。
/// OnValueChanged方法允许继承的类处理这个事件。
/// 注意:当在继承类中覆写OnValueChanged时,一定要调用基类的OnValueChanged方法才可以注册委托
/// </summary>
protected virtual void OnValueChanged()
...{
if (ValueChanged != null)
ValueChanged(this.value, null);
if (value > maxValue)
this.value = maxValue;
if (value < minValue)
this.value = minValue;
ResetSliderButtonPosition();
}
/**//// <summary>
/// 滑动条位置变化时的处理程序,需要重新调整滑块的位置
/// </summary>
protected override void OnLocationChanged()
...{
base.OnLocationChanged();
ResetSliderButtonPosition();
}
/**//// <summary>
/// 重新设置滑动条大小
/// </summary>
protected virtual void ResetSize()
...{ }
/**//// <summary>
/// 重置滑块的位置
/// </summary>
protected virtual void ResetSliderButtonPosition()
...{ }
#endregion
}
}
而水平滑动条从基类继承,代码如下:
namespace StunEngine.SceneNodes.UI
...{
/**//// <summary>
/// 水平滑动条
/// </summary>
public class UIHSlider:UISliderBase
...{
/**//// <summary>
/// 创建一个默认水平滑动条
/// </summary>
/// <param name="engine">引擎</param>
/// <param name="setScene">所属场景</param>
public UIHSlider(StunXnaGE engine, Scene setScene)
: base(engine, setScene, Vector2.Zero, "Textures/UI/UIDxutcontrols", DefaultSliderRailSize, DefaultSliderRailRect, DefaultSliderButtonSize, DefaultSliderButtonRect, DefaultSliderButtonRect, DefaultSliderButtonRect)
...{
//设置水平滑动条滑块的位置
this.sliderButtonPosition = new Vector2(- sliderButtonSize.X / 2, 0);
}
public override void Update(GameTime gameTime)
...{
if (isVisible)
...{
//如果鼠标在滑动条上且左键按下
if (isMouseInside && Input.MouseLeftButtonPressed)
...{
isSliding = true;
float value = 0;
//计算滑动条的值
value = minValue +((int)(System.Math.Round((Input.MousePos.X - position.X) * tickFrequency / sliderRailSize.X))*(maxValue - minValue)/tickFrequency);
//不让value值超出范围
if (value < minValue )
value = minValue ;
else if (value > maxValue)
value = maxValue;
//如果值变化则重新设置新值并引发ValueChanged事件
if (this.value != value )
...{
this.value = value;
OnValueChanged();
}
}
else
isSliding = false;
}
base.Update(gameTime);
}
/**//// <summary>
/// 绘制滑动条
/// </summary>
public override void Draw(GameTime gameTime,bool useReflection)
...{
if (isVisible)
...{
// 获取图像淡入淡出的透明颜色
alphaTextureColor = new Color(color, scene.TransitionAlpha);
//绘制滑动条轨道轨道
StunXnaGE.SpriteBatch.Draw(material.Textures[0], new Rectangle((int)position.X, (int)(position.Y + size.Y / 2 - sliderRailSize.Y / 2), (int)sliderRailSize.X, (int)sliderRailSize.Y), sliderRailRect, alphaTextureColor, rotation, origin, spriteEffect, layerDepth);
//根据鼠标移入还是按下设置滑块图像源矩阵
Rectangle buttonRect = isMouseInside || (uiManager.ActiveControl == this) ? ((Input.MouseLeftButtonPressed && isMouseInside) ? sliderButtonRectClicked : sliderButtonRectHighlight) : sliderButtonRectOrigin;
//绘制滑块
StunXnaGE.SpriteBatch.Draw(material.Textures[0], new Rectangle((int)sliderButtonPosition.X, (int)position.Y, (int)sliderButtonSize.X, (int)sliderButtonSize.Y), buttonRect, alphaTextureColor, rotation, origin, spriteEffect, layerDepth);
}
}
事件相关处理程序#region 事件相关处理程序
/**//// <summary>
/// 重置水平轨道上滑块的位置
/// </summary>
protected override void ResetSliderButtonPosition()
...{
//根据value值重置按钮的位置
sliderButtonPosition.X = (this.value - minValue) * (sliderRailSize.X) / (maxValue - minValue) + position.X - sliderButtonSize.X / 2;
//不让按钮超出滑动条边界
if (sliderButtonPosition.X < position.X - sliderButtonSize.X / 2)
sliderButtonPosition.X = position.X - sliderButtonSize.X / 2;
else if (sliderButtonPosition.X > position.X + sliderRailSize.X - sliderButtonSize.X / 2)
sliderButtonPosition.X = position.X + sliderRailSize.X - sliderButtonSize.X / 2;
}
/**//// <summary>
/// 设置水平滑动条大小,水平滑动条的长为滑动条轨道的长,高为滑块的高
/// </summary>
protected override void ResetSize()
...{
this.size = new Vector2(sliderRailSize.X, sliderButtonSize.Y);
}
#endregion
单元测试#region 单元测试
#if DEBUG
/**//// <summary>
/// 测试滑动条
/// </summary>
public static void TestUISlider()
...{
UIHSlider sliderH = null;
UIVSlider sliderV = null;
UILabel lblValueH = null;
UILabel lblValueV = null;
TestGame.Start("测试滑动条,可按上下左右键和数字键1-8",
delegate
...{
sliderH = new UIHSlider(TestGame.engine, TestGame.scene);
sliderH.Position = new Vector2(100, 100);
sliderH.TickFrequency = 2;
TestGame.scene.AddNode(sliderH);
sliderV = new UIVSlider(TestGame.engine, TestGame.scene);
sliderV.Position = new Vector2(100, 200);
TestGame.scene.AddNode(sliderV);
lblValueH = new UILabel(TestGame.engine, TestGame.scene);
lblValueH.Position = new Vector2(280, 100);
TestGame.scene.AddNode(lblValueH);
lblValueV = new UILabel(TestGame.engine, TestGame.scene);
lblValueV.Position = new Vector2(100, 150);
TestGame.scene.AddNode(lblValueV);
//关闭相机控制器
TestGame.scene.fpsCamCtrl.Enabled = false;
},
delegate
...{
//显示水平滑动条的值
lblValueH.Text = sliderH.Value .ToString();
//显示竖直滑动条的值
lblValueV.Text = sliderV.Value.ToString();
//按Up键移动水平滑动条位置
if (Input.KeyboardUpJustPressed)
sliderH.Position += new Vector2(5, 5);
//按Down键移动竖直滑动条的位置
if(Input .KeyboardDownJustPressed )
sliderV.Position +=new Vector2 (5,5);
//按Left键改变水平滑动条按钮的大小
if (Input.KeyboardLeftJustPressed)
sliderH.SliderButtonSize += new Vector2(5, 5);
//按Right键改变水平滑动条轨道的大小
if (Input.KeyboardRightJustPressed)
sliderH.SliderRailSize += new Vector2(5, 5);
//测试改变水平滑动条Value,MinValue,MaxValue值时的行为
if (Input.KeyboardKeyJustPressed(Keys.D1))
sliderH.Value = 0.6f;
if (Input.KeyboardKeyJustPressed(Keys.D2))
sliderH.Value = 0.7f;
if (Input.KeyboardKeyJustPressed(Keys.D3))
sliderH.MinValue =0.4f;
if (Input.KeyboardKeyJustPressed(Keys.D4))
sliderH.MaxValue = 1.4f;
//测试改变竖直滑动条Value,MinValue,MaxValue值时的行为
if (Input.KeyboardKeyJustPressed(Keys.D5))
sliderV.Value = 0.6f;
if (Input.KeyboardKeyJustPressed(Keys.D6))
sliderV.Value = 0.9f;
if (Input.KeyboardKeyJustPressed(Keys.D7))
sliderV.MinValue = 0.4f;
if (Input.KeyboardKeyJustPressed(Keys.D8))
sliderV.MaxValue = 1.4f;
});
}
#endif
#endregion
}
}
竖直滑动条的代码与水平滑动条的代码类似,不再赘述,最主要的不同是在绘制轨道和滑块时需要将它们的Rotation属性从0变为MathHelper.PiOver2,表示顺时针旋转90度,这样做可以重用轨道和滑块的图像,无需再建立一个竖直轨道和竖直滑块的图像。
单元测试截图如下:
发布时间:2010/1/25 上午8:39:43 阅读次数:9176
2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823。
站长邮箱