Di Android, scroll biasanya dicapai dengan menggunakan
ScrollView
. Susun tata letak standar apa pun yang mungkin melampaui batas penampung
di ScrollView
untuk memberikan tampilan scroll yang dikelola oleh
framework. Menerapkan scroller kustom hanya diperlukan untuk
yang signifikan. Dokumen ini menjelaskan cara menampilkan efek scroll sebagai respons
terhadap gestur sentuh menggunakan scroller.
Aplikasi Anda dapat menggunakan
scroller—Scroller
atau
OverScroller
—ke
mengumpulkan data yang diperlukan untuk menghasilkan animasi scrolling sebagai respons terhadap sentuhan
peristiwa. Keduanya serupa, tetapi OverScroller
juga menyertakan metode untuk
yang menunjukkan kepada pengguna ketika mereka mencapai tepi konten setelah menggeser atau mengayunkan
{i>gesture <i}ini.
- Mulai Android 12 (level API 31), elemen visual memantul dan memantul kejadian {i>drag<i}, lalu ayunkan jari dan kembali ke{i> <i}peristiwa {i>fling<i}.
- Di Android 11 (level API 30) dan yang lebih lama, batas menampilkan "glow" setelah gestur tarik atau ayunkan jari ke tepi.
Contoh InteractiveChart
dalam dokumen ini menggunakan
class
EdgeEffect
untuk menampilkan efek overscroll ini.
Anda bisa menggunakan scroller untuk menganimasikan scrolling dari waktu ke waktu, menggunakan fisika scroll standar platform seperti gesekan, kecepatan, dan kualitas. Scroller itu sendiri tidak menggambar apa pun. Scroller melacak offset scroll untuk Anda dari waktu ke waktu, tetapi tidak otomatis menerapkan posisi tersebut ke tampilan Anda. Anda harus mendapatkan dan menerapkan koordinat baru pada tingkat yang membuat animasi scrolling terlihat mulus.
Memahami terminologi scrolling
Men-scroll adalah kata yang memiliki arti yang berbeda di Android, bergantung pada konteks.
Men-scroll adalah proses umum untuk menggerakkan area pandang—yaitu,
"jendela" konten yang sedang Anda lihat. Saat men-scroll di kedua
Sumbu x- dan y, fungsi ini disebut geser. Tujuan
Aplikasi contoh InteractiveChart
dalam dokumen ini menggambarkan dua
berbagai jenis scrolling, menyeret dan mengayunkan jari:
- Menarik: ini adalah jenis scroll yang terjadi saat pengguna
menarik jari mereka di layar sentuh. Anda dapat menerapkan fungsi tarik dengan
mengganti
onScroll()
inciGestureDetector.OnGestureListener
. Untuk informasi selengkapnya tentang penarikan, lihat Tarik dan skalakan. - Flinging: ini adalah jenis scroll yang terjadi saat pengguna
menarik dan mengangkat jarinya dengan cepat. Setelah pengguna mengangkat jarinya, Anda
umumnya ingin terus menggerakkan area pandang, tetapi melambat hingga
area pandang berhenti bergerak. Anda dapat menerapkan flinging dengan mengganti
onFling()
diGestureDetector.OnGestureListener
dan menggunakan scroller . - Panning: men-scroll secara bersamaan di sepanjang x- dan Sumbu y disebut penggeseran.
Sangat umum menggunakan objek scroller
bersamaan dengan gestur ayunkan jari, tetapi
Anda dapat menggunakannya dalam konteks apa pun di mana Anda ingin UI menampilkan scroll
terhadap peristiwa sentuh. Misalnya, Anda dapat mengganti
onTouchEvent()
untuk memproses peristiwa sentuh secara langsung dan menghasilkan efek scroll atau
"paskan ke halaman" animasi sebagai respons terhadap peristiwa sentuh tersebut.
Komponen yang berisi implementasi scroll bawaan
Komponen Android berikut berisi dukungan bawaan untuk menggulir dan perilaku overscrolling:
GridView
HorizontalScrollView
ListView
NestedScrollView
RecyclerView
ScrollView
ViewPager
ViewPager2
Jika aplikasi Anda perlu mendukung scrolling dan overscrolling di dalam ini, selesaikan langkah-langkah berikut:
- Membuat scroll kustom berbasis sentuhan penerapan.
- Untuk mendukung perangkat yang menjalankan Android 12 dan yang lebih baru, terapkan efek overscroll regangan.
Membuat implementasi scroll berbasis sentuhan kustom
Bagian ini menjelaskan cara membuat scroll sendiri jika aplikasi Anda menggunakan komponen yang tidak berisi dukungan bawaan untuk scroll dan overscroll.
Cuplikan berikut berasal dari
InteractiveChart
contoh. Class ini menggunakan
GestureDetector
dan mengganti
metode
GestureDetector.SimpleOnGestureListener
onFling()
. OverScroller
digunakan untuk melacak gestur lempar. Jika pengguna mencapai tepi konten setelah melakukan
ayunkan jari, kontainer menunjukkan ketika pengguna mencapai akhir
saat ini. Indikasinya tergantung pada versi Android yang digunakan
berjalan:
- Di Android 12 dan yang lebih baru, elemen visual meregang dan bangkit kembali.
- Di Android 11 dan yang lebih lama, elemen visual menampilkan cahaya pengaruh tersebut.
Bagian pertama cuplikan berikut menunjukkan implementasi dari
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); }
Saat onFling()
menelepon
postInvalidateOnAnimation()
,
hal itu memicu
computeScroll()
untuk memperbarui nilai x dan y. Hal ini biasanya dilakukan ketika
turunan tampilan menganimasikan scroll menggunakan objek scroller, seperti ditunjukkan dalam
contoh.
Sebagian besar tampilan meneruskan posisi x dan y objek scroller secara langsung
dapat
scrollTo()
.
Implementasi computeScroll()
berikut menggunakan metode
pendekatan: ia memanggil
computeScrollOffset()
untuk mendapatkan lokasi x dan y saat ini. Ketika kriteria untuk
menampilkan "glow" dengan overscroll terpenuhi—yaitu, tampilan
diperbesar, x atau y berada di luar batas, dan aplikasi belum
menunjukkan {i>overscroll<i}—kode mengatur efek glow {i>overscroll<i} dan
memanggil postInvalidateOnAnimation()
untuk memicu pembatalan validasi di
{i>view<i}.
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; } ... }
Berikut adalah bagian dari kode yang melakukan zoom aktual:
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); }
Ini adalah metode computeScrollSurfaceSize()
yang dipanggil dalam
cuplikan sebelumnya. Metode ini menghitung ukuran platform yang dapat di-scroll saat ini dalam
piksel. Misalnya, jika seluruh area diagram terlihat, ini adalah area saat ini
berukuran mContentRect
. Jika diagram diperbesar 200% di kedua
arah, ukuran yang ditampilkan akan dua kali lebih besar secara horizontal dan vertikal.
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())); }
Untuk contoh lain penggunaan scroller, lihat
kode sumber
untuk class ViewPager
. Ia men-scroll untuk merespons fling dan menggunakan
scroll untuk mengimplementasikan fungsi "snap-to-page" animasi.
Mengimplementasikan efek overscroll regangan
Mulai Android 12, EdgeEffect
menambahkan
API berikut untuk menerapkan efek overscroll regangan:
getDistance()
onPullDistance()
Untuk memberikan pengalaman pengguna terbaik dengan overscroll regangan, lakukan berikut ini:
- Saat animasi regangan diterapkan ketika pengguna menyentuh konten, daftarkan sentuhan sebagai "tangkapan". Pengguna menghentikan animasi dan mulai memanipulasi regangan lagi.
- Ketika pengguna menggerakkan jari mereka ke arah yang berlawanan dari regangan, lepaskan regangan sampai benar-benar hilang, lalu mulailah menggulir.
- Saat pengguna mengayunkan selama peregangan, ayunkan
EdgeEffect
untuk meningkatkan efek peregangan.
Menonton animasi
Saat pengguna menonton animasi regangan aktif,
EdgeEffect.getDistance()
menampilkan 0
. Kondisi ini
menunjukkan bahwa regangan harus dimanipulasi oleh gerakan sentuh. Dalam sebagian besar
penampung, tangkapan terdeteksi di onInterceptTouchEvent()
, sebagai
yang ditampilkan dalam cuplikan kode berikut:
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; ... } }
Dalam contoh sebelumnya, onInterceptTouchEvent()
menampilkan
true
saat mIsBeingDragged
adalah true
, jadi
peristiwa tersebut sudah cukup sebelum turunan memiliki kesempatan untuk
akan menggunakannya.
Merilis efek overscroll
Sebaiknya lepaskan efek regangan sebelum men-scroll untuk mencegah regangan agar tidak diterapkan ke konten scroll. Kode berikut menerapkan praktik terbaik ini:
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); } ...
Saat pengguna menarik, gunakan jarak tarik EdgeEffect
sebelum Anda meneruskan peristiwa sentuh ke penampung scroll bertingkat atau menarik
men-scroll. Dalam contoh kode sebelumnya, getDistance()
menampilkan
positif ketika efek tepi ditampilkan dan dapat dilepaskan dengan
{i>motion <i}(gerakan). Saat melepaskan regangan,
peristiwa sentuh pertama kali digunakan oleh
EdgeEffect
sehingga akan benar-benar dilepaskan sebelum efek lainnya,
seperti scroll bertingkat, ditampilkan. Anda dapat menggunakan getDistance()
untuk mempelajari berapa jarak tarik yang diperlukan untuk melepaskan efek saat ini.
Tidak seperti onPull()
, onPullDistance()
menampilkan
jumlah yang dikonsumsi delta yang diteruskan. Mulai Android 12, jika
onPull()
atau onPullDistance()
diberi nilai negatif
deltaDistance
nilai saat getDistance()
0
, efek regangan tidak berubah. Di Android 11
dan sebelumnya, onPull()
memungkinkan nilai negatif untuk total jarak
menampilkan efek kilau.
Nonaktifkan overscroll
Anda dapat memilih untuk tidak melakukan overscroll dalam file tata letak atau secara terprogram.
Untuk memilih tidak ikut dalam file tata letak, tetapkan android:overScrollMode
sebagai
yang ditunjukkan dalam contoh berikut:
<MyCustomView android:overScrollMode="never"> ... </MyCustomView>
Untuk memilih tidak ikut secara terprogram, gunakan kode seperti berikut:
Kotlin
customView.overScrollMode = View.OVER_SCROLL_NEVER
Java
customView.setOverScrollMode(View.OVER_SCROLL_NEVER);
Referensi lainnya
Lihat referensi terkait berikut: