Skip to content

Commit 1efeb57

Browse files
author
bob.huang
committed
支持pageEnabled分页,效果类似ViewPager
1 parent 8c29966 commit 1efeb57

File tree

3 files changed

+111
-16
lines changed

3 files changed

+111
-16
lines changed

README.MD

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Feature:
1111
* 双向滚动 ![](https://img.shields.io/badge/√-brightgreen.svg)
1212
* 嵌套滚动 ![](https://img.shields.io/badge/√-brightgreen.svg)
1313
* 缩放(zoomScale,minimumZoomScale,maximumZoomScale)![](https://img.shields.io/badge/coming-brightgreen.svg)
14-
* 分页(pageingEnabled)![](https://img.shields.io/badge/coming-brightgreen.svg)
14+
* 分页(pageingEnabled,类似ViewPager)![](https://img.shields.io/badge/-brightgreen.svg)
1515
* 锁定方向(directionalLockEnabled)![](https://img.shields.io/badge/coming-brightgreen.svg)
1616
* 居中(centerContent)![](https://img.shields.io/badge/coming-brightgreen.svg)
1717
* contentInset![](https://img.shields.io/badge/coming-brightgreen.svg)

superscrollview/src/main/java/com/huangmb/superscrollview/SuperScrollView.kt

+102-15
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,26 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
119119
*/
120120
var isSmoothScrollingEnabled = true
121121

122+
/**
123+
* When true, the scroll view stops on multiples of the scroll view's size
124+
* when scrolling. This can be used for horizontal pagination. The default
125+
* value is false.
126+
*/
127+
var isPagingEnabled = false
128+
129+
var horizontalPagingThreshold = 0.5
130+
var verticalPagingThreshold = 0.5
131+
132+
122133
/**
123134
* When true, the ScrollView will try to lock to only vertical or horizontal
124135
* scrolling while dragging.
125136
*/
126137
var isDirectionalLockEnabled = false
127138

139+
private var mActivelyScrolling = false
140+
141+
private var mPostTouchRunnable: Runnable? = null
128142
private val configuration = ViewConfiguration.get(context)
129143
private var mTouchSlop = configuration.scaledTouchSlop
130144
private var mMinimumVelocity = configuration.scaledMinimumFlingVelocity
@@ -204,6 +218,9 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
204218
return scrollRange
205219
}
206220

221+
val isHorizontal: Boolean
222+
get() = childCount > 0 && getChildAt(0).width > (width - paddingLeft - paddingRight)
223+
207224
/**
208225
* Interface definition for a callback to be invoked when the scroll
209226
* X or Y positions of a view change.
@@ -231,9 +248,10 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
231248
initScrollView()
232249

233250
val a = context.obtainStyledAttributes(
234-
attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0)
251+
attrs, R.styleable.SuperScrollView, defStyleAttr, 0)
235252

236-
isFillViewport = a.getBoolean(0, false)
253+
isFillViewport = a.getBoolean(R.styleable.SuperScrollView_isFillViewport, false)
254+
isPagingEnabled = a.getBoolean(R.styleable.SuperScrollView_isPagingEnabled, false)
237255

238256
a.recycle()
239257

@@ -466,6 +484,8 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
466484
if (mOnScrollChangeListener != null) {
467485
mOnScrollChangeListener!!.onScrollChange(this, l, t, oldl, oldt)
468486
}
487+
488+
mActivelyScrolling = true
469489
}
470490

471491
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@@ -563,10 +583,10 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
563583
val canScrollVertical = computeVerticalScrollRange() > computeVerticalScrollExtent()
564584

565585
if (canScrollVertical) {
566-
val direction = if (event.isShiftPressed) View.FOCUS_UP else View.FOCUS_DOWN
586+
val direction = if (event.isShiftPressed) View.FOCUS_UP else View.FOCUS_DOWN
567587
pageScrollVertical(direction)
568588
} else {
569-
val direction = if (event.isShiftPressed) View.FOCUS_LEFT else View.FOCUS_RIGHT
589+
val direction = if (event.isShiftPressed) View.FOCUS_LEFT else View.FOCUS_RIGHT
570590
pageScrollHorizontal(direction)
571591
}
572592

@@ -904,6 +924,7 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
904924
}
905925
mActivePointerId = INVALID_POINTER
906926
endDrag()
927+
handlePostTouchScrolling()
907928
}
908929
MotionEvent.ACTION_CANCEL -> {
909930
if (mIsBeingDragged && childCount > 0) {
@@ -1204,6 +1225,7 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
12041225

12051226
return scrollAndFocusHorizontal(direction, mTempRect.left, mTempRect.right)
12061227
}
1228+
12071229
/**
12081230
*
12091231
* Handles scrolling in response to a "home/end" shortcut press. This
@@ -1459,6 +1481,42 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
14591481
return true
14601482
}
14611483

1484+
1485+
private fun handlePostTouchScrolling() {
1486+
if (isPagingEnabled) {
1487+
mActivelyScrolling = false
1488+
mPostTouchRunnable = PostTouchRunnable()
1489+
ViewCompat.postOnAnimationDelayed(this, mPostTouchRunnable, MOMENTUM_DELAY)
1490+
}
1491+
}
1492+
1493+
/**
1494+
* This will smooth scroll us to the nearest page boundary
1495+
* It currently just looks at where the content is relative to the page and slides to the nearest
1496+
* page. It is intended to be run after we are done scrolling, and handling any momentum
1497+
* scrolling.
1498+
*/
1499+
private fun smoothScrollToPage(velocityX: Int = 0, velocityY: Int = 0) {
1500+
val width = width
1501+
val currentX = scrollX
1502+
val predictedX = currentX + velocityX
1503+
var hpage = currentX / width
1504+
if (predictedX > hpage * width + width * horizontalPagingThreshold) {
1505+
hpage += 1
1506+
}
1507+
1508+
val height = height
1509+
val currentY = scrollY
1510+
val preY = currentY + velocityY
1511+
var vpage = currentY / height
1512+
if (preY > vpage * height + height * verticalPagingThreshold) {
1513+
vpage += 1
1514+
}
1515+
1516+
smoothScrollTo(hpage * width, vpage * height)
1517+
1518+
}
1519+
14621520
/**
14631521
* @return whether the descendant of this scroll view is scrolled off
14641522
* screen.
@@ -1951,19 +2009,24 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
19512009
* which means we want to scroll towards the top.
19522010
*/
19532011
fun fling(velocityX: Int, velocityY: Int) {
1954-
if (childCount > 0) {
1955-
val view = getChildAt(0)
1956-
val width = width - paddingRight - paddingLeft
1957-
val right = view.width
2012+
if (isPagingEnabled) {
2013+
smoothScrollToPage(velocityX, velocityY)
2014+
} else {
2015+
if (childCount > 0) {
2016+
val view = getChildAt(0)
2017+
val width = width - paddingRight - paddingLeft
2018+
val right = view.width
19582019

1959-
val height = height - paddingBottom - paddingTop
1960-
val bottom = view.height
2020+
val height = height - paddingBottom - paddingTop
2021+
val bottom = view.height
19612022

1962-
mScroller.fling(scrollX, scrollY, velocityX, velocityY, 0, max(0, right - width), 0,
1963-
max(0, bottom - height), width / 2, height / 2)
2023+
mScroller.fling(scrollX, scrollY, velocityX, velocityY, 0, max(0, right - width), 0,
2024+
max(0, bottom - height), width / 2, height / 2)
19642025

1965-
ViewCompat.postInvalidateOnAnimation(this)
2026+
ViewCompat.postInvalidateOnAnimation(this)
2027+
}
19662028
}
2029+
handlePostTouchScrolling()
19672030
}
19682031

19692032
private fun flingWithNestedDispatch(velocityX: Int, velocityY: Int) {
@@ -2158,6 +2221,30 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
21582221
}
21592222
}
21602223

2224+
inner class PostTouchRunnable : Runnable {
2225+
private var mSnappingToPage = false
2226+
2227+
override fun run() {
2228+
if (mActivelyScrolling) {
2229+
mActivelyScrolling = false
2230+
ViewCompat.postOnAnimationDelayed(this@SuperScrollView, this, MOMENTUM_DELAY)
2231+
2232+
} else {
2233+
var doneWithAllScrolling = true
2234+
if (isPagingEnabled && !mSnappingToPage) {
2235+
mSnappingToPage = true
2236+
smoothScrollToPage()
2237+
doneWithAllScrolling = false
2238+
}
2239+
if (doneWithAllScrolling) {
2240+
mPostTouchRunnable = null
2241+
} else {
2242+
ViewCompat.postOnAnimationDelayed(this@SuperScrollView, this, MOMENTUM_DELAY)
2243+
}
2244+
}
2245+
}
2246+
}
2247+
21612248
internal class AccessibilityDelegate : AccessibilityDelegateCompat() {
21622249
override fun performAccessibilityAction(host: View, action: Int, arguments: Bundle): Boolean {
21632250
if (super.performAccessibilityAction(host, action, arguments)) {
@@ -2243,6 +2330,8 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
22432330

22442331
private const val TAG = "SuperScrollView"
22452332

2333+
const val MOMENTUM_DELAY = 20L
2334+
22462335
/**
22472336
* Sentinel value for no current active pointer.
22482337
* Used by [.mActivePointerId].
@@ -2251,8 +2340,6 @@ class SuperScrollView @JvmOverloads constructor(context: Context, attrs: Attribu
22512340

22522341
private val ACCESSIBILITY_DELEGATE = AccessibilityDelegate()
22532342

2254-
private val SCROLLVIEW_STYLEABLE = intArrayOf(android.R.attr.fillViewport)
2255-
22562343
/**
22572344
* Return true if child is a descendant of parent, (or equal to the parent).
22582345
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<declare-styleable name="SuperScrollView">
4+
<attr name="isPagingEnabled" format="boolean"/>
5+
<attr name="isFillViewport" format="boolean"/>
6+
7+
</declare-styleable>
8+
</resources>

0 commit comments

Comments
 (0)