问题件简述
当 RecycleView
中有 EditText
的时候,上下滚动被复用的时候,发现长按 EditText
没有弹出上下文菜单
分析
调试了一波后发现,正常情况下长按的时候 TextView
的 performClick()
函数返回的是 true
,没有弹出上下文的时候,此函数返回了 fasle
首先找 TextView
的长按事件 performLongClick()
,看其源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Override public boolean performLongClick() { boolean handled = false;
if (mEditor != null) { mEditor.mIsBeingLongClicked = true; }
if (super.performLongClick()) { handled = true; }
if (mEditor != null) { handled |= mEditor.performLongClick(handled); mEditor.mIsBeingLongClicked = false; }
if (handled) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); if (mEditor != null) mEditor.mDiscardNextActionUp = true; }
return handled; }
|
先正常调试
super.performLongClick()
返回 false
mEditor.performLongCLick()
返回 true
无法弹出菜单的时候调试
super.performLongClick()
返回 false
mEdit.performLongClick()
返回 false
那么问题出现在 mEdit.performLongClick()
上面,查看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public boolean performLongClick(boolean handled) { if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY) && mInsertionControllerEnabled) { final int offset = mTextView.getOffsetForPosition(mLastDownPositionX, mLastDownPositionY); Selection.setSelection((Spannable) mTextView.getText(), offset); getInsertionController().show(); mIsInsertionActionModeStartPending = true; handled = true; } return handled; }
|
正常调试
mInsertionControllerEnabled
是 true
无法弹出菜单时候的调试
mInsertionControllerEnable
为 false
那么问题定位到 mInsertionControllerEnabled
的布尔值了,寻找这个成员属性被修改的地方,发现在 Editor#prepareCursorControllers
内,查看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| void prepareCursorControllers() { boolean windowSupportsHandles = false;
ViewGroup.LayoutParams params = mTextView.getRootView().getLayoutParams(); if (params instanceof WindowManager.LayoutParams) { WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params; windowSupportsHandles = windowParams.type < WindowManager.LayoutParams.FIRST_SUB_WINDOW || windowParams.type > WindowManager.LayoutParams.LAST_SUB_WINDOW; }
boolean enabled = windowSupportsHandles && mTextView.getLayout() != null; mInsertionControllerEnabled = enabled && isCursorVisible(); mSelectionControllerEnabled = enabled && mTextView.textCanBeSelected();
if (!mInsertionControllerEnabled) { hideInsertionPointCursorController(); if (mInsertionPointCursorController != null) { mInsertionPointCursorController.onDetached(); mInsertionPointCursorController = null; } }
if (!mSelectionControllerEnabled) { stopTextActionMode(); if (mSelectionModifierCursorController != null) { mSelectionModifierCursorController.onDetached(); mSelectionModifierCursorController = null; } } }
|
到这里不用调试就可以看出问题出在哪里 ViewGroup.LayoutParams params = mTextView.getRootView().getLayoutParams();
这句话,如果在 ViewHolder
绑定数据的时候被执行的话,永远不可能是 WindowManager.LayoutParams
,因为此时 EdtiText
根本没有被 attachToWindow
解决方案
既然知道了是 EditText
没有被 attachToWindow
的时候调用了 Editor#prepareCursorControllers
函数导致的,那么我在 attachToWindow
之后重新调用一遍这个函数不就可以了?
因为这个是包权限的方法,所以需要找间接调用的地方,找到了 setCursorVisible
方法中有被调用,所以只要在 onAttachToWindow
的时候先设置成 false
然后再设置成 true
就可以了
1 2 3 4 5 6 7 8 9 10 11
| edtImgDesc.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { edtImgDesc.setCursorVisible(false); edtImgDesc.setCursorVisible(true); }
@Override public void onViewDetachedFromWindow(View v) { } });
|