J'ai utilisé NestedScrollView avec CoordinatorLayout pour activer l'animation de défilement pour la barre d'outils (par application: layout_scrollFlags = "scroll | enterAlways").
NestedScrollView contient LinearLayout en tant qu'enfant racine. J'ai placé les 2 TextViews dans LinearLayout pour activer les animations à développer/réduire. L'un était défini sur Visible Et l'autre sur Gone. Et commutation de la visibilité par l'événement onClick de LinearLayout
Normalement, tout fonctionne comme prévu, mais lorsque j'ai fait défiler le NestedScrollView L'événement onClick ne fonctionne pas correctement. J'ai besoin de double-clic après le défilement pour obtenir une animation de développement/réduction
Est-ce que quelqu'un a le même problème avec moi? Aidez-moi, s'il vous plaît
<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;
// gérer l'événement
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);
}
}
C'est un bogue de NestedScrollView, les détails du bogue peuvent être trouvés ici: issue . Le problème est que mScroller.isFinished()
dans onInterceptTouchEvent(MotionEvent ev)
ne retournera pas true
après une opération de lancement (même si le lancement est arrêté). Par conséquent, l'événement tactile est intercepté.
Ce bogue a été signalé pendant un certain temps, mais n'a toujours pas été corrigé. J'ai donc créé ma propre version du correctif pour ce problème. J'ai implémenté ma propre NestedScrollView
, copié tout le code de NestedScrollView
et comportant les modifications suivantes:
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;
...
}
}
}
}
Et cette NestedScrollView
devrait avoir le même comportement que l'original.
J'ai trouvé la solution au même problème sur ce fil de discussion: L'élément dans RecyclerView ne peut pas être cliqué juste après le défilement
Vous pouvez réparer votre code en ajoutant layout_behavior à votre AppBarLayout .Vous pouvez trouver le code ici Fixed AppBarLayout.Behavior . Ajoutez simplement cette classe à votre projet et corrigez votre code:
<Android.support.design.widget.AppBarLayout
Android:layout_width="match_parent"
app:layout_behavior="yourPackageName.FixAppBarLayoutBehavior"
Android:layout_height="wrap_content">
J'ai ouvert un autre numéro ici: https://issuetracker.google.com/issues/68103042 car cela semble toujours être un problème à Oreo pour nous (plusieurs périphériques, y compris des émulateurs).
Mon correctif (adapté des suggestions de ta .. @ graymeter.com à https://issuetracker.google.com/issues/37051723 ) ne nécessite pas de modification du code AOSP car il utilise la réflexion:
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;
}
}
}
C'est une mention Bug
à Google #issues 194398
.
Juste besoin d'utiliser cette classe WorkaroundNestedScrollView.Java
qui étend NestedScrollView
comme,
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);
}
}
Et dans yourlayout utilisez-le comme ça,
layout.xml
<com.yourpackagename.whatever.WorkaroundNestedScrollView
Android:layout_width="match_parent"
Android:layout_height="match_parent">
...
...
</com.yourpackagename.whatever.WorkaroundNestedScrollView>
Vous pouvez également trouver plus détails ici .
J'ai rencontré ce problème aussi
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);
}
}
}
}
et je modifie mon fichier xml, change paddingTop en margin_top, puis l'événement OnClick de ma vue flottante supérieure ne sera pas intercepté par NestedScrollView
Meilleure solution :
1) Créer cette classe:
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) Et utiliser en XML:
<Android.support.design.widget.AppBarLayout
...
app:layout_behavior="yourPackageName.FixAppBarLayoutBehavior"
...>