You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firemonkey中如何让ScrollBox显示在其他组件上方?

Firemonkey中下拉ScrollBox被其他控件遮挡的解决方法

问题描述

我有一个Firemonkey窗体,窗体上包含一个TGridPanelLayout布局(多行单列),其中放置了多个TEdit控件。在顶部的TEdit控件内,我添加了一个下拉按钮和一个嵌套ListView的ScrollBox。点击下拉按钮时,ScrollBox可正常显示,但会被窗体上的其他Edit控件遮挡。我尝试对ScrollBox、ListView甚至TEdit调用BringToFront方法,也调整过ClipParent和ClipChild属性,均未解决问题。请问如何让ScrollBox及其内部的ListView显示在最前方?

补充代码

FMX窗体Unit1代码:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 569
  ClientWidth = 721
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  OnCreate = FormCreate
  DesignerMasterStyle = 0
  object GridPanelLayout1: TGridPanelLayout
    Align = MostTop
    Size.Width = 721.000000000000000000
    Size.Height = 153.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 0
    ColumnCollection = <
      item
        Value = 100.000000000000000000
      end>
    ControlCollection = <
      item
        Column = 0
        Control = Edit1
        Row = 0
      end>
    RowCollection = <
      item
        Value = 100.000000000000000000
      end>
    object Edit1: TEdit
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      Align = Horizontal
      TabOrder = 2
      Position.Y = 48.000000000000000000
      Size.Width = 721.000000000000000000
      Size.Height = 25.000000000000000000
      Size.PlatformDefault = False
      Caret.Interval = 1
      Caret.Width = 10
      object DropDownEditButton1: TDropDownEditButton
        CanFocus = False
        Cursor = crArrow
        Size.Width = 28.000000000000000000
        Size.Height = 21.000000000000000000
        Size.PlatformDefault = False
        TabOrder = 0
        OnClick = DropDownEditButton1Click
      end
      object VertScrollBox1: TVertScrollBox
        Align = Horizontal
        Position.Y = -16.000000000000000000
        Size.Width = 640.000000000000000000
        Size.Height = 50.000000000000000000
        Size.PlatformDefault = False
        TabOrder = 0
        Visible = False
        Viewport.Width = 640.000000000000000000
        Viewport.Height = 50.000000000000000000
        object ListView1: TListView
          ItemAppearanceClassName = 'TListItemAppearance'
          ItemEditAppearanceClassName = 'TListItemShowCheckAppearance'
          HeaderAppearanceClassName = 'TListHeaderObjects'
          FooterAppearanceClassName = 'TListHeaderObjects'
          EditMode = True
          Align = Client
          Size.Width = 640.000000000000000000
          Size.Height = 50.000000000000000000
          Size.PlatformDefault = False
          TabOrder = 0
        end
      end
    end
  end
  object GridPanelLayout2: TGridPanelLayout
    Align = Client
    Size.Width = 721.000000000000000000
    Size.Height = 416.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 1
    ColumnCollection = <
      item
        Value = 100.000000000000000000
      end>
    ControlCollection = <
      item
        Column = 0
        Control = Edit2
        Row = 0
      end
      item
        Column = 0
        Control = Edit3
        Row = 1
      end
      item
        Column = 0
        Control = Edit4
        Row = 2
      end
      item
        Column = 0
        Control = Edit5
        Row = 3
      end>
    RowCollection = <
      item
        SizeStyle = Absolute
        Value = 50.000000000000000000
      end
      item
        SizeStyle = Absolute
        Value = 50.000000000000000000
      end
      item
        SizeStyle = Absolute
        Value = 50.000000000000000000
      end
      item
        SizeStyle = Absolute
        Value = 50.000000000000000000
      end>
    object Edit2: TEdit
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      Align = Center
      TabOrder = 5
      Size.Width = 721.000000000000000000
      Size.Height = 32.000000000000000000
      Size.PlatformDefault = False
    end
    object Edit3: TEdit
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      Align = Center
      TabOrder = 4
      Size.Width = 721.000000000000000000
      Size.Height = 31.000000000000000000
      Size.PlatformDefault = False
    end
    object Edit4: TEdit
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      Align = Center
      TabOrder = 3
      Size.Width = 721.000000000000000000
      Size.Height = 28.000000000000000000
      Size.PlatformDefault = False
    end
    object Edit5: TEdit
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      Align = Center
      TabOrder = 2
      Size.Width = 721.000000000000000000
      Size.Height = 26.000000000000000000
      Size.PlatformDefault = False
    end
  end
end

创建及按钮点击事件代码:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.Edit, FMX.Layouts, FMX.StdCtrls, FMX.ListView.Types,
  FMX.ListView.Appearances, FMX.ListView.Adapters.Base, FMX.ListView;

type
  TForm1 = class(TForm)
    GridPanelLayout1: TGridPanelLayout;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    DropDownEditButton1: TDropDownEditButton;
    VertScrollBox1: TVertScrollBox;
    ListView1: TListView;
    GridPanelLayout2: TGridPanelLayout;
    procedure DropDownEditButton1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.DropDownEditButton1Click(Sender: TObject);
begin
  Edit1.BringToFront;
  VertScrollBox1.Position.Y := TSpeedButton(Sender).Height;
  VertScrollBox1.Height := (GridPanelLayout1.Height + GridPanelLayout2.Height) - Edit1.LocalToAbsolute(PointF(0, 0)).Y - Edit1.Height - 10;
  VertScrollBox1.Visible := true;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ListView1.Items.Clear;
  for var I := 0 to 100 do
    ListView1.Items.Add.Text := 'Demo ' + I.ToString;

end;

end.

问题表现

问题表现

解决方案

问题核心是Firemonkey的控件绘制层级由父容器决定,ScrollBox嵌套在Edit1内部时,层级再高也无法突破父容器和同级控件的遮挡。以下是两种可行解决方法:

方法一:将ScrollBox挂载到窗体顶层

  1. 在设计器中把VertScrollBox1从Edit1的子控件中移出,直接设为Form1的直接子控件。
  2. 修改按钮点击事件代码,重新设置ScrollBox的位置和大小:
procedure TForm1.DropDownEditButton1Click(Sender: TObject);
var
  EditAbsPos: TPointF;
begin
  // 获取Edit1在窗体中的绝对位置
  EditAbsPos := Edit1.LocalToAbsolute(PointF(0, 0));
  // 设置ScrollBox位置为Edit1下方
  VertScrollBox1.Position.X := EditAbsPos.X;
  VertScrollBox1.Position.Y := EditAbsPos.Y + Edit1.Height;
  // 设置宽度和高度
  VertScrollBox1.Width := Edit1.Width;
  VertScrollBox1.Height := ClientHeight - EditAbsPos.Y - Edit1.Height - 10;
  // 置顶显示
  VertScrollBox1.BringToFront;
  VertScrollBox1.Visible := True;
end;
  1. 确保VertScrollBox1的ClipParent属性设为False,避免被窗体边界裁剪。

方法二:用无边框窗体作为下拉弹窗(更优雅)

  1. 创建新的TForm(如TListPopupForm),设置BorderStyle为bsNone,FormStyle为fsStayOnTop。
  2. 在该窗体中添加TListView并铺满整个窗体。
  3. 修改按钮点击事件,计算位置后显示弹窗:
procedure TForm1.DropDownEditButton1Click(Sender: TObject);
var
  EditAbsPos: TPointF;
  PopupForm: TListPopupForm;
begin
  EditAbsPos := Edit1.LocalToAbsolute(PointF(0, 0));
  PopupForm := TListPopupForm.Create(nil);
  try
    PopupForm.Left := Screen.Left + Round(EditAbsPos.X);
    PopupForm.Top := Screen.Top + Round(EditAbsPos.Y + Edit1.Height);
    PopupForm.Width := Round(Edit1.Width);
    PopupForm.Height := ClientHeight - Round(EditAbsPos.Y) - Edit1.Height - 10;
    // 填充ListView数据
    PopupForm.ListView1.Items.Clear;
    for var I := 0 to 100 do
      PopupForm.ListView1.Items.Add.Text := 'Demo ' + I.ToString;
    PopupForm.ShowModal;
    // 可选:将选中项文本赋值给Edit1
    if PopupForm.ListView1.Selected <> nil then
      Edit1.Text := PopupForm.ListView1.Selected.Text;
  finally
    PopupForm.Free;
  end;
end;

原方法无效原因

  • BringToFront()仅能在当前父容器内调整层级,无法突破父容器范围。
  • ClipParent/ClipChild控制的是控件是否被父容器裁剪,与层级显示无关。

内容的提问来源于stack exchange,提问作者fisi-pjm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 23:04:57