Scrollbewegung animieren

Schreiben Sie jetzt
Jetpack Compose ist das empfohlene UI-Toolkit für Android. Weitere Informationen zur Verwendung von Berührungen und Eingaben in „Schreiben“
<ph type="x-smartling-placeholder"></ph> Scrollen →

In Android erfolgt das Scrollen in der Regel mithilfe der ScrollView . Verschachteln Sie jedes Standardlayout, das über die Grenzen seiner Container in einem ScrollView für eine scrollbare Ansicht, die von des Frameworks. Die Implementierung eines benutzerdefinierten Scroller ist nur bei speziellen Szenarien durchführen. In diesem Dokument wird beschrieben, wie ein Scrolleffekt als Reaktion darauf angezeigt wird. um Touch-Gesten mit Scrollen zu berühren.

Deine App kann Folgendes verwenden: Scrollers: Scroller oder OverScroller – bis Sie sammeln die Daten, die zum Erstellen einer Scroll-Animation als Reaktion auf eine Berührung erforderlich sind. . Sie sind ähnlich, aber OverScroller enthält auch Methoden zum Hinweis für Nutzer, wenn sie die Inhaltsränder nach einem Schwenken oder Wischen erreichen Touch-Geste.

  • Ab Android 12 (API-Level 31) dehnen sich die visuellen Elemente aus auf ein Drag-Event zurück.
  • Unter Android 11 (API-Level 30) und niedriger sind die Grenzen mit einem Schein gekennzeichnet nach einem Ziehen oder Ziehen an den Rand.

Im Beispiel InteractiveChart in diesem Dokument wird die Methode EdgeEffect , um diese Overscroll-Effekte anzuzeigen.

Sie können einen Scroller verwenden, um das Scrollen mit der Zeit zu animieren. plattformüblichen Standards für das Scrollen, wie z. B. Reibung, Geschwindigkeit Qualitäten zu verstehen. Der Scroller selbst zeichnet nichts. Scroller erfassen Scrollen im Laufe der Zeit für Sie, aber sie werden diese Positionen nicht automatisch auf Ihre Ansicht anpassen. Sie müssen neue Koordinaten mit einer Geschwindigkeit abrufen und anwenden, damit die Scrollanimation flüssig aussieht.

Terminologie für das Scrollen

Scrollen kann in Android unterschiedliche Bedeutungen haben. Kontext.

Beim Scrollen wird der Darstellungsbereich in der Regel verschoben, d. h. das „Fenster“ des Inhalts, den Sie sich ansehen. Wenn das Scrollen sowohl in der x- und y-Achsen wird als Schwenken bezeichnet. Die In diesem Dokument sind zwei Beispiele für InteractiveChart-Beispiel-Apps zu sehen: verschiedene Arten von Scrollen, Ziehen und Ziehen:

  • Ziehen: Dies ist die Art von Scrollen, bei der ein Nutzer und zieht den Finger über den Touchscreen. Sie können das Ziehen implementieren, indem Sie wird überschrieben onScroll() in GestureDetector.OnGestureListener. Weitere Informationen zum Ziehen finden Sie unter Ziehen und skalieren:
  • Flingen:Dies ist die Art von Scrollen, bei der ein Nutzer und hebt den Finger schnell an. Nachdem die Nutzenden den Finger angehoben haben, den Darstellungsbereich in der Regel weiter bewegen, aber verlangsamen, bis der bewegt sich der Darstellungsbereich nicht mehr. Sie können Flinging implementieren, indem Sie onFling() in GestureDetector.OnGestureListener und mit einem Bildlaufleister -Objekt enthält.
  • Schwenken: Scrollen Sie gleichzeitig auf dem x- und Eine y-Achse wird als Schwenken bezeichnet.

Es ist üblich, Scroller-Objekte in Verbindung mit einer Fingerbewegung zu verwenden, aber Sie können sie in jedem Kontext verwenden, in dem die Benutzeroberfläche das Scrollen anzeigen soll. auf ein Touch-Ereignis reagieren. So können Sie zum Beispiel onTouchEvent() um Touch-Ereignisse direkt zu verarbeiten und einen Scrolleffekt oder einen "An Seite ausrichten" als Reaktion auf diese Touch-Ereignisse.

Komponenten mit integrierten Scrolling-Implementierungen

Die folgenden Android-Komponenten enthalten integrierte Unterstützung für Scrollen und Overscrolling-Verhalten:

Wenn Ihre App das Scrollen und Overscrolling innerhalb einer anderen Komponente führen Sie die folgenden Schritte aus:

  1. Benutzerdefiniertes berührungsbasiertes Scrollen erstellen Implementierung.
  2. Wenn Sie Geräte mit Android 12 und höher unterstützen möchten, die Stretch-Overscroll-Funktion .

Benutzerdefinierte Implementierung für das berührungsbasierte Scrollen erstellen

In diesem Abschnitt wird beschrieben, wie Sie Ihren eigenen Scroller erstellen, wenn Ihre App einen Komponente, die nicht integrierte Unterstützung für und Overscrolling.

Das folgende Snippet stammt aus der InteractiveChart Beispiel. Dabei wird ein GestureDetector und überschreibt die GestureDetector.SimpleOnGestureListener onFling()-Methode. OverScroller wird verwendet, um Fingergeste. Wenn Nutzende die Inhaltsränder erreichen, nachdem sie die Fling-Geste zeigt der Container an, wann der Nutzer das Ende des Inhalte. Die Anzeige hängt von der Android-Version ab, auf der ein Gerät ausgeführt:

  • Unter Android 12 und höher werden die visuellen Elemente zu reflektieren.
  • Unter Android 11 und niedriger werden die visuellen Elemente leuchtend hervorgehoben. Effekts.
<ph type="x-smartling-placeholder">

Der erste Teil des folgenden Snippets zeigt die Implementierung eines onFling():

Kotlin

// Viewport extremes. See currentViewport for a discussion of the viewport.
private val AXIS_X_MIN = -1f
private val AXIS_X_MAX = 1f
private val AXIS_Y_MIN = -1f
private val AXIS_Y_MAX = 1f

// The current viewport. This rectangle represents the visible chart
// domain and range. The viewport is the part of the app that the
// user manipulates via touch gestures.
private val currentViewport = RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX)

// The current destination rectangle—in pixel coordinates—into which
// the chart data must be drawn.
private lateinit var contentRect: Rect

private lateinit var scroller: OverScroller
private lateinit var scrollerStartViewport: RectF
...
private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {

    override fun onDown(e: MotionEvent): Boolean {
        // Initiates the decay phase of any active edge effects.
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
            releaseEdgeEffects()
        }
        scrollerStartViewport.set(currentViewport)
        // Aborts any active scroll animations and invalidates.
        scroller.forceFinished(true)
        ViewCompat.postInvalidateOnAnimation(this@InteractiveLineGraphView)
        return true
    }
    ...
    override fun onFling(
            e1: MotionEvent,
            e2: MotionEvent,
            velocityX: Float,
            velocityY: Float
    ): Boolean {
        fling((-velocityX).toInt(), (-velocityY).toInt())
        return true
    }
}

private fun fling(velocityX: Int, velocityY: Int) {
    // Initiates the decay phase of any active edge effects.
    // On Android 12 and later, the edge effect (stretch) must
    // continue.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
            releaseEdgeEffects()
    }
    // Flings use math in pixels, as opposed to math based on the viewport.
    val surfaceSize: Point = computeScrollSurfaceSize()
    val (startX: Int, startY: Int) = scrollerStartViewport.run {
        set(currentViewport)
        (surfaceSize.x * (left - AXIS_X_MIN) / (AXIS_X_MAX - AXIS_X_MIN)).toInt() to
                (surfaceSize.y * (AXIS_Y_MAX - bottom) / (AXIS_Y_MAX - AXIS_Y_MIN)).toInt()
    }
    // Before flinging, stops the current animation.
    scroller.forceFinished(true)
    // Begins the animation.
    scroller.fling(
            // Current scroll position.
            startX,
            startY,
            velocityX,
            velocityY,
            /*
             * Minimum and maximum scroll positions. The minimum scroll
             * position is generally 0 and the maximum scroll position
             * is generally the content size less the screen size. So if the
             * content width is 1000 pixels and the screen width is 200
             * pixels, the maximum scroll offset is 800 pixels.
             */
            0, surfaceSize.x - contentRect.width(),
            0, surfaceSize.y - contentRect.height(),
            // The edges of the content. This comes into play when using
            // the EdgeEffect class to draw "glow" overlays.
            contentRect.width() / 2,
            contentRect.height() / 2
    )
    // Invalidates to trigger computeScroll().
    ViewCompat.postInvalidateOnAnimation(this)
}

Java

// Viewport extremes. See currentViewport for a discussion of the viewport.
private static final float AXIS_X_MIN = -1f;
private static final float AXIS_X_MAX = 1f;
private static final float AXIS_Y_MIN = -1f;
private static final float AXIS_Y_MAX = 1f;

// The current viewport. This rectangle represents the visible chart
// domain and range. The viewport is the part of the app that the
// user manipulates via touch gestures.
private RectF currentViewport =
  new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX);

// The current destination rectangle—in pixel coordinates—into which
// the chart data must be drawn.
private final Rect contentRect = new Rect();

private final OverScroller scroller;
private final RectF scrollerStartViewport =
  new RectF(); // Used only for zooms and flings.
...
private final GestureDetector.SimpleOnGestureListener gestureListener
        = new GestureDetector.SimpleOnGestureListener() {
    @Override
    public boolean onDown(MotionEvent e) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
            releaseEdgeEffects();
        }
        scrollerStartViewport.set(currentViewport);
        scroller.forceFinished(true);
        ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this);
        return true;
    }
...
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        fling((int) -velocityX, (int) -velocityY);
        return true;
    }
};

private void fling(int velocityX, int velocityY) {
    // Initiates the decay phase of any active edge effects.
    // On Android 12 and later, the edge effect (stretch) must
    // continue.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
            releaseEdgeEffects();
    }
    // Flings use math in pixels, as opposed to math based on the viewport.
    Point surfaceSize = computeScrollSurfaceSize();
    scrollerStartViewport.set(currentViewport);
    int startX = (int) (surfaceSize.x * (scrollerStartViewport.left -
            AXIS_X_MIN) / (
            AXIS_X_MAX - AXIS_X_MIN));
    int startY = (int) (surfaceSize.y * (AXIS_Y_MAX -
            scrollerStartViewport.bottom) / (
            AXIS_Y_MAX - AXIS_Y_MIN));
    // Before flinging, stops the current animation.
    scroller.forceFinished(true);
    // Begins the animation.
    scroller.fling(
            // Current scroll position.
            startX,
            startY,
            velocityX,
            velocityY,
            /*
             * Minimum and maximum scroll positions. The minimum scroll
             * position is generally 0 and the maximum scroll position
             * is generally the content size less the screen size. So if the
             * content width is 1000 pixels and the screen width is 200
             * pixels, the maximum scroll offset is 800 pixels.
             */
            0, surfaceSize.x - contentRect.width(),
            0, surfaceSize.y - contentRect.height(),
            // The edges of the content. This comes into play when using
            // the EdgeEffect class to draw "glow" overlays.
            contentRect.width() / 2,
            contentRect.height() / 2);
    // Invalidates to trigger computeScroll().
    ViewCompat.postInvalidateOnAnimation(this);
}

Wenn onFling() anruft postInvalidateOnAnimation(), es wird ausgelöst computeScroll() um die Werte für x und y zu aktualisieren. Dies geschieht in der Regel, wenn ein „view untergeordnetes Element“ eine Scrollbewegung mithilfe eines Scroller-Objekts animiert, wie oben Beispiel.

Bei den meisten Ansichten wird die x- und y-Position des Scroller-Objekts direkt übergeben. bis scrollTo() Die folgende Implementierung von computeScroll() ändert sich Ansatz: Es ruft computeScrollOffset() um den aktuellen Standort von x und y zu ermitteln. Wenn die Kriterien für Overscroll-Effekt erfüllt sind, d. h. die Anzeige herangezoomt ist, x oder y außerhalb des gültigen Bereichs liegt und die App nicht bereits Overscroll – der Code richtet den Overscroll-Leuchteffekt ein ruft postInvalidateOnAnimation() auf, um eine Entwertung im Ansicht.

Kotlin

// Edge effect/overscroll tracking objects.
private lateinit var edgeEffectTop: EdgeEffect
private lateinit var edgeEffectBottom: EdgeEffect
private lateinit var edgeEffectLeft: EdgeEffect
private lateinit var edgeEffectRight: EdgeEffect

private var edgeEffectTopActive: Boolean = false
private var edgeEffectBottomActive: Boolean = false
private var edgeEffectLeftActive: Boolean = false
private var edgeEffectRightActive: Boolean = false

override fun computeScroll() {
    super.computeScroll()

    var needsInvalidate = false

    // The scroller isn't finished, meaning a fling or
    // programmatic pan operation is active.
    if (scroller.computeScrollOffset()) {
        val surfaceSize: Point = computeScrollSurfaceSize()
        val currX: Int = scroller.currX
        val currY: Int = scroller.currY

        val (canScrollX: Boolean, canScrollY: Boolean) = currentViewport.run {
            (left > AXIS_X_MIN || right < AXIS_X_MAX) to (top > AXIS_Y_MIN || bottom < AXIS_Y_MAX)
        }

        /*
         * If you are zoomed in, currX or currY is
         * outside of bounds, and you aren't already
         * showing overscroll, then render the overscroll
         * glow edge effect.
         */
        if (canScrollX
                && currX < 0
                && edgeEffectLeft.isFinished
                && !edgeEffectLeftActive) {
            edgeEffectLeft.onAbsorb(scroller.currVelocity.toInt())
            edgeEffectLeftActive = true
            needsInvalidate = true
        } else if (canScrollX
                && currX > surfaceSize.x - contentRect.width()
                && edgeEffectRight.isFinished
                && !edgeEffectRightActive) {
            edgeEffectRight.onAbsorb(scroller.currVelocity.toInt())
            edgeEffectRightActive = true
            needsInvalidate = true
        }

        if (canScrollY
                && currY < 0
                && edgeEffectTop.isFinished
                && !edgeEffectTopActive) {
            edgeEffectTop.onAbsorb(scroller.currVelocity.toInt())
            edgeEffectTopActive = true
            needsInvalidate = true
        } else if (canScrollY
                && currY > surfaceSize.y - contentRect.height()
                && edgeEffectBottom.isFinished
                && !edgeEffectBottomActive) {
            edgeEffectBottom.onAbsorb(scroller.currVelocity.toInt())
            edgeEffectBottomActive = true
            needsInvalidate = true
        }
        ...
    }
}

Java

// Edge effect/overscroll tracking objects.
private EdgeEffectCompat edgeEffectTop;
private EdgeEffectCompat edgeEffectBottom;
private EdgeEffectCompat edgeEffectLeft;
private EdgeEffectCompat edgeEffectRight;

private boolean edgeEffectTopActive;
private boolean edgeEffectBottomActive;
private boolean edgeEffectLeftActive;
private boolean edgeEffectRightActive;

@Override
public void computeScroll() {
    super.computeScroll();

    boolean needsInvalidate = false;

    // The scroller isn't finished, meaning a fling or
    // programmatic pan operation is active.
    if (scroller.computeScrollOffset()) {
        Point surfaceSize = computeScrollSurfaceSize();
        int currX = scroller.getCurrX();
        int currY = scroller.getCurrY();

        boolean canScrollX = (currentViewport.left > AXIS_X_MIN
                || currentViewport.right < AXIS_X_MAX);
        boolean canScrollY = (currentViewport.top > AXIS_Y_MIN
                || currentViewport.bottom < AXIS_Y_MAX);

        /*
         * If you are zoomed in, currX or currY is
         * outside of bounds, and you aren't already
         * showing overscroll, then render the overscroll
         * glow edge effect.
         */
        if (canScrollX
                && currX < 0
                && edgeEffectLeft.isFinished()
                && !edgeEffectLeftActive) {
            edgeEffectLeft.onAbsorb((int)mScroller.getCurrVelocity());
            edgeEffectLeftActive = true;
            needsInvalidate = true;
        } else if (canScrollX
                && currX > (surfaceSize.x - contentRect.width())
                && edgeEffectRight.isFinished()
                && !edgeEffectRightActive) {
            edgeEffectRight.onAbsorb((int)mScroller.getCurrVelocity());
            edgeEffectRightActive = true;
            needsInvalidate = true;
        }

        if (canScrollY
                && currY < 0
                && edgeEffectTop.isFinished()
                && !edgeEffectTopActive) {
            edgeEffectRight.onAbsorb((int)mScroller.getCurrVelocity());
            edgeEffectTopActive = true;
            needsInvalidate = true;
        } else if (canScrollY
                && currY > (surfaceSize.y - contentRect.height())
                && edgeEffectBottom.isFinished()
                && !edgeEffectBottomActive) {
            edgeEffectRight.onAbsorb((int)mScroller.getCurrVelocity());
            edgeEffectBottomActive = true;
            needsInvalidate = true;
        }
        ...
    }

Dies ist der Abschnitt des Codes, der den tatsächlichen Zoom durchführt:

Kotlin

lateinit var zoomer: Zoomer
val zoomFocalPoint = PointF()
...
// If a zoom is in progress—either programmatically
// or through double touch—this performs the zoom.
if (zoomer.computeZoom()) {
    val newWidth: Float = (1f - zoomer.currZoom) * scrollerStartViewport.width()
    val newHeight: Float = (1f - zoomer.currZoom) * scrollerStartViewport.height()
    val pointWithinViewportX: Float =
            (zoomFocalPoint.x - scrollerStartViewport.left) / scrollerStartViewport.width()
    val pointWithinViewportY: Float =
            (zoomFocalPoint.y - scrollerStartViewport.top) / scrollerStartViewport.height()
    currentViewport.set(
            zoomFocalPoint.x - newWidth * pointWithinViewportX,
            zoomFocalPoint.y - newHeight * pointWithinViewportY,
            zoomFocalPoint.x + newWidth * (1 - pointWithinViewportX),
            zoomFocalPoint.y + newHeight * (1 - pointWithinViewportY)
    )
    constrainViewport()
    needsInvalidate = true
}
if (needsInvalidate) {
    ViewCompat.postInvalidateOnAnimation(this)
}

Java

// Custom object that is functionally similar to Scroller.
Zoomer zoomer;
private PointF zoomFocalPoint = new PointF();
...
// If a zoom is in progress—either programmatically
// or through double touch—this performs the zoom.
if (zoomer.computeZoom()) {
    float newWidth = (1f - zoomer.getCurrZoom()) *
            scrollerStartViewport.width();
    float newHeight = (1f - zoomer.getCurrZoom()) *
            scrollerStartViewport.height();
    float pointWithinViewportX = (zoomFocalPoint.x -
            scrollerStartViewport.left)
            / scrollerStartViewport.width();
    float pointWithinViewportY = (zoomFocalPoint.y -
            scrollerStartViewport.top)
            / scrollerStartViewport.height();
    currentViewport.set(
            zoomFocalPoint.x - newWidth * pointWithinViewportX,
            zoomFocalPoint.y - newHeight * pointWithinViewportY,
            zoomFocalPoint.x + newWidth * (1 - pointWithinViewportX),
            zoomFocalPoint.y + newHeight * (1 - pointWithinViewportY));
    constrainViewport();
    needsInvalidate = true;
}
if (needsInvalidate) {
    ViewCompat.postInvalidateOnAnimation(this);
}

Dies ist die Methode computeScrollSurfaceSize(), die in aus dem vorherigen Snippet. Sie berechnet die aktuelle scrollbare Oberfläche Pixel. Ist beispielsweise der gesamte Diagrammbereich sichtbar, ist dies der aktuelle Bereich Größe von mContentRect. Wenn das Diagramm in beiden Fällen um 200 % ist die zurückgegebene Größe horizontal und vertikal doppelt so groß.

Kotlin

private fun computeScrollSurfaceSize(): Point {
    return Point(
            (contentRect.width() * (AXIS_X_MAX - AXIS_X_MIN) / currentViewport.width()).toInt(),
            (contentRect.height() * (AXIS_Y_MAX - AXIS_Y_MIN) / currentViewport.height()).toInt()
    )
}

Java

private Point computeScrollSurfaceSize() {
    return new Point(
            (int) (contentRect.width() * (AXIS_X_MAX - AXIS_X_MIN)
                    / currentViewport.width()),
            (int) (contentRect.height() * (AXIS_Y_MAX - AXIS_Y_MIN)
                    / currentViewport.height()));
}

Ein weiteres Beispiel für die Verwendung von Scroller finden Sie auf der Quellcode ViewPager. Sie scrollt als Reaktion auf Seitenbewegungen und verwendet Scrollen zum Implementieren des "Snap-to-Page"- Animation.

Overscroll-Effekt implementieren

Ab Android 12 fügt EdgeEffect den Parameter folgenden APIs zur Implementierung des Overscroll-Effekts:

  • getDistance()
  • onPullDistance()

Um die beste User Experience mit Stretch Overscroll zu bieten, sollten Sie Folgendes:

  1. Wenn die Streckanimation aktiv ist, wenn der Nutzer das Symbol kannst du die Berührung als „Fang“ registrieren. Der Nutzer stoppt die Animation und die Dehnung wieder zu bearbeiten.
  2. Wenn Nutzende ihren Finger in die entgegengesetzte Richtung der Dehnung bewegen, lassen Sie sie los, bis sie ganz weg ist, und fangen Sie dann an zu scrollen.
  3. Wenn der Nutzer während einer Dehnübung kippt, lässt sich das EdgeEffect um den Dehnungseffekt zu verbessern.

Animation ansehen

Wenn Nutzende eine aktive Streckanimation sehen, EdgeEffect.getDistance() gibt 0 zurück. Diese Bedingung gibt an, dass die Dehnung durch die Berührungsbewegung beeinflusst werden muss. In den meisten Container enthält, wird der Catch in onInterceptTouchEvent() erkannt, wie: wie im folgenden Code-Snippet dargestellt:

Kotlin

override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
  ...
  when (action and MotionEvent.ACTION_MASK) {
    MotionEvent.ACTION_DOWN ->
      ...
      isBeingDragged = EdgeEffectCompat.getDistance(edgeEffectBottom) > 0f ||
          EdgeEffectCompat.getDistance(edgeEffectTop) > 0f
      ...
  }
  return isBeingDragged
}

Java

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
  ...
  switch (action & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN:
      ...
      isBeingDragged = EdgeEffectCompat.getDistance(edgeEffectBottom) > 0
          || EdgeEffectCompat.getDistance(edgeEffectTop) > 0;
      ...
  }
}

Im vorherigen Beispiel gibt onInterceptTouchEvent() true, wenn mIsBeingDragged gleich true ist, also reicht es aus, das Ereignis zu verarbeiten, bevor das Kind die Möglichkeit hat, konsumieren.

Overscroll-Effekt loslassen

Es ist wichtig, den Stretch-Effekt vor dem Scrollen loszulassen, um zu verhindern, nicht auf den scrollbaren Inhalt angewendet wird. Der folgende Code wird diese Best Practice angewendet:

Kotlin

override fun onTouchEvent(ev: MotionEvent): Boolean {
  val activePointerIndex = ev.actionIndex

  when (ev.getActionMasked()) {
    MotionEvent.ACTION_MOVE ->
      val x = ev.getX(activePointerIndex)
      val y = ev.getY(activePointerIndex)
      var deltaY = y - lastMotionY
      val pullDistance = deltaY / height
      val displacement = x / width

      if (deltaY < 0f && EdgeEffectCompat.getDistance(edgeEffectTop) > 0f) {
        deltaY -= height * EdgeEffectCompat.onPullDistance(edgeEffectTop,
            pullDistance, displacement);
      }
      if (deltaY > 0f && EdgeEffectCompat.getDistance(edgeEffectBottom) > 0f) {
        deltaY += height * EdgeEffectCompat.onPullDistance(edgeEffectBottom,
            -pullDistance, 1 - displacement);
      }
      ...
  }

Java

@Override
public boolean onTouchEvent(MotionEvent ev) {

  final int actionMasked = ev.getActionMasked();

  switch (actionMasked) {
    case MotionEvent.ACTION_MOVE:
      final float x = ev.getX(activePointerIndex);
      final float y = ev.getY(activePointerIndex);
      float deltaY = y - lastMotionY;
      float pullDistance = deltaY / getHeight();
      float displacement = x / getWidth();

      if (deltaY < 0 && EdgeEffectCompat.getDistance(edgeEffectTop) > 0) {
        deltaY -= getHeight() * EdgeEffectCompat.onPullDistance(edgeEffectTop,
            pullDistance, displacement);
      }
      if (deltaY > 0 && EdgeEffectCompat.getDistance(edgeEffectBottom) > 0) {
        deltaY += getHeight() * EdgeEffectCompat.onPullDistance(edgeEffectBottom,
            -pullDistance, 1 - displacement);
      }
            ...

Beim Ziehen durch den Nutzer die Pull-Entfernung von EdgeEffect nutzen bevor Sie das Touch-Ereignis an einen verschachtelten scrollbaren Container übergeben oder die Scrollen. Im vorherigen Codebeispiel gibt getDistance() den Fehlerwert positiver Wert, wenn ein Randeffekt angezeigt wird und dieser mit Bewegung. Löst das Touch-Ereignis die Strecke aus, wird sie zuerst vom EdgeEffect, damit es vor anderen Effekten vollständig freigegeben wird, wie verschachteltes Scrollen, angezeigt werden. Du kannst getDistance() verwenden um zu erfahren, wie viel Ziehabstand erforderlich ist, um den aktuellen Effekt freizugeben.

Im Gegensatz zu onPull() gibt onPullDistance() den Wert Verbrauchsmenge des übergebenen Deltas. Ab Android 12, wenn onPull() oder onPullDistance() wurde ein negativer Wert übergeben. deltaDistance-Werte, wenn getDistance() gleich 0, der Stretch-Effekt ändert sich nicht. Android 11 und zuvor lässt onPull() negative Werte für die Gesamtentfernung zu Scheineffekte zeigen.

Overscroll deaktivieren

Sie können Overscroll in Ihrer Layoutdatei oder programmatisch deaktivieren.

Um diese Funktion in Ihrer Layoutdatei zu deaktivieren, legen Sie android:overScrollMode fest als: Beispiel:

<MyCustomView android:overScrollMode="never">
    ...
</MyCustomView>

Zur programmatischen Deaktivierung verwenden Sie Code wie den folgenden:

Kotlin

customView.overScrollMode = View.OVER_SCROLL_NEVER

Java

customView.setOverScrollMode(View.OVER_SCROLL_NEVER);

Weitere Informationen

Weitere Informationen finden Sie in den folgenden verwandten Ressourcen: