Android 4.3(API 18)及以下调用 RelativeLayout.mesure() 时出现 NullPointerException 异常

本文分析了在Android4.3及其以下版本中使用RelativeLayout时出现NullPointerException的原因,并提供了三种解决方案,包括设置父视图、显式设置LayoutParams以及更改布局。

负责的一个新手引导的需求,友盟上报 Android 4.3(API 18) 的机型(公司APP 最低支持的安卓版本即为 4.3)中会出现 NullPointerException 的 bug。

百度后知道了原因:http://blog.youkuaiyun.com/chengxu_hou/article/details/74940938。原来,只要是在 4.3 及以下的系统中调用 RelativeLayout.mesure() 方法,就会出现空指针的问题,是因为源码没有做判空处理;4.4 及以上由于修复了系统源码,就不会出现该问题了。

再看看我的bug,我的 Dialog 的 xml 根布局的确是 RelativeLayout,并且在 Dialog 的构造方法中调用了 mesure() 方法:

这里写图片描述

解决方法:
(第一种方法最简单,推荐之)
1、使用 inflate 初始化布局时,尽量设置父视图(parent

contentView = LayoutInflater.from(context).inflate(layoutResId, parent, false);

2、显式设置 LayoutParams

contentView = LayoutInflater.from(context).inflate(layoutResId, null);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
contentView.setLayoutParams(params);

3、老老实实修改布局,不使用 RelativeLayout ~

总结
在使用 RelativeLayout 并且需要手动 measure 时,要注意可能出现空指针的问题。

### 代码解释 在 Java 中,`RelativeLayout.LayoutParams` 用于为 `RelativeLayout` 中的子视图指定布局参数。`RelativeLayout` 是 Android 中常用的布局容器,它允许子视图相对于其他视图或父容器进行定位。`RelativeLayout.LayoutParams` 提供了一系列方法来设置这些相对位置关系。 以下是一个简单的使用 `RelativeLayout.LayoutParams` 创建布局参数的示例代码: ```java import android.os.Bundle; import android.widget.Button; import android.widget.RelativeLayout; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 创建 RelativeLayout 作为父布局 RelativeLayout relativeLayout = new RelativeLayout(this); // 创建一个 Button 作为子视图 Button button = new Button(this); button.setText("Click me"); // 创建 RelativeLayout.LayoutParams 对象 RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT ); // 设置按钮相对于父布局的位置 layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); // 将布局参数应用到按钮上 button.setLayoutParams(layoutParams); // 将按钮添加到 RelativeLayoutrelativeLayout.addView(button); // 设置 RelativeLayout 为活动的内容视图 setContentView(relativeLayout); } } ``` 在上述代码中,首先创建了一个 `RelativeLayout` 作为父布局,然后创建了一个 `Button` 作为子视图。接着,使用 `RelativeLayout.LayoutParams` 创建布局参数对象,并设置其宽度和高度为 `WRAP_CONTENT`。通过 `addRule` 方法设置按钮相对于父布局的位置为居中显示。最后,将布局参数应用到按钮上,并将按钮添加到 `RelativeLayout` 中。 ### 使用场景 `RelativeLayout.LayoutParams` 主要用于在运行动态设置 `RelativeLayout` 中子视图的布局参数。常见的使用场景包括: - **动态布局调整**:根据不同的条件或用户交互,动态改变子视图的位置和大小。例如,在用户点击某个按钮后,将另一个视图移动到指定位置。 - **响应式设计**:根据设备的屏幕尺寸或方向变化,动态调整布局。例如,在横屏和竖屏模式下,子视图的排列方式可能需要不同。 - **复杂布局管理**:当布局关系较为复杂,难以在 XML 文件中静态定义,可以使用代码动态设置布局参数。例如,子视图的位置依赖于其他视图的状态或数据。 ### 潜在问题及优化建议 #### 潜在问题 - **类型转换错误**:在获取视图的布局参数,如果类型转换不正确,可能会抛出 `ClassCastException`。例如,将 `RelativeLayout.LayoutParams` 错误地转换为 `LinearLayout.LayoutParams`。 - **性能问题**:频繁地在运行动态设置布局参数可能会影响性能,尤其是在布局较为复杂的情况下。每次调用 `setLayoutParams` 方法都会触发布局的重新计算和绘制。 - **布局冲突**:如果使用 `addRule` 方法设置的布局规则相互冲突,可能会导致布局显示异常。例如,同设置视图在父布局的左边和右边。 #### 优化建议 - **正确的类型转换**:在获取视图的布局参数,确保使用正确的类型进行转换。如果不确定视图的布局参数类型,可以使用 `ViewGroup.MarginLayoutParams` 作为通用类型,因为它是大多数布局参数类的基类。例如: ```java ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); ``` - **减少布局重绘**:尽量减少在运行频繁调用 `setLayoutParams` 方法。可以在布局初始化一次性设置好布局参数,或者在必要批量更新布局参数,而不是每次只修改一个参数就调用 `setLayoutParams`。 - **检查布局规则**:在使用 `addRule` 方法设置布局规则,仔细检查规则之间是否存在冲突。可以在设置规则之前先清除已有的规则,避免冲突。例如: ```java layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_LEFT); layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值