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
相关产品推荐
相关产品推荐

