W Androidzie do przewijania zwykle służy klasa ScrollView
. Umieść w elemencie ScrollView
dowolny układ standardowy, który może wykraczać poza granice swojego kontenera, aby zapewnić przewijany widok zarządzany przez platformę. Wdrożenie niestandardowego scrollera jest konieczne tylko w szczególnych przypadkach. Ten dokument opisuje wyświetlanie efektu przewijania w odpowiedzi na gesty dotykowe za pomocą rolki.
Aplikacja może używać scrollerów (Scroller
lub OverScroller
), aby zbierać dane potrzebne do wygenerowania animacji przewijania w reakcji na zdarzenie dotykowe. Są one podobne, ale OverScroller
zawiera też metody informowania użytkowników, gdy dotrą do krawędzi treści po przesunięciu lub przeciągnięciu.
- Począwszy od Androida 12 (poziom interfejsu API 31) elementy wizualne rozciągają się i odbijają się w wyniku przeciągania, a potem wracają i wracają.
- Na Androidzie 11 (poziom interfejsu API 30) i starszych granicach pojawiają się efekt „poświaty” po przeciągnięciu lub przesunięciu palcem do krawędzi.
Przykładowy InteractiveChart
w tym dokumencie używa klasy EdgeEffect
do wyświetlania efektów przewijania.
Możesz użyć scrollera, aby animować przewijanie w czasie, używając standardowych właściwości fizyki przewijania na platformie, takich jak tarcie, prędkość i inne cechy. Scroller sam z siebie niczego nie rysuje. Przewijacze śledzą za Ciebie przesunięcia przewijania, ale nie stosują ich automatycznie w Twoim widoku. Nowe współrzędne musisz pobierać i stosować z taką częstotliwością, aby animacja przewijania była płynna.
Terminologia dotycząca przewijania
Przewijanie to pojęcie, które może mieć na Androidzie różne znaczenie w zależności od kontekstu.
Przewijanie to ogólny proces przesuwania widoku, czyli „okna” treści, które widzisz. Przewijanie w obu kierunkach osi x i y nazywa się przesuwaniem. Przykładowa aplikacja InteractiveChart
w tym dokumencie ilustruje 2 różne typy przewijania, przeciągania i przesuwania:
- Przeciąganie: ten typ przewijania występuje, gdy użytkownik przesuwa palcem po ekranie dotykowym. Przeciąganie możesz zaimplementować, zastępując
onScroll()
w plikuGestureDetector.OnGestureListener
. Więcej informacji o przeciąganiu znajdziesz w sekcji Przeciąganie i skalowanie. - Flinging: ten typ przewijania występuje, gdy użytkownik szybko przeciąga i podnosi palec. Gdy użytkownik uniesie palec, warto nadal przesuwać widoczny obszar, ale zwalniać jego tempo, aż widok przestanie się poruszać. Możesz wdrożyć przesuwanie, zastępując
onFling()
wGestureDetector.OnGestureListener
i używając obiektu przewijania. - Przesuwanie: przewijanie jednocześnie po osi x i y nazywamy przesuwaniem.
Obiekty scroller są często używane w połączeniu z gestami przeciągania, ale można ich używać w dowolnym kontekście, w którym chcesz, aby interfejs wyświetlał przewijanie w odpowiedzi na zdarzenie dotykowe. Możesz na przykład zastąpić funkcję onTouchEvent()
, aby przetwarzać zdarzenia dotknięcia bezpośrednio i wywoływać w odpowiedzi na nie efekt przewijania lub animację „dopasowania do strony”.
komponenty zawierające wbudowane implementacje przewijania;
Te komponenty Androida mają wbudowaną obsługę przewijania i przewijania:
GridView
HorizontalScrollView
ListView
NestedScrollView
RecyclerView
ScrollView
ViewPager
ViewPager2
Jeśli aplikacja musi obsługiwać przewijanie i przewijanie z przeskokiem w innym komponencie, wykonaj te czynności:
- Utwórz niestandardową implementację przewijania za pomocą dotyku.
- Aby zapewnić obsługę urządzeń z Androidem 12 lub nowszym, zaimplementuj efekt rozciągnięcia w trakcie przewijania.
Utwórz niestandardową implementację przewijania dotykiem
Z tej sekcji dowiesz się, jak utworzyć własny scroller, jeśli Twoja aplikacja używa komponentu, który nie obsługuje wbudowanego przewijania.
Poniższy fragment pochodzi z pliku InteractiveChart
. Używana jest metoda GestureDetector
i zastępuje metodę GestureDetector.SimpleOnGestureListener
onFling()
. Do śledzenia gestu flirtu wykorzystuje się OverScroller
. Jeśli użytkownik dotrze do krawędzi treści po wykonaniu gestu przesuwania, kontener poinformuje o tym, kiedy użytkownik dotrze do końca treści. Zależy to od wersji Androida, na której działa urządzenie:
- Na Androidzie 12 i nowszych elementy wizualne są rozciągane i powracają do pierwotnego rozmiaru.
- Na Androidzie 11 i starszych elementy wizualne wyświetlają efekt poświaty.
Pierwsza część tego fragmentu kodu pokazuje implementację 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); }
Gdy funkcja onFling()
wywołuje funkcję postInvalidateOnAnimation()
, powoduje to uruchomienie funkcji computeScroll()
, która aktualizuje wartości x i y. Zwykle dzieje się tak, gdy podrzędna widoku animuje przewijanie za pomocą obiektu scroller, jak pokazano w poprzednim przykładzie.
Większość widoków przekazuje pozycję x i y obiektu scroller bezpośrednio do scrollTo()
.
Implementacja funkcji computeScroll()
w tym przykładzie jest inna: wywołuje ona funkcję computeScrollOffset()
, aby uzyskać bieżącą lokalizację x i y. Gdy są spełnione kryteria wyświetlania efektu „zanikania” krawędzi przy przewijaniu – czyli gdy wyświetlacz jest powiększony, x lub y są poza zakresem, a aplikacja nie wyświetla już efektu przewijania – kod tworzy efekt zanikania i wywołuje funkcję postInvalidateOnAnimation()
, aby wywołać nieważność widoku.
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; } ... }
Oto fragment kodu, który służy do rzeczywistego powiększenia:
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); }
W poprzednim fragmencie kodu wywoływana jest metoda computeScrollSurfaceSize()
. Oblicza bieżący rozmiar powierzchni do przewijania w pikselach. Jeśli np. widoczny jest cały obszar wykresu, oznacza to, że bieżący rozmiar elementu mContentRect
. Jeśli wykres jest powiększony o 200% w obu kierunkach, zwrócony rozmiar będzie dwukrotnie większy w poziomie i w pionie.
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())); }
Inny przykład użycia przewijaka znajdziesz w kodzie źródłowym klasy ViewPager
. Przewija się w odpowiedzi na gesty przesuwania i wykorzystuje przewijanie do implementacji animacji „dopasowania do strony”.
Wdrażanie efektu rozciągania podczas przewijania
Począwszy od Androida 12, EdgeEffect
dodaje te interfejsy API do implementowania efektu rozciągania podczas przewijania:
getDistance()
onPullDistance()
Aby zapewnić użytkownikom jak najlepsze wrażenia podczas przewijania rozszerzonego, wykonaj te czynności:
- Gdy użytkownik dotyka treści, a rozciąganie jest włączone, zarejestruj dotyk jako „chwytanie”. Użytkownik zatrzymuje animację i ponownie zaczyna manipulować rozciąganiem.
- Gdy użytkownik przesunie palcem w kierunku przeciwnym do rozciągania, zwolnij rozciąganie, aż zniknie w pełni, a potem zacznij przewijać.
- Gdy użytkownik przesunie palcem podczas rozciągania, przesuń
EdgeEffect
, aby wzmocnić efekt rozciągania.
Animacja
Gdy użytkownik złapuje aktywną animację rozciąganą, EdgeEffect.getDistance()
zwraca wartość 0
. Ten warunek oznacza, że rozciąganie musi być modyfikowane dotykiem. W większości kontenerów błąd jest wykrywany w onInterceptTouchEvent()
, jak pokazano w tym fragmencie kodu:
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; ... } }
W powyższym przykładzie funkcja onInterceptTouchEvent()
zwraca wartość true
, gdy mIsBeingDragged
ma wartość true
, więc wystarczy użyć zdarzenia przed tym, jak dziecko będzie mogło je wykorzystać.
Wyłączenie efektu overscroll
Pamiętaj, by przed przewijaniem zwolnić efekt rozciągania, aby zapobiec zastosowaniu rozciągania do przewijanej treści. W tym przykładzie kodu zastosowano tę sprawdzoną metodę:
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); } ...
Gdy użytkownik przeciąga, użyj EdgeEffect
odległości przeciągania, zanim przekażesz zdarzenie dotykowe do zduplikowanego kontenera przewijania lub przeciągnij suwak. W poprzednim przykładzie kodu getDistance()
zwraca dodatnią wartość, gdy wyświetlany jest efekt krawędzi i można go wyłączyć za pomocą ruchu. Gdy zdarzenie kliknięcia zwolni rozciąganie, jest ono najpierw przetwarzane przez EdgeEffect
, aby zostało całkowicie zwolnione, zanim zostaną wyświetlone inne efekty, takie jak przewijanie zagnieżdżone. Możesz użyć getDistance()
, aby dowiedzieć się, jaka odległość jest wymagana do zwolnienia bieżącego efektu.
W przeciwieństwie do funkcji onPull()
funkcja onPullDistance()
zwraca ilość zużytą z przekazanej wartości delta. Od Androida 12, jeśli onPull()
lub onPullDistance()
mają wartości ujemne deltaDistance
, gdy getDistance()
ma wartość 0
, efekt rozciągania się nie zmienia. W Androidzie 11 i starszych wartości ujemne w przypadku całkowitej odległości powodują wyświetlanie efektów poświaty.onPull()
Zrezygnuj z nadmiernych przewijania
Możesz wyłączyć przewijanie w pliku układu lub programowo.
Aby zrezygnować z tej funkcji w pliku układu, ustaw android:overScrollMode
w następujący sposób:
<MyCustomView android:overScrollMode="never"> ... </MyCustomView>
Aby zrezygnować z automatycznego wyświetlania reklam, użyj kodu podobnego do tego:
Kotlin
customView.overScrollMode = View.OVER_SCROLL_NEVER
Java
customView.setOverScrollMode(View.OVER_SCROLL_NEVER);
Dodatkowe materiały
Zapoznaj się z tymi materiałami:
- Omówienie zdarzeń wejściowych
- Omówienie czujników
- Uzyskiwanie widoku niestandardowego w wersji interaktywnej