1.问题现象
点击recyclerView headView 中的EditText时recycleView 莫名自动滑动
布局代码
主布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<!-- 顶部栏 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="56dp">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:theme="@style/ToolbarTheme" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:gravity="center"
android:text="@string/title_new_card"
android:textColor="@color/white"
android:textSize="20sp" />
<ImageView
android:id="@+id/iv_back"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:padding="7dp"
android:src="@drawable/nav_btn_back" />
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white" />
</LinearLayout>
headView布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:focusable="true"
android:id="@+id/fdfsdf"
android:focusableInTouchMode="true"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<!-- card名称 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/et_card_name"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:background="@null"
android:gravity="center_vertical"
android:hint="@string/hint_edit_card_name"
android:paddingRight="2dp"
android:maxLines="1"
android:textColor="@color/text_black"
android:textColorHint="@color/text_hint"
android:textSize="@dimen/font_small_3" />
</RelativeLayout>
<View
android:id="@+id/cardNameUnderLine"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/divide_line" />
<!-- card帐号 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_card_id_copy"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@drawable/btn_copy" />
<EditText
android:id="@+id/et_card_id"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/iv_card_id_copy"
android:background="@null"
android:gravity="center_vertical"
android:hint="@string/hint_edit_card_id"
android:paddingRight="2dp"
android:maxLines="1"
android:textColor="@color/text_black"
android:textColorHint="@color/text_hint"
android:textSize="@dimen/font_small_3" />
</RelativeLayout>
<View
android:id="@+id/cardIdUnderLine"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/divide_line" />
<!-- card类型 -->
<LinearLayout
android:id="@+id/layout_card_type"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon_card_type"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/btn_cards" />
<TextView
android:id="@+id/tv_card_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_weight="1"
android:text="@string/str_card_type"
android:textColor="@color/text_black"
android:textSize="@dimen/font_small_3" />
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:scaleType="center"
android:src="@drawable/btn_up" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/divide_line" />
<View
android:layout_width="match_parent"
android:layout_height="12dp" />
</LinearLayout>
2.问题原因
在ViewGroup中 源码
private void addViewInner(View child, int index, LayoutParams params,
boolean preventRequestLayout) {
if (mTransition != null) {
// Don't prevent other add transitions from completing, but cancel remove
// transitions to let them complete the process before we add to the container
mTransition.cancel(LayoutTransition.DISAPPEARING);
}
if (child.getParent() != null) {
throw new IllegalStateException("The specified child already has a parent. " +
"You must call removeView() on the child's parent first.");
}
if (mTransition != null) {
mTransition.addChild(this, child);
}
if (!checkLayoutParams(params)) {
params = generateLayoutParams(params);
}
if (preventRequestLayout) {
child.mLayoutParams = params;
} else {
child.setLayoutParams(params);
}
if (index < 0) {
index = mChildrenCount;
}
addInArray(child, index);
// tell our children
if (preventRequestLayout) {
child.assignParent(this);
} else {
child.mParent = this;
}
//look here!!!!!!!!!!!!!!!! 看这!!!!!!!!!!!
if (child.hasFocus()) {
requestChildFocus(child, child.findFocus());
}
AttachInfo ai = mAttachInfo;
if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
boolean lastKeepOn = ai.mKeepScreenOn;
ai.mKeepScreenOn = false;
child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
if (ai.mKeepScreenOn) {
needGlobalAttributesUpdate(true);
}
ai.mKeepScreenOn = lastKeepOn;
}
**if (child.isLayoutDirectionInherited()) {
child.resetRtlProperties();
}**
dispatchViewAdded(child);
if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
}
if (child.hasTransientState()) {
childHasTransientStateChanged(child, true);
}
if (child.getVisibility() != View.GONE) {
notifySubtreeAccessibilityStateChangedIfNeeded();
}
if (mTransientIndices != null) {
final int transientCount = mTransientIndices.size();
for (int i = 0; i < transientCount; ++i) {
final int oldIndex = mTransientIndices.get(i);
if (index <= oldIndex) {
mTransientIndices.set(i, oldIndex + 1);
}
}
}
if (mCurrentDragStartEvent != null && child.getVisibility() == VISIBLE) {
notifyChildOfDragStart(child);
}
}
在recyclerView 中
@Override
public void requestChildFocus(View child, View focused) {
if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
// get item decor offsets w/o refreshing. If they are invalid, there will be another
// layout pass to fix them, then it is LayoutManager's responsibility to keep focused
// View in viewport.
final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
if (focusedLayoutParams instanceof LayoutParams) {
// if focused child has item decors, use them. Otherwise, ignore.
final LayoutParams lp = (LayoutParams) focusedLayoutParams;
if (!lp.mInsetsDirty) {
final Rect insets = lp.mDecorInsets;
mTempRect.left -= insets.left;
mTempRect.right += insets.right;
mTempRect.top -= insets.top;
mTempRect.bottom += insets.bottom;
}
}
offsetDescendantRectToMyCoords(focused, mTempRect);
offsetRectIntoDescendantCoords(child, mTempRect);
//look here!!!!!! 看这儿!!!!!!
requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);}
super.requestChildFocus(child, focused);
}
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
}
requestChildRectangleOnScreen 在recyclerView 中的具体实现
/**
* Requests that the given child of the RecyclerView be positioned onto the screen. This
* method can be called for both unfocusable and focusable child views. For unfocusable
* child views, focusedChildVisible is typically true in which case, layout manager
* makes the child view visible only if the currently focused child stays in-bounds of RV.
* @param parent The parent RecyclerView.
* @param child The direct child making the request.
* @param rect The rectangle in the child's coordinates the child
* wishes to be on the screen.
* @param immediate True to forbid animated or delayed scrolling,
* false otherwise
* @param focusedChildVisible Whether the currently focused view must stay visible.
* @return Whether the group scrolled to handle the operation
*/
public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
boolean immediate,
boolean focusedChildVisible) {
int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
immediate);
int dx = scrollAmount[0];
int dy = scrollAmount[1];
//here 滚动!!! 粗暴点 屏蔽这一段就不滚动了
if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
if (dx != 0 || dy != 0) {
if (immediate) {
parent.scrollBy(dx, dy);
} else {
parent.smoothScrollBy(dx, dy);
}
return true;
}
}
return false;
}
3.解决办法
自定义layoutManager
/**
* 为了解决recycle人View上添加的headView 中的EditText等控件获取了焦点导致RecyclerView 莫名滚动
*/
class FoucsLinearLayoutManager extends LinearLayoutManager {
public FoucsLinearLayoutManager(Context context) {
super(context);
}
public FoucsLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public FoucsLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
/**
* public boolean requestChildRectangleOnScreen (View child, Rect rectangle, boolean immediate)
* <p>
* 当组里的某个子视图需要被定位在屏幕的某个矩形范围时,调用此方法。重载此方法的ViewGroup可确认以下几点:
* <p>
* * 子项目将是组里的直系子项
* * 矩形将在子项目的坐标体系中
* 重载此方法的ViewGroup应该支持以下几点:
* * 若矩形已经是可见的,则没有东西会改变
* * 为使矩形区域全部可见,视图将可以被滚动显示
* 参数
* child 发出请求的子视图
* rectangle 子项目坐标系内的矩形,即此子项目希望在屏幕上的定位
* immediate 设为true,则禁止动画和平滑移动滚动条
* <p>
* 返回值
* 进行了滚动操作的这个组(group),是否处理此操作。
*
* @param parent
* @param child
* @param rect
* @param immediate
* @return
*/
@Override
public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, boolean immediate) {
//这里的child 是整个HeadView 而不是某个具体的editText
LogUtil.e("requestChildRectangleOnScreen()====> chlild==" + child.getId() + "parent==" + parent.getId());
return false;
}
@Override
public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, boolean immediate, boolean focusedChildVisible) {
//这里的child 是整个HeadView 而不是某个具体的editText
LogUtil.e("requestChildRectangleOnScreen( focusedChildVisible=)====> chlild==" + child.getId() + "parent==" + parent.getId());
return false;
}
}
替换掉原来的LInearLayoutManager
mRecyclerView = findViewById(view, R.id.recyclerView);
mRecyclerView.setLayoutManager(new FoucsLinearLayoutManager(getContext()));
参考:http://blog.csdn.net/qq_18480625/article/details/54923796