滚动NestedScrollView后,onClick方法无法正常工作

时间:2015-08-05 10:31:58

标签: android android-layout android-design-library

我使用NestedScrollView和CoordinatorLayout为工具栏启用滚动动画(通过app:layout_scrollFlags =“scroll | enterAlways”)。

NestedScrollView包含LinearLayout作为根子项,我将2个TextViews放入LinearLayout以启用展开/折叠动画。一个是可见的 其他人也准备好了。并通过LinearLayout的onClick事件切换可见性

通常,一切都按预期工作,但是当我滚动NestedScrollView时 onClick事件无法正常工作。我需要在滚动后双击以获得展开/折叠动画

有没有人和我有同样的问题?请帮帮我

<android.support.design.widget.CoordinatorLayout 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.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="98dp"
        android:paddingLeft="24dp"
        android:paddingRight="24dp">

        <android.support.v7.widget.AppCompatTextView
            android:id="@+id/detail_expense_reason_trim"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="false"
            android:textColor="@color/add_new_expense_text_color" />

        <android.support.v7.widget.AppCompatTextView
            android:id="@+id/detail_expense_reason"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="false"
            android:textColor="@color/add_new_expense_text_color"
            android:visibility="gone" />
    </LinearLayout>

</android.support.v4.widget.NestedScrollView>

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.Toolbar
        android:id="@+id/detail_expense_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:layout_scrollFlags="scroll|enterAlways"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout>

 @InjectView(R.id.detail_expense_reason)
AppCompatTextView originalReason;

@InjectView(R.id.detail_expense_reason_trim)
AppCompatTextView trimReason;

@InjectView(R.id.detail_expense_container)
LinearLayout expenseContainer;

//处理事件

public void onClick() {
    if (originalReason.getVisibility() == View.VISIBLE) {
        originalReason.setVisibility(View.GONE);
        trimReason.setVisibility(View.VISIBLE);
    } else {
        originalReason.setVisibility(View.VISIBLE);
        trimReason.setVisibility(View.GONE);
    }

}

7 个答案:

答案 0 :(得分:32)

我在这个帖子上找到了同样问题的解决方案:The item inside RecyclerView can't be clicked right after scrolling

您可以通过将layout_behavior添加到 AppBarLayout 来修复代码。您可以在此处找到代码Fixed AppBarLayout.Behavior 。只需在此项目中添加项目并修改代码:

<android.support.design.widget.AppBarLayout android:layout_width="match_parent" app:layout_behavior="yourPackageName.FixAppBarLayoutBehavior" android:layout_height="wrap_content">

答案 1 :(得分:18)

这是NestedScrollView的一个错误,可以在这里找到错误的详细信息:issue。问题是,mScroller.isFinished()中的onInterceptTouchEvent(MotionEvent ev)在投掷操作后不会返回true(即使停止投掷)。因此触发事件被截获。

此错误已报告一段时间,但仍未修复。所以我已经为这个问题创建了自己的bug修复版本。我实现了自己的NestedScrollView,复制了NestedScrollView中的所有代码并进行了以下修改:

public class NestedScrollView extends FrameLayout implements NestedScrollingParent, NestedScrollingChild {
    ...
    private void initScrollView() {
        ...
        // replace this line:
        // mScroller = new ScrollerCompat(getContext(), null);
        mScroller = ScrollerCompat.create(getContext(), null);
        ...
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        ...
        switch (action & MotionEventCompat.ACTION_MASK) {
            ...
            case MotionEvent.ACTION_DOWN: {
                ...
                // replace this line:
                // mIsBeingDragged = !mScroller.isFinished();
                mIsBeingDragged = false;
                ...
            }
        }
    }   
}

NestedScrollView应与原始行为具有相同的行为。

答案 2 :(得分:8)

我在这里打开了另一个问题:https://issuetracker.google.com/issues/68103042因为它似乎仍然是Oreo中的一个问题(多个设备,包括模拟器)。

我的修复(改编自@ {whitemeter.com在https://issuetracker.google.com/issues/37051723的建议)不需要修改AOSP代码,因为它使用反射:

public class MyNestedScrollView extends NestedScrollView {

    private static final Logger sLogger = LogFactory.getLogger(MyNestedScrollView.class);

    private OverScroller mScroller;
    public boolean isFling = false;

    public MyNestedScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = getOverScroller();
    }

    @Override
    public void fling(int velocityY) {
        super.fling(velocityY);

        // here we effectively extend the super class functionality for backwards compatibility and just call invalidateOnAnimation()
        if (getChildCount() > 0) {
            ViewCompat.postInvalidateOnAnimation(this);

            // Initializing isFling to true to track fling action in onScrollChanged() method
            isFling = true;
        }
    }

    @Override
    protected void onScrollChanged(int l, final int t, final int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

        if (isFling) {
            if (Math.abs(t - oldt) <= 3 || t == 0 || t == (getChildAt(0).getMeasuredHeight() - getMeasuredHeight())) {
                isFling = false;

                // This forces the mFinish variable in scroller to true (as explained the
                //    mentioned link above) and does the trick
                if (mScroller != null) {
                    mScroller.abortAnimation();
                }
            }
        }
    }

    private OverScroller getOverScroller() {
        Field fs = null;
        try {
            fs = this.getClass().getSuperclass().getDeclaredField("mScroller");
            fs.setAccessible(true);
            return (OverScroller) fs.get(this);
        } catch (Throwable t) {
            return null;
        }
    }
}

答案 3 :(得分:1)

Bug提及Google #issues 194398

只需要使用WorkaroundNestedScrollView.java类扩展NestedScrollView类,

WorkaroundNestedScrollView.java

public class WorkaroundNestedScrollView extends NestedScrollView {

public WorkaroundNestedScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        // Explicitly call computeScroll() to make the Scroller compute itself
        computeScroll();
    }
    return super.onInterceptTouchEvent(ev);
}
}

在你的布局中使用它,

layout.xml

<com.yourpackagename.whatever.WorkaroundNestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">
...
...

</com.yourpackagename.whatever.WorkaroundNestedScrollView>

您可以找到更多details here

答案 4 :(得分:0)

我也遇到了这个问题

    public class NestedScrollView extends FrameLayout implements NestedScrollingParent,
    NestedScrollingChild, ScrollingView {
       @Override
        public boolean onTouchEvent(MotionEvent ev) {
          switch (actionMasked) {
            case MotionEvent.ACTION_DOWN: {
                if (getChildCount() == 0) {
                    return false;
                }
                //add this line
                if (!inChild((int) ev.getX(), (int) ev.getY())) {
                    return false;
                }
                if ((mIsBeingDragged = !mScroller.isFinished())) {
                        final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
           }
      }

我更改了我的xml文件,将paddingTop更改为margin_top,然后我的顶级浮动视图的OnClick事件将不会被NestedScrollView拦截

答案 5 :(得分:0)

最佳解决方案:

1)创建此类:

public class FixAppBarLayoutBehavior extends AppBarLayout.Behavior {

public FixAppBarLayoutBehavior() {
    super();
}

public FixAppBarLayoutBehavior(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target,
        int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
    super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
            dxUnconsumed, dyUnconsumed, type);
    stopNestedScrollIfNeeded(dyUnconsumed, child, target, type);
}

@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
        View target, int dx, int dy, int[] consumed, int type) {
    super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
    stopNestedScrollIfNeeded(dy, child, target, type);
}

private void stopNestedScrollIfNeeded(int dy, AppBarLayout child, View target, int type) {
    if (type == ViewCompat.TYPE_NON_TOUCH) {
        final int currOffset = getTopAndBottomOffset();
        if ((dy < 0 && currOffset == 0)
                || (dy > 0 && currOffset == -child.getTotalScrollRange())) {
            ViewCompat.stopNestedScroll(target, ViewCompat.TYPE_NON_TOUCH);
        }
    }
}}

2)并在xml中使用:

<android.support.design.widget.AppBarLayout
...
app:layout_behavior="yourPackageName.FixAppBarLayoutBehavior"
...>

答案 6 :(得分:0)

这是支持库中的一个问题。看到这个 https://issuetracker.google.com/u/1/issues/37070828

如果您使用的是androidX,则

'androidx.appcompat:appcompat:1.1.0-alpha04'.

这可能是解决此问题的方法,尽管这是Alpha版。