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

如何利用自定义Renderer获取ListView的实际测量高度?

解决ListView子控件无滚动时测量维度为零的问题

我太懂你这种挫败感了——搜遍了各种解决方案,结果那些监听布局事件反复测量的招完全不管用,只要滚动状态没变化,子控件的测量维度就一直是零。刚好我之前也碰到过类似的问题,给你调整下你的CustomListViewRenderer实现,试试这个思路:

核心调整思路

  • 跳过依赖滚动状态的逻辑,直接在控件初始化完成后强制触发一次子控件的布局测量
  • 利用原生平台的专属时机(Android的ViewTreeObserver、iOS的LayoutSubviews),确保在控件真正准备就绪后再执行测量,不卡滚动状态的限制

调整后的代码示例

public class CustomListViewRenderer : ListViewRenderer
{
    private bool _hasMeasuredOnce = false;

    protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
    {
        base.OnElementChanged(e);

        if (Control != null && !_hasMeasuredOnce)
        {
            // Android平台专属处理
            #if __ANDROID__
            var viewTreeObserver = Control.ViewTreeObserver;
            viewTreeObserver.GlobalLayout += OnGlobalLayout;
            #endif

            // iOS平台专属处理
            #if __IOS__
            Control.SetNeedsLayout();
            ForceMeasureSubviews();
            #endif
        }
    }

    #if __ANDROID__
    private void OnGlobalLayout(object sender, EventArgs e)
    {
        var viewTreeObserver = (Android.Views.ViewTreeObserver)sender;
        // 先移除监听,避免重复触发浪费性能
        viewTreeObserver.GlobalLayout -= OnGlobalLayout;

        // 遍历所有可见子控件,强制执行测量
        for (int i = 0; i < Control.ChildCount; i++)
        {
            var child = Control.GetChildAt(i);
            child.Measure(
                Android.Views.View.MeasureSpec.MakeMeasureSpec(Control.Width, Android.Views.View.MeasureSpecMode.Exactly),
                Android.Views.View.MeasureSpec.MakeMeasureSpec(0, Android.Views.View.MeasureSpecMode.Unspecified)
            );
            // 这里就能拿到真实的测量尺寸了
            var measuredHeight = child.MeasuredHeight;
            var measuredWidth = child.MeasuredWidth;
        }

        _hasMeasuredOnce = true;
    }
    #endif

    #if __IOS__
    private void ForceMeasureSubviews()
    {
        foreach (var subview in Control.Subviews)
        {
            subview.SetNeedsLayout();
            subview.LayoutIfNeeded();
            // 获取子控件的真实布局尺寸
            var frame = subview.Frame;
        }
        _hasMeasuredOnce = true;
    }
    #endif
}

关键细节说明

  • Android端:GlobalLayout事件会在整个视图树完成布局后触发,不管有没有滚动操作,都能保证控件已经完成初始布局,这时候测量子控件就能拿到真实尺寸,而且只执行一次避免冗余
  • iOS端:通过SetNeedsLayout和LayoutIfNeeded强制子控件完成布局,跳过滚动状态的限制,直接获取有效尺寸
  • _hasMeasuredOnce标记用来防止重复执行测量逻辑,避免不必要的性能消耗

如果还是没解决,可以试试在OnElementPropertyChanged里监听ListView的ItemsSource变化,当数据源更新后再触发一次测量逻辑,确保新添加的子控件也能被正确测量。

内容的提问来源于stack exchange,提问作者ery245gs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:47:33