In Android wird das Scrollen normalerweise mit der Klasse ScrollView erreicht. Verschachteln Sie alle Standardlayouts, die über die Grenzen ihres Containers hinausgehen, in einem ScrollView, um eine scrollbare Ansicht zu erhalten, die vom Framework verwaltet wird. Die Implementierung eines benutzerdefinierten Scrollers ist nur für spezielle Szenarien erforderlich. In diesem Dokument wird beschrieben, wie Sie mithilfe von Scroller einen Scrolleffekt als Reaktion auf Touch-Gesten anzeigen.
Ihre App kann Scroller (Scroller oder OverScroller) verwenden, um die Daten zu erfassen, die für die Erstellung einer Scroll-Animation als Reaktion auf ein Touch-Event erforderlich sind. Sie sind ähnlich, aber OverScroller enthält auch Methoden, mit denen Nutzer darauf hingewiesen werden, wenn sie nach einer Schwenk- oder Wischgeste die Inhaltsränder erreichen.
- Ab Android 12 (API-Level 31) werden die visuellen Elemente bei einem Ziehereignis gedehnt und springen zurück. Bei einem Wischereignis werden sie weggeschleudert und springen zurück.
- Unter Android 11 (API-Level 30) und niedriger wird nach einer Zieh- oder Wischgeste zum Rand ein „Leuchteffekt“ angezeigt.
Im Beispiel InteractiveChart in diesem Dokument wird die Klasse EdgeEffect verwendet, um diese Overscroll-Effekte anzuzeigen.
Mit einem Scroller können Sie das Scrollen im Laufe der Zeit animieren. Dabei werden plattformübliche Scroll-Physiken wie Reibung, Geschwindigkeit und andere Eigenschaften verwendet. Der Scroller selbst zeichnet nichts. Scroller verfolgen die Scroll-Offsets im Laufe der Zeit, wenden diese Positionen aber nicht automatisch auf die Ansicht an. Sie müssen neue Koordinaten mit einer Geschwindigkeit abrufen und anwenden, die die Scrollanimation flüssig erscheinen lässt.
Terminologie zum Scrollen
Scrollen ist ein Wort, das in Android je nach Kontext unterschiedliche Bedeutungen haben kann.
Scrollen ist der allgemeine Vorgang, bei dem der Darstellungsbereich verschoben wird, also das „Fenster“ mit den Inhalten, die du dir ansiehst. Wenn sowohl auf der X- als auch auf der Y-Achse gescrollt wird, wird dies als Schwenken bezeichnet. Die Beispiel-App InteractiveChart in diesem Dokument veranschaulicht zwei verschiedene Arten des Scrollens, Ziehens und Schleuderns:
- Ziehen:Diese Art des Scrollens tritt auf, wenn ein Nutzer seinen Finger über den Touchscreen zieht. Sie können das Ziehen implementieren, indem Sie
onScroll()inGestureDetector.OnGestureListenerüberschreiben. Weitere Informationen zum Ziehen finden Sie unter Ziehen und skalieren. - Flinging:Diese Art des Scrollens tritt auf, wenn ein Nutzer den Finger schnell zieht und anhebt. Nachdem der Nutzer den Finger angehoben hat, sollte sich der Darstellungsbereich im Allgemeinen weiter bewegen, aber langsamer werden, bis er sich nicht mehr bewegt. Sie können das Schleudern implementieren, indem Sie
onFling()inGestureDetector.OnGestureListenerüberschreiben und ein Scroller-Objekt verwenden. - Schwenken:Das gleichzeitige Scrollen entlang der X- und Y-Achse wird als Schwenken bezeichnet.
Scroller-Objekte werden häufig in Verbindung mit einer Wischgeste verwendet. Sie können sie aber in jedem Kontext verwenden, in dem die Benutzeroberfläche als Reaktion auf ein Touch-Ereignis scrollen soll. Sie können beispielsweise onTouchEvent() überschreiben, um Touch-Ereignisse direkt zu verarbeiten und als Reaktion auf diese Touch-Ereignisse einen Scrolleffekt oder eine „An Seite ausrichten“-Animation zu erzeugen.
Komponenten mit integrierten Scroll-Implementierungen
Die folgenden Android-Komponenten bieten integrierte Unterstützung für das Scrollen und Overscrollen:
GridViewHorizontalScrollViewListViewNestedScrollViewRecyclerViewScrollViewViewPagerViewPager2
Wenn Ihre App das Scrollen und Overscrollen in einer anderen Komponente unterstützen muss, führen Sie die folgenden Schritte aus:
- Benutzerdefinierte Implementierung für das Scrollen per Berührung erstellen
- Wenn du Geräte mit Android 12 und höher unterstützen möchtest, implementiere den Stretch-Overscroll-Effekt.
Benutzerdefinierte Implementierung für berührungsbasiertes Scrollen erstellen
In diesem Abschnitt wird beschrieben, wie Sie einen eigenen Scroller erstellen, wenn Ihre App eine Komponente verwendet, die keine integrierte Unterstützung für Scrollen und Overscrollen bietet.
Das folgende Snippet stammt aus dem Beispiel InteractiveChart. Sie verwendet einen GestureDetector und überschreibt die Methode onFling() von GestureDetector.SimpleOnGestureListener. Dabei wird OverScroller verwendet, um die Wischgeste zu verfolgen. Wenn der Nutzer nach der Wischgeste die Ränder des Inhalts erreicht, zeigt der Container an, wenn der Nutzer das Ende des Inhalts erreicht hat. Die Anzeige hängt von der Android-Version des Geräts ab:
- Unter Android 12 und höher werden die visuellen Elemente gedehnt und springen zurück.
- Unter Android 11 und niedriger wird für die visuellen Elemente ein Leuchteffekt angezeigt.
Der erste Teil des folgenden Snippets zeigt die Implementierung von 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() postInvalidateOnAnimation() aufruft, wird computeScroll() ausgelöst, um die Werte für x und y zu aktualisieren. Dies geschieht in der Regel, wenn ein untergeordnetes Ansichtselement einen Bildlauf mit einem Scroller-Objekt animiert, wie im vorherigen Beispiel gezeigt.
Die meisten Ansichten übergeben die x- und y-Position des Scroller-Objekts direkt an scrollTo().
Die folgende Implementierung von computeScroll() verfolgt einen anderen Ansatz: Sie ruft computeScrollOffset() auf, um die aktuelle Position von x und y abzurufen. Wenn die Kriterien für die Anzeige eines Randeffekts für das Überscrollen erfüllt sind, d. h. die Anzeige ist herangezoomt, x oder y liegt außerhalb der Grenzen und die App zeigt noch kein Überscrollen an, richtet der Code den Randeffekt für das Überscrollen ein und ruft postInvalidateOnAnimation() auf, um eine Ungültigkeitserklärung für die Ansicht auszulösen.
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; } ... }
Hier ist der Codeabschnitt, der den eigentlichen Zoom ausfü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 im vorherigen Snippet aufgerufen wird. Sie berechnet die aktuelle Größe der scrollbaren Oberfläche in Pixeln. Wenn beispielsweise der gesamte Diagrammbereich sichtbar ist, ist dies die aktuelle Größe von mContentRect. Wenn das Diagramm in beide Richtungen um 200% vergrößert wird, 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 im Quellcode für die Klasse ViewPager. Sie scrollt als Reaktion auf Wischbewegungen und verwendet Scrollen, um die Animation „An Seite ausrichten“ zu implementieren.
Stretch-Overscroll-Effekt implementieren
Ab Android 12 fügt EdgeEffect die folgenden APIs hinzu, um den Stretch-Overscroll-Effekt zu implementieren:
getDistance()onPullDistance()
So bieten Sie mit dem Stretch-Overscroll die beste Nutzererfahrung:
- Wenn die Streckanimation aktiv ist und der Nutzer die Inhalte berührt, wird die Berührung als „Catch“ registriert. Der Nutzer stoppt die Animation und beginnt wieder, die Dehnung zu manipulieren.
- Wenn der Nutzer seinen Finger in die entgegengesetzte Richtung der Dehnung bewegt, wird die Dehnung aufgehoben, bis sie vollständig verschwunden ist. Anschließend wird mit dem Scrollen begonnen.
- Wenn der Nutzer während einer Dehnung eine Schleuderbewegung ausführt, wird die
EdgeEffectgeschleudert, um den Dehnungseffekt zu verstärken.
Animation ansehen
Wenn ein Nutzer eine aktive Streckanimation abfängt, gibt EdgeEffect.getDistance() 0 zurück. Diese Bedingung gibt an, dass die Dehnung durch die Berührungsbewegung manipuliert werden muss. In den meisten Containern wird der Catch in onInterceptTouchEvent() erkannt, wie im folgenden Code-Snippet gezeigt:
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() den Wert true zurück, wenn mIsBeingDragged den Wert true hat. Es reicht also aus, das Ereignis zu verarbeiten, bevor das untergeordnete Element die Möglichkeit dazu hat.
Overscroll-Effekt freigeben
Es ist wichtig, den Streckeffekt vor dem Scrollen zu beenden, damit er nicht auf den gescrollten Inhalt angewendet wird. Im folgenden Codebeispiel 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); } ...
Wenn der Nutzer zieht, wird die Ziehdistanz EdgeEffect verbraucht, bevor das Touch-Event an einen verschachtelten Scrollcontainer übergeben oder der Scrollvorgang gezogen wird. Im vorherigen Codebeispiel gibt getDistance() einen positiven Wert zurück, wenn ein Randeffekt angezeigt wird und durch eine Bewegung freigegeben werden kann. Wenn das Touch-Event die Dehnung aufhebt, wird es zuerst von der EdgeEffect verarbeitet, sodass es vollständig aufgehoben wird, bevor andere Effekte wie verschachteltes Scrollen angezeigt werden. Mit getDistance() können Sie herausfinden, wie weit Sie ziehen müssen, um den aktuellen Effekt zu beenden.
Im Gegensatz zu onPull() gibt onPullDistance() die verbrauchte Menge des übergebenen Deltas zurück. Wenn ab Android 12 negative deltaDistance-Werte an onPull() oder onPullDistance() übergeben werden, wenn getDistance() den Wert 0 hat, ändert sich der Streckeffekt nicht. Unter Android 11 und niedriger werden bei onPull() negative Werte für die Gesamtdistanz mit Leuchteffekten angezeigt.
Overscroll deaktivieren
Sie können das Overscroll-Verhalten in Ihrer Layoutdatei oder programmatisch deaktivieren.
Wenn Sie die Funktion in Ihrer Layoutdatei deaktivieren möchten, legen Sie android:overScrollMode wie im folgenden Beispiel gezeigt fest:
<MyCustomView android:overScrollMode="never"> ... </MyCustomView>
Wenn Sie die Funktion programmatisch deaktivieren möchten, verwenden Sie Code wie den folgenden:
Kotlin
customView.overScrollMode = View.OVER_SCROLL_NEVER
Java
customView.setOverScrollMode(View.OVER_SCROLL_NEVER);
Zusätzliche Ressourcen
Weitere Informationen finden Sie hier:
- Übersicht über Eingabeereignisse
- Sensoren – Übersicht
- Benutzerdefinierte Ansicht interaktiv gestalten