在 TV 开发中,焦点管理是通过 Focus Navigation
实现的,PopupWindow
默认不接受焦点,导致遥控器无法选择弹窗内的控件。这是因为 PopupWindow
默认不会将焦点传递到其内容视图上。
要解决问题,可以通过以下步骤调整 PopupWindow
的焦点行为。
解决方法
1. 设置 PopupWindow
可聚焦并允许其内容获取焦点
确保 PopupWindow
的 focusable
属性为 true
,并强制让其内容视图可以获取焦点。
在 BasePopupWindow
的构造函数中添加以下代码:
setFocusable(true); // 允许 PopupWindow 获取焦点
setOutsideTouchable(false); // 禁止点击外部关闭(可选,根据需求调整)
完整代码修改:
public BasePopupWindow(Context context, int layoutResId, int width, int height, boolean focusable) {super(width, height, focusable);binding = DataBindingUtil.inflate(LayoutInflater.from(context), layoutResId, null, false);setContentView(binding.getRoot());setBackgroundDrawable(new ColorDrawable(0x00000000)); // 默认背景透明setFocusable(true); // 确保弹窗获取焦点setOutsideTouchable(false); // 避免点击外部时关闭,保证焦点initialize(); // 子类实现具体逻辑
}
2. 强制请求焦点到弹窗的内容
在 LogoutPopupWindow
的 initialize
方法中,调用 requestFocus()
将焦点移动到弹窗的按钮上。
@Override
protected void initialize() {// 设置动态文案binding.tvMessage.setText(username + ",是否退出登录?");// 设置按钮点击事件binding.btnConfirm.setOnClickListener(v -> {Toast.makeText(binding.getRoot().getContext(), username + "已退出登录", Toast.LENGTH_SHORT).show();dismissPopup();});binding.btnCancel.setOnClickListener(v -> dismissPopup());// 强制将焦点设置到退出按钮上binding.btnConfirm.post(() -> binding.btnConfirm.requestFocus());
}
3. 确保布局中的控件支持焦点
在 popup_logout.xml
中,确保按钮和其他交互控件明确声明支持焦点和点击事件:
<Buttonandroid:id="@+id/btn_confirm"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="退出登录"android:focusable="true"android:clickable="true"android:backgroundTint="@android:color/holo_red_light"android:textColor="@android:color/white"android:layout_marginTop="8dp" /><Buttonandroid:id="@+id/btn_cancel"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="取消"android:focusable="true"android:clickable="true"android:backgroundTint="@android:color/darker_gray"android:textColor="@android:color/white"android:layout_marginTop="8dp" />
4. 使用 WindowManager.LayoutParams
设置焦点模式
确保 PopupWindow
在显示时优先处理焦点事件。可以在弹窗显示时配置 WindowManager.LayoutParams
:
@Override
public void showAtLocation(View parent, int gravity, int x, int y) {super.showAtLocation(parent, gravity, x, y);getContentView().setFocusable(true); // 内容允许聚焦getContentView().setFocusableInTouchMode(true);
}
完整流程
- 在
BasePopupWindow
中:- 确保
setFocusable(true)
和setOutsideTouchable(false)
。
- 确保
- 在布局文件中:
- 明确声明交互控件支持焦点和点击事件。
- 在
initialize()
方法中:- 使用
requestFocus()
将初始焦点设置到弹窗内的某个控件。
- 使用
- 在
showAtLocation
或showAsDropDown
中:- 确保视图允许焦点和触摸模式。
完成这些步骤后,弹出的 PopupWindow
就会正确响应 TV 遥控器的焦点导航。