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

Xamarin Android自定义渲染器疑问:CameraPreview创建与视图显示问题

解决Xamarin自定义渲染器Android端视图不显示的问题

我来帮你搞定这个问题!你的代码里有几个关键细节没处理好,导致按钮没渲染出来,咱们一步步来修正:

核心问题:ViewGroup必须实现子视图布局逻辑

你创建的CameraPreview继承自ViewGroup,但空的OnLayout方法是致命的——ViewGroup不会自动帮你摆放子View,必须在这个方法里手动指定每个子View的位置和大小,不然Inflate进来的按钮根本不知道该放哪。

修正后的完整代码

1. 完善CameraPreview类

public class CameraPreview : ViewGroup
{
    public CameraPreview(Context context) : base(context)
    {
    }

    // 建议添加带AttributeSet的重载,适配不同创建场景
    public CameraPreview(Context context, IAttributeSet attrs) : base(context, attrs)
    {
    }

    public CameraPreview(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
    {
    }

    // 重写OnMeasure,先测量子View的尺寸(可选但推荐)
    protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        // 先测量所有子View
        MeasureChildren(widthMeasureSpec, heightMeasureSpec);
        // 设置当前ViewGroup的尺寸
        SetMeasuredDimension(ResolveSize(SuggestedMinimumWidth, widthMeasureSpec),
                             ResolveSize(SuggestedMinimumHeight, heightMeasureSpec));
    }

    // 关键:实现OnLayout摆放子View
    protected override void OnLayout(bool changed, int l, int t, int r, int b)
    {
        int childCount = ChildCount;
        int parentWidth = r - l;
        int parentHeight = b - t;

        // 遍历所有子View,这里以线性垂直布局为例(和你的axml布局对应)
        int currentTop = 0;
        for (int i = 0; i < childCount; i++)
        {
            var child = GetChildAt(i);
            if (child.Visibility == ViewStates.Gone) continue;

            // 测量子View的宽高
            int childWidth = child.MeasuredWidth;
            int childHeight = child.MeasuredHeight;

            // 水平居中,垂直从上到下排列
            int childLeft = (parentWidth - childWidth) / 2;
            child.Layout(childLeft, currentTop, childLeft + childWidth, currentTop + childHeight);

            currentTop += childHeight;
        }
    }
}

2. 修正CameraRenderer类

public class CameraRenderer : ViewRenderer<Controls.CustomControl, CameraPreview>
{
    private readonly LayoutInflater _inflater;

    public CameraRenderer(Context context) : base(context)
    {
        _inflater = LayoutInflater.FromContext(context);
    }

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

        // 只有当新元素存在时才初始化,避免空引用
        if (e.NewElement != null && Control == null)
        {
            var cameraPreview = new CameraPreview(Context);
            // Inflate布局到cameraPreview中,第三个参数设为true表示添加到父容器
            _inflater.Inflate(Resource.Layout.layout1, cameraPreview, true);
            SetNativeControl(cameraPreview);
        }

        // 可选:处理旧元素的清理逻辑
        if (e.OldElement != null)
        {
            // 释放资源等操作
        }
    }
}

3. 你的layout1.axml保持不变

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:text="Button test"
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

关键修改点说明

  • OnLayout方法实现:这是让子View显示的核心,必须手动定义每个子View的布局位置,我这里写的是简单的垂直居中排列,你可以根据需求调整。
  • Renderer的初始化逻辑:加了e.NewElement != null && Control == null的判断,避免重复初始化;用LayoutInflater.FromContext替代GetSystemService,更简洁可靠。
  • Inflate参数:第三个参数设为true,表示将Inflate的视图添加到父容器(cameraPreview)中,不然布局只会被解析但不会被添加到ViewGroup里。

另外你之前遇到的TargetInvocationException,大概率是因为空的OnLayout或者初始化逻辑错误导致的,修正后应该就能解决啦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:43:34