I used NestedScrollView with CoordinatorLayout to enable scroll animation for Toolbar (by app:layout_scrollFlags=\"scroll|enterAlways\").
NestedScrollView contain th
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.
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"
...>
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
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 ta..@graymeter.com'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;
}
}
}
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.
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.