web-dev-qa-db-ja.com

NestedScrollViewがスクロールした後、onClickメソッドが正しく機能しない

NestedScrollViewとCoordinatorLayoutを使用して、ツールバーのスクロールアニメーションを有効にしました(app:layout_scrollFlags = "scroll | enterAlways"による)。

NestedScrollViewには、ルートの子としてLinearLayoutが含まれています。2つのTextViewをLinearLayoutに配置して、展開/折りたたみアニメーションを有効にします。 1つはVisibleに設定され、もう1つは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);
    }

}
37
toidv

これはNestedScrollViewのバグです。バグの詳細は、ここで見つけることができます: issue 。問題は、mScroller.isFinished()onInterceptTouchEvent(MotionEvent ev)が、フリング操作後に(フリングが停止していても)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は元のものと同じ振る舞いを持つべきです。

19
Chris

このスレッドで同じ問題の解決策を見つけました: RecyclerView内のアイテムはスクロール直後にクリックできません

コードを修正するには、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">

32
Mihuilk

私はここで別の問題を開きました: https://issuetracker.google.com/issues/68103042 それは私たちにとってはまだOreoの問題のようです(エミュレータを含む複数のデバイス)。

私の修正(ta .. @ graymeter.comの https://issuetracker.google.com/issues/3705172 の提案から適応)は、リフレクションを使用するため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;
        }
    }
}
8
DustinB

Google #issues 194398Bugに言及しています。

NestedScrollViewのようなWorkaroundNestedScrollView.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>

また、さらに多くの 詳細はこちら を見つけることもできます。

1
Jaydipsinh Zala

最適なソリューション:

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
Fidan Bacaj

これはサポートライブラリの問題でした。こちらをご覧ください https://issuetracker.google.com/u/1/issues/37070828

AndroidXを使用している場合

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

これはおそらくアルファビルドですが、おそらくこの問題を修正します。

1
shine_joseph

私もこの問題に出会いました

    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によってインターセプトされません。

0
vivianChen