In Android erfolgt das Scrollen in der Regel mithilfe der Klasse ScrollView
. Verschachteln Sie jedes Standardlayout, das über die Grenzen seines Containers hinausgeht, in einem ScrollView
, um eine scrollbare Ansicht bereitzustellen, die vom Framework verwaltet wird. Die Implementierung eines benutzerdefinierten Scrollers ist nur in bestimmten Szenarien erforderlich. In diesem Dokument wird beschrieben, wie ein Scrolleffekt als Reaktion auf Touch-Gesten mithilfe von Scrollern angezeigt wird.
Ihre App kann Scroller – Scroller
oder OverScroller
– verwenden, um die Daten zu erfassen, die zum Erstellen einer Scrollanimation als Reaktion auf ein Berührungsereignis erforderlich sind. Sie sind ähnlich, aber OverScroller
enthält auch Methoden, um Nutzern mitzuteilen, wenn sie die Inhaltsränder nach einem Schwenken oder Wischen erreichen.
- Ab Android 12 (API-Level 31) dehnen sich die visuellen Elemente bei Drag-Ereignissen und reflektieren bei einem Schläge-Ereignis.
- Unter Android 11 (API-Level 30) und niedriger wird durch Ziehen oder Wischen an den Rand ein Leuchteffekt angezeigt.
Im Beispiel InteractiveChart
in diesem Dokument wird die Klasse EdgeEffect
verwendet, um diese Overscroll-Effekte anzuzeigen.
Sie können einen Scroller verwenden, um das Scrollen im Zeitverlauf zu animieren. Dazu werden die plattformüblichen Scrollingphysiken wie Reibung, Geschwindigkeit und andere Eigenschaften verwendet. Der Scroller selbst zeichnet nichts. Scroller erfassen Scroll-Offsets für Sie im Laufe der Zeit, wenden diese Positionen jedoch nicht automatisch auf Ihre Ansicht an. Sie müssen neue Koordinaten mit einer Geschwindigkeit abrufen und anwenden, mit der die Scrollanimation flüssiger aussieht.
Terminologie für das Scrollen
Scrollen ist ein Wort, das in Android je nach Kontext verschiedene Bedeutungen haben kann.
Beim Scrollen wird der Darstellungsbereich, also das "Fenster" des angezeigten Inhalts, verschoben. Wenn sich das Scrollen sowohl auf der x- als auch auf der y-Achse befindet, wird dies als Schwenken bezeichnet. Die Beispiel-App InteractiveChart
in diesem Dokument veranschaulicht zwei verschiedene Arten des Scrollens, Ziehens und Ziehens:
- Ziehen: Dies ist die Art von Scrollen, bei der ein Nutzer den Finger über den Touchscreen zieht. Sie können das Ziehen implementieren, indem Sie
onScroll()
inGestureDetector.OnGestureListener
überschreiben. Weitere Informationen zum Ziehen findest du unter Ziehen und skalieren. - Flingen: Dies ist die Art von Scrollen, bei der ein Nutzer den Finger schnell zieht und anhebt. Nachdem der Nutzer den Finger angehoben hat, sollten Sie den Darstellungsbereich in der Regel weiter bewegen, aber so lange verzögern, bis er sich nicht mehr bewegt. Sie können Flinging implementieren, indem Sie
onFling()
inGestureDetector.OnGestureListener
überschreiben und ein Scroller-Objekt verwenden. - Schwenken: Das gleichzeitige Scrollen auf der x- und y-Achse wird als Schwenken bezeichnet.
Scroller-Objekte werden üblicherweise in Verbindung mit einer Fingergeste verwendet. Sie können sie jedoch in jedem Kontext verwenden, in dem auf der Benutzeroberfläche das Scrollen als Reaktion auf ein Touch-Ereignis angezeigt werden soll. Du kannst beispielsweise onTouchEvent()
überschreiben, um Touch-Ereignisse direkt zu verarbeiten und einen Scrolleffekt oder eine „An Seite ausrichten“-Animation als Reaktion darauf zu erzeugen.
Komponenten mit integrierten Scrolling-Implementierungen
Die folgenden Android-Komponenten unterstützen das Scrollen und Overscroll-Verhalten:
GridView
HorizontalScrollView
ListView
NestedScrollView
RecyclerView
ScrollView
ViewPager
ViewPager2
Wenn Ihre App Scrollen und Overscroll innerhalb einer anderen Komponente unterstützen muss, führen Sie die folgenden Schritte aus:
- Erstelle eine benutzerdefinierte Implementierung für berührungsbasiertes Scrollen.
- Implementiere den Stretch-Overscroll-Effekt, um Geräte mit Android 12 und höher zu unterstützen.
Benutzerdefinierte Implementierung für das berührungsbasierte Scrollen erstellen
In diesem Abschnitt wird beschrieben, wie Sie einen eigenen Scroller erstellen, wenn Ihre Anwendung eine Komponente verwendet, die keine integrierte Unterstützung für Scrollen und Overscroll enthält.
Das folgende Snippet stammt aus dem Beispiel InteractiveChart
. Sie verwendet eine GestureDetector
und überschreibt die GestureDetector.SimpleOnGestureListener
-Methode onFling()
. Dabei wird OverScroller
verwendet, um den Fingergeste zu erfassen. Wenn der Nutzer die Inhaltsränder erreicht, nachdem er die Fingergeste ausgeführt hat, zeigt der Container an, wann der Nutzer das Ende des Inhalts erreicht hat. Die Angabe hängt davon ab, welche Android-Version auf dem Gerät ausgeführt wird:
- Unter Android 12 und höher werden die visuellen Elemente gedehnt und reflektiert.
- Unter Android 11 und niedriger zeigen die visuellen Elemente einen Leuchteffekt an.
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 Element eine Scroll-Aktion mithilfe eines Scroller-Objekts animiert, wie im vorherigen Beispiel gezeigt.
Bei den meisten Ansichten wird die x- und y-Position des Scroller-Objekts direkt an scrollTo()
übergeben.
Die folgende Implementierung von computeScroll()
verfolgt einen anderen Ansatz: Sie ruft computeScrollOffset()
auf, um den aktuellen Standort von x und y abzurufen. Wenn die Kriterien für die Anzeige eines Glow-Edge-Effekts des Overscroll-Effekts erfüllt sind, d. h. wenn die Anzeige herangezoomt ist, x oder y außerhalb der Grenzen liegt und die App nicht bereits einen Overscroll anzeigt, richtet der Code den Overscroll-Leuchteffekt ein und ruft postInvalidateOnAnimation()
auf, um eine Entwertung 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; } ... }
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 im vorherigen Snippet aufgerufen wird. Damit wird die Größe der aktuellen scrollbaren Oberfläche in Pixeln berechnet. Wenn beispielsweise der gesamte Diagrammbereich sichtbar ist, ist das die aktuelle Größe von mContentRect
. Wenn das Diagramm in beiden Richtungen um 200% gezoomt 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
. Durch Scrollen wird die
„Snap-to-Page“-Animation implementiert.
Overscroll-Effekt implementieren
Ab Android 12 fügt EdgeEffect
die folgenden APIs hinzu, um den Stretch-Overscroll-Effekt zu implementieren:
getDistance()
onPullDistance()
Gehen Sie so vor, um eine optimale Nutzererfahrung beim Stretch-Overscroll zu gewährleisten:
- Wenn die Streckanimation beim Berühren des Inhalts durch den Nutzer aktiv ist, erfasse die Berührung als „Fang“. Der Nutzer stoppt die Animation und bearbeitet die Strecke noch einmal.
- Wenn der Nutzer seinen Finger in die entgegengesetzte Richtung bewegt, lasse die Dehnung los, bis sie vollständig weg ist, und beginne dann mit dem Scrollen.
- Wenn der Nutzer während einer Dehnübung herumwirft,
EdgeEffect
, um den Dehnungseffekt zu verbessern.
Animation ansehen
Wenn ein Nutzer eine aktive Streckanimation erkennt, gibt EdgeEffect.getDistance()
0
zurück. Diese Bedingung gibt an, dass die Dehnung durch die Berührungsbewegung beeinflusst 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()
true
zurück, wenn mIsBeingDragged
gleich true
ist. Daher reicht es aus, das Ereignis zu verarbeiten, bevor das untergeordnete Element die Möglichkeit hat, es zu nutzen.
Overscroll-Effekt loslassen
Es ist wichtig, den Streckeffekt vor dem Scrollen loszulassen, um zu verhindern, dass die Streckung auf den scrollbaren 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, nimm die EdgeEffect
-Abrufstrecke auf, bevor du das Touch-Ereignis an einen verschachtelten scrollbaren Container übergibst oder den Scrollvorgang ziehst. Im vorherigen Codebeispiel gibt getDistance()
einen positiven Wert zurück, wenn ein Randeffekt angezeigt wird und dieser durch Bewegung ausgelöst werden kann. Wenn das Touch-Ereignis die Dehnung loslässt, wird es zuerst vom EdgeEffect
verbraucht, sodass es vollständig freigegeben wird, bevor andere Effekte wie verschachteltes Scrollen angezeigt werden. Mit getDistance()
können Sie herausfinden, wie viel Ziehstrecke erforderlich ist, um den aktuellen Effekt freizugeben.
Im Gegensatz zu onPull()
gibt onPullDistance()
den verbrauchten Betrag des übergebenen Deltas zurück. Ab Android 12 ändert sich der Stretch-Effekt nicht, wenn für onPull()
oder onPullDistance()
negative deltaDistance
-Werte übergeben werden und getDistance()
gleich 0
ist. Unter Android 11 und niedriger ermöglicht onPull()
bei negativen Werten für die Gesamtentfernung, Glow-Effekte anzuzeigen.
Overscroll deaktivieren
Sie können Overscroll in Ihrer Layoutdatei oder programmatisch deaktivieren.
Um diese Funktion in Ihrer Layoutdatei zu deaktivieren, legen Sie android:overScrollMode
wie im folgenden Beispiel fest:
<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);
Zusätzliche Ressourcen
Weitere Informationen finden Sie in den folgenden verwandten Ressourcen: