Android Oreo设备中AutoCompleteTextView获焦时showDropDown失效报错问题
解决Android Oreo上AutoCompleteTextView调用showDropDown报BadTokenException的问题
这个问题我在适配Oreo版本时刚好踩过坑,本质是Android 8.0(API 26)对窗口管理的token校验变得更严格,再加上你的代码里存在两个关键问题,导致了这个异常:
问题根源分析
- 上下文使用错误:你给
googlePlaceAutocompleteAdapter传了getApplicationContext(),但AutoCompleteTextView的下拉窗口是依附于当前Activity的,Application上下文没有关联的Activity窗口token,在Oreo中会直接导致系统无法验证窗口的有效性。 - 焦点变化时机与窗口状态不同步:
OnFocusChangeListener触发时,Activity可能还没完全进入resumed状态,或者控件的窗口token尚未准备就绪,直接调用showDropDown()就会抛出BadTokenException。
分步解决方案
1. 替换Adapter的上下文为Activity实例
这是最核心的修复步骤,先把Adapter的上下文改成当前Activity的实例,而不是Application上下文:
// 把getApplicationContext()换成你的Activity实例,比如MainActivity.this googlePlaceAutocompleteAdapter = new googlePlaceAutocompleteAdapter(YourActivityName.this, android.R.layout.simple_list_item_1, this, list); search_place.setAdapter(googlePlaceAutocompleteAdapter);
2. 优化showDropDown的调用时机
即使修复了上下文,焦点变化的时机还是可能存在窗口未就绪的问题,推荐两种更可靠的触发方式:
方式一:延迟调用,给系统留足窗口准备时间
用postDelayed延迟100-200ms执行,同时再次确认控件是否还处于焦点状态,避免后续焦点丢失导致的异常:
search_place.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if(hasFocus){ search_place.postDelayed(new Runnable() { @Override public void run() { // 再次校验焦点状态,防止中途焦点被其他控件抢走 if(search_place.isFocused() && !search_place.isPopupShowing()){ search_place.showDropDown(); } } }, 100); } } });
方式二:改用OnTouchListener触发(更稳定)
用户点击控件时触发showDropDown,这个时机的窗口状态是完全就绪的,比焦点变化回调更可靠:
search_place.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_UP){ // 确保下拉窗口没显示再调用 if(!search_place.isPopupShowing()){ search_place.showDropDown(); } } // 返回false,让系统继续处理触摸事件,不影响正常输入 return false; } });
额外注意点
- 确保你的Activity处于resumed状态时才调用
showDropDown,如果是在onCreate或者onStart里触发,也可能因为窗口未完全创建而报错。 - 可以在调用前先判断
search_place.isAttachedToWindow(),确保控件已经绑定到窗口上。
内容的提问来源于stack exchange,提问作者Nirmal Kumar A
相关产品推荐
相关产品推荐

