在 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 遙控器的焦點導航。