【问题标题】:Odd behaviour on track bar mouse move event轨迹栏鼠标移动事件的奇怪行为
【发布时间】:2014-10-28 14:53:16
【问题描述】:

我正在尝试实现与大多数媒体播放器类似的功能,通过将鼠标移动到媒体持续时间轨道栏上方,它会显示一个小弹出窗口,通知您鼠标当前位于上方的时间。在实现下面给出的代码时,我注意到了一个奇怪的行为。

procedure TForm1.TrackBar1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Single);
var
  pers: Extended;
begin
  pers := (X/TrackBar1.Width);
  PixelLabel.Text := FloatToStr(pers * TrackBar1.Max);
end;

如果我点击轨迹栏的中间,我会得到一个非常接近该点实际轨迹栏值的值,例如,如果轨迹栏范围从 0 到 2000 并单击中间某处得到 1000,但当我向左或向右移动时,我开始分别获得更小和更大的值。因此,例如,如果我的鼠标接近起点,我可能会得到 180 而不是 100 ,那应该是当时的实际值。有人可以指出我在这里做错了什么吗?

编辑

我所说的实际轨迹栏值是指来自以下的值:

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  ActualLabel.Text := 'Actual Val: '+FloatToStr(TrackBar1.Value);

end;

所以我将鼠标移动到位置 308(此处轨迹栏从 0 到 609),我将得到一个 perch 值 0.50574,这告诉我鼠标下方位置 308 处的轨迹栏为 10114,但通过单击鼠标并启动 onChange 函数,我得到的值为 10116。当我们从轨迹栏的中间到其中任何一个时,这种差异会增加侧面。

编辑 2

一个更清楚的例子就是这个。如下图所示,我将鼠标移动到 X=572 的位置。如果以整个轨迹栏的百分比表示,该位置将为 572/609 = 0,9392。因此,人们会期望该位置的轨迹栏值的百分比(如图所示,最小值:0 - 最大值:200)是相同的。换句话说,MValue/max = 0,9392。

但是在单击该确切位置的轨迹栏然后请求其值后,它不会返回我计算为“MValue”的值,如下图所示(鼠标不可见,但此图像确实是在我单击轨迹之后条在同一位置,您可以看到 ActualValue 已更新)

【问题讨论】:

  • 尝试使用TrackBar1.ClientWidth 而不是Width,尽管我认为这不会有太大的不同。这个想法是实际轨道比控件的宽度小几个像素。
  • ClientWidth 不是轨迹栏的属性,如果您的意思是绝对宽度,我确实尝试过,但它完全相同。
  • 我刚刚注意到您使用的是 Firemonkey,但概念是一样的,实际的轨迹栏本身小于控件的宽度。您必须确定实际轨道的宽度,并且可能没有直接的属性可以告诉您这些信息。
  • VCL 也存在同样的问题。只需要到较低的级别才能获得这条轨道的宽度,这超出了我的知识范围。
  • 您有没有想过使用其他组件。例如,您可以改用 TGauge,这样您就可以得到类似于 BS Player 或 Cyber​​link Power DVD 的结果。 TGauge 的主要优点是,由于它的左右两侧没有任何多余的空间,因此您可以像在原始代码中那样轻松地使用 Width 来计算位置。

标签: delphi firemonkey


【解决方案1】:

问题 1

您计算的值与控件使用的值不匹配。该控件使用此代码执行此操作,该代码可在 FMX.StdCtrls 中找到:

function PosToValue(MinValue, MaxValue, ViewportSize, ThumbSize, TrackSize, 
  Pos: Single; IgnoreViewportSize: boolean): Single;
var ValRel: Double;
begin
  Result := MinValue;
  if (ViewportSize < 0) or IgnoreViewportSize then
    ViewportSize := 0;
  ValRel := TrackSize - ThumbSize;
  if ValRel > 0 then
  begin
    ValRel := (Pos - ThumbSize / 2) / ValRel;
    if ValRel < 0 then
      ValRel := 0;
    if ValRel > 1 then
      ValRel := 1;
    Result := MinValue + ValRel * (MaxValue - MinValue - ViewportSize);
  end;
end;

不同之处在于此处的代码允许拇指大小。您的公式相当于调用此函数并为ThumbSize 传递0 的值。

如果你想复制控件的行为,那么你也需要使用这个算法。您需要受保护的 hack 才能破解课程。

type
  THackedTrackBar = class(TTrackBar);

procedure TForm1.TrackBar1MouseMove(Sender: TObject; Shift: TShiftState; 
  X, Y: Single);
var
  tb: THackedTrackBar;
begin
  tb := THackedTrackBar(TrackBar1);
  Label1.Text := FloatToStr(
    PosToValue(
      tb.Min,
      tb.Max,
      tb.ViewportSize,
      tb.GetThumbSize(tb.FIgnoreViewportSize),
      tb.Width,
      X,
      tb.FIgnoreViewportSize
    )
  );
end;

问题 2

OnMouseMove 在光标位于轨道拇指上时不会触发。这似乎是 FMX TTrackBar 控件的基本限制。底层框架显然知道光标在拇指上方,因为它将它绘制成不同的颜色,即所谓的热跟踪效果。但是,框架这样做似乎不利于让您知道鼠标正在移动。

轨迹栏上的拇指被实现为一个单独的对象。它是TThumb 类型的对象。 TTrackBar 控件通过受保护的属性公开对象。您可以使用受保护的 hack 来获取 thumb 对象,然后设置其 OnMouseMove 事件处理程序。不是很有趣,但肯定是解决问题的一种方法。

【讨论】:

  • 确实是的,但除了跟踪拇指之外,我真正的问题是这些值与预期不符。让我用代码更新问题,显示我如何获得我所谓的“实际”轨迹栏值。
  • 这里的值很好。对于我的最小值为 0 和最大值为 100 的跟踪栏,您的代码生成的值范围为 0 到 100。但在跟踪拇指时不会。我假设你使用的是 XE7。
  • 是的,它确实会产生从 min 到 max 的值,因为由于某种原因,它应该与轨迹栏本身在该位置下实际认为的正确值不同。检查我对问题的更好理解的编辑。
  • 我认为你理解你的问题。但这对我们来说很难。问题多一点怎么样。也许是一些截图。无论如何,我现在明白了这个问题。
  • 非常感谢您更新问题。现在这非常好。正如您在我的更新中看到的那样,我想我还是设法解决了所有问题。
【解决方案2】:

对于您最初的问题,您需要深入研究样式以找到轨迹栏周围左右填充的确切数量。请注意,这会因平台、样式和 Delphi 版本而异。

您可以考虑使用自己的风格,这样您就知道一切都会保持不变。

要解决 David Heffernan 提出的问题,您可以对 TTrackBar 进行透明控制(使其成为跟踪栏的客户端对齐子项)、拦截鼠标事件、进行自己的处理并将它们传递到跟踪栏。

【讨论】:

  • 这不应该是问题,因为如果您在移动鼠标时检查 X 的值,它会正确报告开始时的 0 和带有 position.x = 的轨迹栏结束时的 609 0 宽度 = 609。
  • 当鼠标位于轨迹栏的最左侧(最低位置)时,您是在说 Position.X = 0 吗?如果是这样,那么您似乎直接与您自己的问题相矛盾。
  • 怎么样?另外,检查接受的答案可能会让您更好地了解我的意思。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-05
  • 1970-01-01
  • 2014-06-30
  • 2010-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多