Xamarin.Forms通过DP设置适配全屏幕的按钮高度运行报错排查
我参考Android平台Xamarin.Forms相关帖子修改了下述代码,但运行时触发报错且无法定位错误根源,实现目标为:让按钮的HeightRequest属性不受设备屏幕尺寸影响,在所有设备上呈现一致的视觉高度,相关实现代码如下:
接口定义
public interface IServiceDp { double GetHeightInInches(double inch); // 按英寸设置高度 double GetHeightInDp(double dp); // 按DP(设备无关像素)设置高度 }
Android平台接口实现
[assembly: Dependency(typeof(ServiceDp))] namespace easySales.Droid { [Preserve(AllMembers = true)] public class ServiceDp : IServiceDp { public ServiceDp() { } Context Context => Android.App.Application.Context; [Obsolete] public double GetScreenHeightInInches() { DisplayMetrics dm = new DisplayMetrics(); MainActivity activity = new MainActivity();activity.WindowManager.DefaultDisplay.GetMetrics(dm); double density = dm.Density * 160; var nWidthPixels = dm.WidthPixels; var nHeightPixels = dm.HeightPixels; activity.WindowManager.DefaultDisplay.GetMetrics(dm); double x = Math.Pow(nWidthPixels / dm.Xdpi, 2); double y = Math.Pow(nHeightPixels / dm.Ydpi, 2); double screenInches = Math.Sqrt(x + y); return screenInches; } [Obsolete] public double GetHeightInInches(double inch) { var screenHeight = GetScreenHeightInInches(); DisplayMetrics dm = new DisplayMetrics(); MainActivity mainActivity = new MainActivity(); mainActivity.WindowManager.DefaultDisplay.GetMetrics(dm); var pixelInch = (dm.HeightPixels / screenHeight) / dm.Density; return pixelInch * inch; } public double GetHeightInDp(double dp) { var screenHeight = GetScreenHeightInInches(); DisplayMetrics dm = new DisplayMetrics(); MainActivity mainActivity = new MainActivity(); mainActivity.WindowManager.DefaultDisplay.GetMetrics(dm); var pixelInch = (dm.HeightPixels / screenHeight) / dm.Density; return pixelInch / 160 * dp; } } }
按钮高度设置实现
void buttonWidthAndHeight() { var ser = DependencyService.Get<IServiceDp>(); btnCheck.HeightRequest = ser.GetHeightInDp(100); }
问题根因
- 核心崩溃原因:手动
new MainActivity()获取Activity实例。Android中Activity是系统托管的组件,手动实例化的Activity没有绑定Window对象,访问WindowManager时会直接触发空指针异常。 - 逻辑错误:Xamarin.Forms所有控件的尺寸单位本身就是设备无关像素(DP),框架已经自动做了全设备密度适配,直接给
HeightRequest赋值固定数值就能在所有设备上保持一致视觉大小,现有代码多余的换算逻辑反而会导致尺寸错乱。 - 冗余代码:重复调用
GetMetrics、声明未使用的density变量,没有实际作用。
修复方案
如果只是需要固定DP尺寸(绝大多数业务场景的需求),完全不需要写自定义依赖服务,直接赋值即可:
void buttonWidthAndHeight() { // 100单位为DP,所有设备上视觉大小完全一致 btnCheck.HeightRequest = 100; }注意不要在页面构造函数中调用该方法,建议放到
OnAppearing生命周期中执行,避免控件未初始化完成导致赋值失效。如果确实需要固定真实物理英寸尺寸(比如要求按钮高度严格等于0.2英寸),使用修正后的平台实现:
[assembly: Dependency(typeof(ServiceDp))] namespace easySales.Droid { [Preserve(AllMembers = true)] public class ServiceDp : IServiceDp { private Context AppContext => Android.App.Application.Context; private DisplayMetrics GetRealDisplayMetrics() { // 通过全局Context获取系统服务,不需要手动实例化Activity var wm = AppContext.GetSystemService(Context.WindowService).JavaCast<IWindowManager>(); var dm = new DisplayMetrics(); wm.DefaultDisplay.GetMetrics(dm); return dm; } public double GetScreenHeightInInches() { var dm = GetRealDisplayMetrics(); double widthInch = Math.Pow(dm.WidthPixels / dm.Xdpi, 2); double heightInch = Math.Pow(dm.HeightPixels / dm.Ydpi, 2); return Math.Sqrt(widthInch + heightInch); } public double GetHeightInInches(double inch) { var dm = GetRealDisplayMetrics(); // 1英寸物理尺寸对应的DP值 double dpPerInch = dm.Xdpi / dm.Density; return inch * dpPerInch; } public double GetHeightInDp(double dp) { // Xamarin.Forms原生单位就是DP,直接返回即可 return dp; } } }
内容的提问来源于stack exchange,提问作者Atsenogua Sandra
相关产品推荐
相关产品推荐

