codex补充:
把笔记给codex审核后,补充笔记如下。

## View Binding

### 1. 作用

View Binding 会根据 XML 布局生成 Binding 类,用于替代 `findViewById()`。

例如:

```text
activity_main.xml → ActivityMainBinding
btn_submit        → binding.btnSubmit

优点:

  • 减少重复代码
  • 提供类型安全
  • 降低控件 ID 写错导致的空指针风险

2. 启用

在模块级 build.gradle 中配置:

android {
    buildFeatures {
        viewBinding true
    }
}

3. Activity 中使用

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnSubmit.setOnClickListener(v ->
                binding.tvResult.setText("提交成功")
        );
    }
}

使用 View Binding 后,应通过:

setContentView(binding.getRoot());

加载布局,不再使用:

setContentView(R.layout.activity_main);

4. Fragment 中使用

public class HomeFragment extends Fragment {

    private FragmentHomeBinding binding;

    @Override
    public View onCreateView(
            LayoutInflater inflater,
            ViewGroup container,
            Bundle savedInstanceState
    ) {
        binding = FragmentHomeBinding.inflate(inflater, container, false);
        return binding.getRoot();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }
}

Fragment 的 View 销毁后必须清空 Binding,避免继续持有旧 View。


防止按钮重复点击

重复点击可能导致:

  • 重复发送请求
  • 重复提交订单
  • 重复页面跳转
  • 重复弹出对话框

1. 短时间点击防抖

适合页面跳转、弹窗等本地操作。

private long lastClickTime = 0L;
private static final long CLICK_INTERVAL = 800L;

private boolean isFastClick() {
    long currentTime = SystemClock.elapsedRealtime();

    if (currentTime - lastClickTime < CLICK_INTERVAL) {
        return true;
    }

    lastClickTime = currentTime;
    return false;
}

使用:

binding.btnNext.setOnClickListener(v -> {
    if (isFastClick()) {
        return;
    }

    openNextPage();
});

建议使用 SystemClock.elapsedRealtime(),因为它不会受到系统时间修改的影响。

2. 异步操作期间禁用按钮

适合登录、提交订单、上传文件等网络操作。

private boolean isSubmitting = false;

private void submit() {
    if (isSubmitting) {
        return;
    }

    isSubmitting = true;
    binding.btnSubmit.setEnabled(false);
    binding.btnSubmit.setText("提交中");

    requestData();
}

请求结束后恢复:

private void finishSubmit() {
    isSubmitting = false;
    binding.btnSubmit.setEnabled(true);
    binding.btnSubmit.setText("提交");
}

成功和失败时都要调用 finishSubmit(),否则按钮可能一直处于禁用状态。

3. 使用原则

场景 推荐方式
页面跳转、弹窗 时间防抖
登录、提交、上传 业务状态 + 禁用按钮
支付、订单等关键业务 客户端防重复 + 服务端幂等

客户端只能减少误操作。支付、订单等重要业务还必须由服务端根据唯一请求标识防止重复执行。
```

监听器写法与选择

1. 推荐:独立 Lambda

每个按钮分别绑定自己的点击逻辑:

binding.btnLogin.setOnClickListener(v -> login());
binding.btnRegister.setOnClickListener(v -> register());

适合:

  • 按钮执行不同功能
  • 点击逻辑较短
  • 大多数普通页面

优点:

  • 代码简洁
  • 控件与行为对应清晰
  • 不需要根据 ID 再次判断来源

逻辑较多时,应提取为独立方法:

binding.btnSubmit.setOnClickListener(v -> submitForm());

private void submitForm() {
    // 表单校验
    // 提交数据
}

不要把大量业务代码直接写进 Lambda。


2. 相关按钮:共享监听器

多个按钮属于同一组操作时,可以共享监听器:

View.OnClickListener listener = v -> {
    int id = v.getId();

    if (id == R.id.btn_one) {
        handleButtonOne();
    } else if (id == R.id.btn_two) {
        handleButtonTwo();
    }
};

binding.btnOne.setOnClickListener(listener);
binding.btnTwo.setOnClickListener(listener);

适合:

  • 数字键盘按钮
  • 筛选或排序按钮
  • 播放、暂停等同类操作
  • 多个按钮执行相似逻辑

如果逻辑只存在少量差异,可以直接传递参数,避免 ID 判断:

binding.btnOne.setOnClickListener(v -> selectType(1));
binding.btnTwo.setOnClickListener(v -> selectType(2));

这种写法通常比共享监听器更直观。

建议使用 if/else 判断资源 ID。现代 Android Gradle Plugin 中,资源 ID 不一定是编译期常量,switch (v.getId()) 在部分项目中可能无法编译。


3. 匿名内部类

Java 8 以前常使用匿名内部类:

binding.btnSubmit.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        submitForm();
    }
});

它与 Lambda 功能相同,但代码更长。

现在通常优先使用:

binding.btnSubmit.setOnClickListener(v -> submitForm());

匿名内部类适合学习接口回调原理,或需要创建无法使用 Lambda 的复杂接口实现。


4. Activity 实现监听接口

传统写法是让 Activity 实现 View.OnClickListener

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener {

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_login) {
            login();
        }
    }
}

绑定时统一传入 this

binding.btnLogin.setOnClickListener(this);

这种方式不是“按钮越多越适合”。按钮较多时,所有判断集中在一个 onClick() 中,容易造成:

  • 条件判断过多
  • 方法体过长
  • 控件和行为距离较远
  • 后期维护困难

现代项目一般优先使用独立 Lambda,只有多个控件确实需要共享处理流程时才集中处理。


5. 静态内部监听器

static class SubmitClickListener implements View.OnClickListener {

    @Override
    public void onClick(View v) {
        // 点击逻辑
    }
}

这种写法适合监听器需要复用、单独测试或封装时使用。

普通页面点击事件不建议专门创建静态内部类,因为会增加类和参数传递,降低代码可读性。


6. 选择原则

场景 推荐写法
按钮功能互不相同 独立 Lambda
逻辑较长 Lambda 调用独立方法
多个按钮只有参数不同 Lambda 传递参数
多个按钮共享处理流程 共享监听器
需要复用或单独测试 独立监听器类
学习接口回调原理 匿名内部类或 Activity 实现接口

核心原则:

是否共享监听器取决于按钮行为是否相关,而不是按钮数量。默认优先使用独立 Lambda,并将复杂业务提取为方法。


Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐