【问题标题】:onClick method not working properly after NestedScrollView scrolled在 NestedScrollView 滚动后,onClick 方法无法正常工作
【发布时间】:2015-10-28 01:54:45
【问题描述】:

我使用带有 CoordinatorLayout 的 NestedScrollView 来启用工具栏的滚动动画(通过 app:layout_scrollFlags="scroll|enterAlways")。

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

}

【问题讨论】:

  • 将 NestedScrollView 更改为 ScrollView 后一切正常,但我们错过了显示/隐藏工具栏的滚动动画
  • 同样的问题,你找到解决办法了吗?
  • 好像是google设计库的bug,我还是用ScrollView而不是NestedScrollView,而且动画丢失了。

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


【解决方案1】:

我在这个帖子上找到了相同问题的解决方案: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">

【讨论】:

  • 它解决了fling后的触摸问题,但正常的ACTION_MOVE在NestedScrollView内的水平RecyclerView上不起作用。不过感谢您的解决方案!
  • 伟大的研究...拯救了我的一天!
【解决方案2】:

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

此错误已报告了一段时间,但仍未修复。所以我已经为这个问题创建了自己的错误修复版本。我实现了自己的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 应该和原来的行为一样。

【讨论】:

  • 这又是一个问题。如果 NestedScrollView 正在弹奏,我们无法单击停止弹奏
【解决方案3】:

我在这里打开了另一个问题: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;
        }
    }
}

【讨论】:

  • 这很有用。为我工作。
【解决方案4】:

这是BugGoogle #issues 194398 的提及。

只需要使用这个扩展NestedScrollViewWorkaroundNestedScrollView.java类就好了,

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

【讨论】:

    【解决方案5】:

    最佳解决方案:

    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】:

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

      如果你使用的是androidX,那么

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

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

      【讨论】:

      • 正如@dustinb 在他的回答stackoverflow.com/a/46876273/2290950 中提到的那样,它与特定操作系统版本相关,不支持lib 问题。我正在使用所有库的最新版本,并在我的 Nexus 6p (oreo - 8.1) 上看到了这个问题。
      【解决方案7】:

      我也遇到过这个问题

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-02-19
        • 2022-08-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多