1
+ package com.ooftf.vertical.nested
2
+
3
+ import android.content.Context
4
+ import android.os.Build
5
+ import android.support.annotation.RequiresApi
6
+ import android.support.v4.view.NestedScrollingParent2
7
+ import android.support.v4.view.NestedScrollingParentHelper
8
+ import android.support.v4.view.PagerAdapter
9
+ import android.util.AttributeSet
10
+ import android.util.Log
11
+ import android.view.MotionEvent
12
+ import android.view.View
13
+ import android.widget.FrameLayout
14
+ import android.widget.Scroller
15
+
16
+
17
+ /* *
18
+ * 高仿淘宝商品页上下分页布局
19
+ * Created by master on 2016/3/28.
20
+ */
21
+ class VerticalPagerLayout : FrameLayout , NestedScrollingParent2 {
22
+ var helper = NestedScrollingParentHelper (this )
23
+ override fun onNestedPreScroll (target : View , dx : Int , dy : Int , consumed : IntArray? , type : Int ) {
24
+ moved = true
25
+ Log .e(" onNestedPreScroll" , " dy::" + dy)
26
+ if (scrollY % height != 0 ) {
27
+ scrollBy(0 , dy)
28
+ consumed?.set(1 , dy)
29
+ }
30
+ }
31
+
32
+ override fun onStopNestedScroll (target : View , type : Int ) {
33
+ helper.onStopNestedScroll(target, type)
34
+ Log .e(" onStopNestedScroll" , " onStopNestedScroll" )
35
+ judgePage(true )
36
+ }
37
+
38
+ override fun onStartNestedScroll (child : View , target : View , axes : Int , type : Int ): Boolean {
39
+ mScroller.forceFinished(true )
40
+ Log .e(" onStartNestedScroll" , " onStartNestedScroll" )
41
+ moved = false
42
+ return true
43
+ }
44
+
45
+ override fun onNestedScrollAccepted (child : View , target : View , axes : Int , type : Int ) {
46
+ Log .e(" onNestedScrollAccepted" , " onNestedScrollAccepted" )
47
+ helper.onNestedScrollAccepted(child, target, axes, type)
48
+ }
49
+
50
+ var moved = false
51
+ override fun onNestedScroll (target : View , dxConsumed : Int , dyConsumed : Int , dxUnconsumed : Int , dyUnconsumed : Int , type : Int ) {
52
+ Log .e(" onNestedScroll" , " dyConsumed::" + dyConsumed + " ,dyUnconsumed::" + dyUnconsumed)
53
+ scrollBy(0 , dyUnconsumed)
54
+ }
55
+
56
+
57
+ /* *
58
+ * 松开时布局滑动动画时间
59
+ */
60
+ private var SCROLL_DURATION = 700
61
+
62
+ var offscreenPageLimit = 1 ;
63
+ private var mScroller: Scroller
64
+ private var items = ArrayList <ItemInfo >()
65
+ var adapter: PagerAdapter ? = null
66
+ set(value) {
67
+ field = value
68
+ resetLayout()
69
+ }
70
+
71
+ override fun onInterceptHoverEvent (event : MotionEvent ? ): Boolean {
72
+ Log .e(" onInterceptHoverEvent" , " ..." )
73
+ return super .onInterceptHoverEvent(event)
74
+ }
75
+
76
+ /* *
77
+ * 当更换adapter之后,重置所有信息
78
+ */
79
+ private fun resetLayout () {
80
+ removeAllItem()
81
+ scrollTo(0 , 0 )
82
+ refreshViews()
83
+ }
84
+
85
+ constructor (context: Context ) : super (context)
86
+
87
+
88
+ constructor (context: Context , attrs: AttributeSet ? ) : super (context, attrs)
89
+
90
+ constructor (context: Context , attrs: AttributeSet ? , defStyleAttr: Int ) : super (context, attrs, defStyleAttr)
91
+
92
+ @RequiresApi(api = Build .VERSION_CODES .LOLLIPOP )
93
+ constructor (context: Context , attrs: AttributeSet ? , defStyleAttr: Int , defStyleRes: Int ) : super (context, attrs, defStyleAttr, defStyleRes)
94
+
95
+ init {
96
+ mScroller = Scroller (context)
97
+ }
98
+
99
+ /* *
100
+ * 获取到点击事件的Page 如要是为了防止一次滑动翻超过一页
101
+ */
102
+ private var actionDownPage = 0
103
+
104
+
105
+ override fun scrollTo (x : Int , y : Int ) {
106
+ refreshViews()
107
+ super .scrollTo(x, y)
108
+ }
109
+
110
+ private fun refreshViews () {
111
+ // 移除不必要View
112
+ adapter ? : return
113
+ adapter?.startUpdate(this )
114
+ items
115
+ .filter { it.position < getCurrentPage() - offscreenPageLimit || it.position > getCurrentPage() + offscreenPageLimit }
116
+ .forEach {
117
+ removeForItemInfo(it)
118
+ }
119
+ (getCurrentPage() - offscreenPageLimit.. getCurrentPage() + offscreenPageLimit).forEach {
120
+ addNewView(it)
121
+ }
122
+ adapter?.setPrimaryItem(this , getCurrentPage(), itemInfoForPosition(getCurrentPage()).obj)
123
+ adapter?.finishUpdate(this )
124
+
125
+ }
126
+
127
+ /* *
128
+ * 移除所有的View
129
+ */
130
+ private fun removeAllItem () {
131
+ adapter?.startUpdate(this )
132
+ items.forEach {
133
+ adapter?.destroyItem(this , it.position, it.obj)
134
+ }
135
+ items.clear()
136
+ adapter?.finishUpdate(this )
137
+ }
138
+
139
+ /* *
140
+ * 根据ItemInfo 移除View
141
+ */
142
+ private fun removeForItemInfo (item : ItemInfo ) {
143
+ adapter?.destroyItem(this , item.position, item.obj)
144
+ items.remove(item)
145
+ }
146
+
147
+ /* *
148
+ * 根据位置信息 添加新的view
149
+ */
150
+ private fun addNewView (position : Int ) {
151
+ if (position < 0 && position >= adapter!! .count) return
152
+ items.forEach { if (it.position == position) return }
153
+ items.add(ItemInfo (position, adapter!! .instantiateItem(this , position)))
154
+ }
155
+
156
+ /* *
157
+ * 获取到指定位置的View
158
+ */
159
+ private fun viewForPosition (position : Int ): View {
160
+ val item = itemInfoForPosition(position)
161
+ return viewForItemInfo(item)
162
+ }
163
+
164
+ private fun viewForItemInfo (itemInfo : ItemInfo ): View {
165
+ (0 until childCount).forEach {
166
+ if (adapter!! .isViewFromObject(getChildAt(it), itemInfo.obj)) {
167
+ return getChildAt(it)
168
+ }
169
+ }
170
+ throw NullPointerException ()
171
+ }
172
+
173
+ private fun itemInfoForPosition (position : Int ): ItemInfo {
174
+ items.forEach {
175
+ if (position == it.position) {
176
+ return it
177
+ }
178
+ }
179
+ throw NullPointerException ()
180
+ }
181
+
182
+
183
+ /* *
184
+ * 判断应该停留在哪一页
185
+ */
186
+ private fun judgePage (smooth : Boolean ) {
187
+ setCurrentItem(getCurrentPage(), smooth)
188
+ }
189
+
190
+ /* *
191
+ * 滚动到指定页面
192
+ */
193
+ fun setCurrentItem (page : Int , smooth : Boolean = true) {
194
+ if (height == 0 ) {
195
+ post { setCurrentItem(page, smooth) }
196
+ return
197
+ }
198
+ if (smooth) {
199
+ mScroller.startScroll(0 , scrollY, 0 , height * pageController(page) - scrollY, SCROLL_DURATION )
200
+ invalidate()
201
+ } else {
202
+ scrollTo(0 , height * pageController(page))
203
+ }
204
+ }
205
+
206
+ /* *
207
+ * 防止position超出边缘
208
+ */
209
+ private fun pageController (src : Int ): Int {
210
+ if (src < 0 ) {
211
+ return 0
212
+ }
213
+ return if (src > adapter!! .count - 1 ) {
214
+ adapter!! .count - 1
215
+ } else src
216
+ }
217
+
218
+ /* *
219
+ * 得到的是当前占有试图最大的页面
220
+ */
221
+ private fun getCurrentPage (): Int {
222
+ if (height == 0 ) return 0
223
+ return Math .round(scrollY.toFloat() / height)
224
+ }
225
+
226
+ override fun onLayout (changed : Boolean , l : Int , t : Int , r : Int , b : Int ) {
227
+ val height = b - t
228
+ (0 until childCount).forEach {
229
+ var itemInfo = itemInfoForView(getChildAt(it))
230
+ getChildAt(it).layout(0 , itemInfo.position * height, r - l, (itemInfo.position + 1 ) * height)
231
+ }
232
+ }
233
+
234
+ private fun itemInfoForView (child : View ): ItemInfo {
235
+ items.forEach {
236
+ if (adapter!! .isViewFromObject(child, it.obj)) {
237
+ return it
238
+ }
239
+ }
240
+ throw NullPointerException ()
241
+ }
242
+
243
+ override fun computeScroll () {
244
+ if (mScroller.computeScrollOffset() && ! mScroller.isFinished) {
245
+ scrollTo(mScroller.currX, mScroller.currY)
246
+ postInvalidate()
247
+ }
248
+ }
249
+
250
+ /* fun getCurrentPage(): Int {
251
+ if (height == 0) return 0
252
+ return Math.round(scrollY.toFloat() / height)
253
+ }*/
254
+
255
+ class ItemInfo (var position : Int , var obj : Any )
256
+
257
+
258
+ }
0 commit comments