本文主要是介绍RecyclerView复用EditText后长按不可选中,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
要想找到问题的原因就得debug,入口呢?就是上篇文章提到的selectCurrentWordAndStartDrag()这个方法
private boolean selectCurrentWordAndStartDrag() {if (mInsertionActionModeRunnable != null) {mTextView.removeCallbacks(mInsertionActionModeRunnable);}if (extractedTextModeWillBeStarted()) {return false;}if (!checkField()) {return false;}if (!mTextView.hasSelection() && !selectCurrentWord()) {// No selection and cannot select a word.return false;}stopTextActionModeWithPreservingSelection();getSelectionController().enterDrag(SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_WORD);return true;}
我发现被复用的EditText在checkField()的时候返回的是false,从而导致了这个方法进行不下去了,这是问题的切入点。我们看看checkField()方法:
/*** Checks whether a selection can be performed on the current TextView.** @return true if a selection can be performed*/boolean checkField() {if (!mTextView.canSelectText() || !mTextView.requestFocus()) {Log.w(TextView.LOG_TAG,"TextView does not support text selection. Selection cancelled.");return false;}return true;}
这个方法的作用是检测在当前的TextView中是否可以执行选中,mTextView.requestFocus()是没有问题的,问题出在mTextView.canSelectText(),于是进入到canSelectText()方法:
boolean canSelectText() {return mText.length() != 0 && mEditor != null && mEditor.hasSelectionController();
}
这个方法很简单,debug 显示mEditor.hasSelectionController()返回为false,通过上一篇文章可以知道正常情况下mEditor的SelectionController是SelectionModifierCursorController,这里为啥返回为false呢?进去看看:
boolean hasSelectionController() {return mSelectionControllerEnabled;
}
只是返回了一个变量mSelectionControllerEnabled,想必是mSelectionControllerEnabled在被复用的时候被设置为了false,搜索一下这个变量在Editor中是在哪个地方赋值的,结果发现在这个方法里:
void prepareCursorControllers() {boolean windowSupportsHandles = false;//获取mTextView的布局属性ViewGroup.LayoutParams params = mTextView.getRootView().getLayoutParams();//如果布局属性为WindowManager.LayoutParams才能执行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;}}}
mSelectionControllerEnabled 的值取决于enabled && mTextView.textCanBeSelected(); 从debug上看 mTextView.textCanBeSelected();返回的是true,那问题就出在enabled 喽,enabled = windowSupportsHandles && mTextView.getLayout() != null; debug显示windowSupportsHandles 值为false,windowSupportsHandles 默认为false,赋值的地方就在if语句中,难道赋值为false了,还是根本就没有执行赋值语句呢?反复进行了几次debug发现都没有进入if语句中。
问题的关键来了,正常情况下mTextView.getRootView()返回的是DecorView,DecorView的LayoutParams类型就是WindowManager.LayoutParams,所以能执行if语句,被复用后的mTextView.getRootView()返回的并不是DecorView,而是EditText自己,为什么会出现这种情况呢?getRootView()这个方法是位于View中的:
public View getRootView() {if (mAttachInfo != null) {//正常情况下mAttachInfo.mRootView就是DecorViewfinal View v = mAttachInfo.mRootView;if (v != null) {return v;}}View parent = this;while (parent.mParent != null && parent.mParent instanceof View) {parent = (View) parent.mParent;}return parent;}
mAttachInfo是在AttachedToWindow的时候赋值的,结果发现mAttachInfo为空,所以才不会执行mAttachInfo.mRootView,而返回this;那么为什么mAttachInfo 为空呢,这里我没有去研究RecyclerView(懒),但可以肯定的是RecyclerView复用EditText时候没有做AttachedToWindow的操作从而导致mAttachInfo 为空。
那如何解决这个复用问题呢?不复用。卧槽,搞了半天你没有解决问题啊(不要打我啊)。目前还没有想到好的解决办法,如果有同学知道的话还请不吝赐教,这里看一下我的解决方法,在RecyclerView中的onBindViewHolder中调用holder.setIsRecyclable(false) 就可以解决问题啦!
@Overridepublic void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {if (holder instanceof EditVH) {/* 强制关闭复用,以解决EditText被复用后长按无法弹出ContextMenu的问题 */holder.setIsRecyclable(false);} else if (holder instanceof ImgVH) {//......}}
如果是用ListView的话一样可以通过不复用convertView而解决这个问题。
这篇关于RecyclerView复用EditText后长按不可选中的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!