onClick method not working properly after NestedScrollView scrolled
Asked Answered
A

8

41

I used NestedScrollView with CoordinatorLayout to enable scroll animation for Toolbar (by app:layout_scrollFlags="scroll|enterAlways").

NestedScrollView contain the LinearLayout as the root child, I put the 2 TextViews into LinearLayout to enable expand/collapse animation. The one was set Visible and other one was set to Gone. And switching visibility by onClick event of LinearLayout

Normally, everything work as expected but when I scrolled the NestedScrollView the onClick event not working properly. I need double click after scroll to get expand/collapse animation

Does anyone have same problem with me ? Please help me

<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;

// Handle event

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);
    }

}
Appease answered 5/8, 2015 at 10:31 Comment(5)
Everything work well after change NestedScrollView to ScrollView, But we miss scrolled animation to show/hide toolbarAppease
Same problem here, did you found a solution?Anana
It seems to be google design library bugs, I still use ScrollView instead of NestedScrollView and losing the animation.Appease
Reported the bug on code.google.com/p/android/issues/detail?id=184028Splint
this worked for me ,just put OnClickListener on the child layout like Linear or Constraint Layout not on scroll viewHilliary
P
19

It is a bug of the NestedScrollView, the detail of the bug can be found in here: issue. The problem is that mScroller.isFinished() in onInterceptTouchEvent(MotionEvent ev) will not return true after a fling operation (even if the fling is stopped). Therefore the touch event is intercepted.

This bug have been reported for a while, but still have not been fixed. So I have created by own version of bug fix for this problem. I implemented my own NestedScrollView, copied all the code from NestedScrollView and having the with the following amendments:

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;
                ...
            }
        }
    }   
}

And this NestedScrollView should have the same behaviour as the original one.

Puglia answered 25/9, 2015 at 13:39 Comment(1)
It makes another problem. If the NestedScrollView is fling ,we cannot click to stop the flingHemielytron
B
33

I found solution for same problem on this thread :The item inside RecyclerView can't be clicked right after scrolling

You can fix your code by adding layout_behavior to your AppBarLayout.You can find code here Fixed AppBarLayout.Behavior .Just add this class tou your project and fix your code :

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

Brassware answered 7/11, 2017 at 15:32 Comment(2)
It solves the touch issue after a fling but normal ACTION_MOVE does not work on horizontal RecyclerView inside NestedScrollView. Thanks for the solution though!!Gunar
Great Research... Saved my day!Morphogenesis
P
19

It is a bug of the NestedScrollView, the detail of the bug can be found in here: issue. The problem is that mScroller.isFinished() in onInterceptTouchEvent(MotionEvent ev) will not return true after a fling operation (even if the fling is stopped). Therefore the touch event is intercepted.

This bug have been reported for a while, but still have not been fixed. So I have created by own version of bug fix for this problem. I implemented my own NestedScrollView, copied all the code from NestedScrollView and having the with the following amendments:

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;
                ...
            }
        }
    }   
}

And this NestedScrollView should have the same behaviour as the original one.

Puglia answered 25/9, 2015 at 13:39 Comment(1)
It makes another problem. If the NestedScrollView is fling ,we cannot click to stop the flingHemielytron
L
10

I've opened another issue here: https://issuetracker.google.com/issues/68103042 as it still seems to be an issue in Oreo for us (multiple devices, including emulators).

My fix (adapted from [email protected]'s suggestions at https://issuetracker.google.com/issues/37051723) doesn't require modifying AOSP code as it uses reflection:

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;
        }
    }
}
Llanes answered 22/10, 2017 at 16:8 Comment(1)
This is useful .Worked for me .Langland
S
1

It is a Bug mention at Google #issues 194398.

Just need to use this WorkaroundNestedScrollView.java class which extends NestedScrollView like,

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);
}
}

And in yourlayout use it like this,

layout.xml

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

</com.yourpackagename.whatever.WorkaroundNestedScrollView>

You can alson find more details here.

Shear answered 31/12, 2015 at 13:32 Comment(0)
S
1

Best solution :

1) Create this class :

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) And use in xml :

<android.support.design.widget.AppBarLayout
...
app:layout_behavior="yourPackageName.FixAppBarLayoutBehavior"
...>
Sherburne answered 16/10, 2018 at 17:39 Comment(0)
S
1

This was an issue in support libraries. see this https://issuetracker.google.com/u/1/issues/37070828

If you are using androidX then

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

will probably fix this issue though this is an alpha build.

Sustentation answered 28/4, 2019 at 8:56 Comment(1)
as @dustinb mentioned in his answer https://mcmap.net/q/388084/-onclick-method-not-working-properly-after-nestedscrollview-scrolled it issue related to specific OS versions, not support lib issue. I'm on the latest versions of all libs and see this issue on my Nexus 6p (oreo - 8.1).Sciurine
I
0

I have same issue coming,i had recyclerview and i was trying to collapse and expand, So when i do collapse, my first click do not work.. but when i do research on it and i found that i was doing scroll up my nestedScrollView on collapsing my recycleview.

 this.nestedScrollView.post {
            nestedScrollView.fling(0)
            nestedScrollView.smoothScrollBy(
                0, - 100dp),
                1000ms
            )
        }

So this fling up and scrolling up code is blocking my click listener for 1000ms. So when i handled this duration value my problem is solved :)

Icefall answered 4/7, 2022 at 15:23 Comment(0)
T
-1

I met this problem too

    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);
                    }
                }
           }
      }

and I change my xml file,change paddingTop to margin_top,then my top floating view's OnClick event will not be intercepted by NestedScrollView

Tillery answered 25/10, 2016 at 2:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.