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

问题描述 投票:35回答:7

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

NestedScrollView包含LinearLayout作为根子项,我将2个TextViews放入LinearLayout以启用展开/折叠动画。一个设置为Visible,另一个设置为Gone。并通过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);
    }

}
android android-layout android-design-library
7个回答
18
投票

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

这个bug已经报道了一段时间,但仍然没有修复。所以我已经为这个问题创建了自己的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应该具有与原始相同的行为。


32
投票

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

你可以通过将layout_behavior添加到你的AppBarLayout来修复你的代码。你可以在这里找到代码Fixed AppBarLayout.Behavior .Just添加这个类来触摸你的项目并修复你的代码:

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


8
投票

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

我的修复(改编自ta .. @ graymeter.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;
        }
    }
}

1
投票

这是Bug提到的Google #issues 194398

只需要使用这个扩展WorkaroundNestedScrollView.javaNestedScrollView类,

workaround nested scroll view.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


1
投票

最佳方案:

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"
...>

1
投票

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

如果你正在使用androidX

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

虽然这是一个alpha版本,但可能会解决这个问题。


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拦截

© www.soinside.com 2019 - 2024. All rights reserved.