Advertisement
Guest User

Untitled

a guest
Oct 19th, 2019
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.82 KB | None | 0 0
  1. package com.hvasc.ui
  2.  
  3. import android.annotation.SuppressLint
  4. import android.content.Context
  5. import android.util.AttributeSet
  6. import android.util.Log
  7. import android.view.MotionEvent
  8. import android.view.VelocityTracker
  9. import android.view.View
  10. import android.view.ViewConfiguration
  11. import android.webkit.WebView
  12. import android.widget.OverScroller
  13. import androidx.core.view.NestedScrollingChild3
  14. import androidx.core.view.NestedScrollingChildHelper
  15. import androidx.core.view.ViewCompat
  16. import androidx.customview.widget.ViewDragHelper.INVALID_POINTER
  17. import kotlin.math.abs
  18.  
  19.  
  20.  
  21. class NestedScrollingWebView : WebView, NestedScrollingChild3 {
  22.  
  23. private var mLastMotionY: Int = 0
  24.  
  25. private val mScrollOffset = IntArray(2)
  26. private val mScrollConsumed = IntArray(2)
  27.  
  28. private var mNestedOffsetY: Float = 0f
  29. private val mChildHelper = NestedScrollingChildHelper(rootView)
  30. private var mIsBeingDragged = false
  31. private var mVelocityTracker: VelocityTracker? = null
  32. private var mTouchSlop: Int
  33. private var mMinimumVelocity: Float
  34. private var mMaximumVelocity: Float
  35. private var mActivePointerId: Int = INVALID_POINTER
  36.  
  37. private var mScroller: OverScroller = OverScroller(context)
  38.  
  39. init {
  40. isNestedScrollingEnabled = true
  41. val configuration = ViewConfiguration.get(context)
  42. mTouchSlop = configuration.scaledTouchSlop
  43. mMinimumVelocity = configuration.scaledMinimumFlingVelocity.toFloat()
  44. mMaximumVelocity = configuration.scaledMaximumFlingVelocity.toFloat()
  45. }
  46.  
  47. constructor(context: Context) : super(context)
  48.  
  49. constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
  50.  
  51. constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
  52. context,
  53. attrs,
  54. defStyleAttr
  55. )
  56.  
  57. override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
  58. val action = ev.action
  59. if (action == MotionEvent.ACTION_MOVE && mIsBeingDragged) { // most common
  60. return true
  61. }
  62.  
  63. when (action and MotionEvent.ACTION_MASK) {
  64. MotionEvent.ACTION_MOVE -> {
  65. val activePointerId = mActivePointerId
  66. if (activePointerId == INVALID_POINTER) {
  67. return super.onTouchEvent(ev)
  68. }
  69.  
  70. val pointerIndex = ev.findPointerIndex(activePointerId)
  71. if (pointerIndex == -1) {
  72. Log.e(
  73. TAG, "Invalid pointerId=" + activePointerId
  74. + " in onInterceptTouchEvent"
  75. )
  76. return super.onTouchEvent(ev)
  77. }
  78.  
  79. val y = ev.getY(pointerIndex).toInt()
  80. val yDiff = Math.abs(y - mLastMotionY)
  81. if (yDiff > mTouchSlop && nestedScrollAxes and ViewCompat.SCROLL_AXIS_VERTICAL == 0) {
  82. mIsBeingDragged = true
  83. mLastMotionY = y
  84. initVelocityTrackerIfNotExists()
  85. mVelocityTracker!!.addMovement(ev)
  86. mNestedOffsetY = 0f
  87. val parent = parent
  88. parent?.requestDisallowInterceptTouchEvent(true)
  89. }
  90. }
  91. MotionEvent.ACTION_DOWN -> {
  92. mLastMotionY = ev.y.toInt()
  93. mActivePointerId = ev.getPointerId(0)
  94.  
  95. initOrResetVelocityTracker()
  96. mVelocityTracker!!.addMovement(ev)
  97.  
  98. mScroller.computeScrollOffset()
  99. mIsBeingDragged = !mScroller.isFinished
  100.  
  101. startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
  102. }
  103. MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
  104. mIsBeingDragged = false
  105. mActivePointerId = INVALID_POINTER
  106. recycleVelocityTracker()
  107. if (mScroller.springBack(scrollX, scrollY, 0, 0, 0, getScrollRange())) {
  108. ViewCompat.postInvalidateOnAnimation(this)
  109. }
  110. stopNestedScroll()
  111. }
  112. }
  113.  
  114. return mIsBeingDragged
  115. }
  116.  
  117. @SuppressLint("ClickableViewAccessibility")
  118. override fun onTouchEvent(event: MotionEvent): Boolean {
  119.  
  120. initVelocityTrackerIfNotExists()
  121.  
  122. val action = event.actionMasked
  123.  
  124. if (action == MotionEvent.ACTION_DOWN) {
  125. mNestedOffsetY = 0f
  126. }
  127.  
  128. val trackedEvent = MotionEvent.obtain(event)
  129. trackedEvent.offsetLocation(0f, mNestedOffsetY)
  130. Log.d(TAG, "$action")
  131. when (action) {
  132. MotionEvent.ACTION_DOWN -> {
  133. if (mIsBeingDragged) {
  134. val parent = parent
  135. parent?.requestDisallowInterceptTouchEvent(true)
  136. }
  137.  
  138. if (!mScroller.isFinished) {
  139. abortAnimatedScroll()
  140. }
  141.  
  142. mLastMotionY = event.y.toInt()
  143. mActivePointerId = event.getPointerId(0)
  144. startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH)
  145. }
  146. MotionEvent.ACTION_MOVE -> {
  147. val activePointerIndex = event.findPointerIndex(mActivePointerId)
  148. if (activePointerIndex == -1) {
  149. Log.e(TAG, "Invalid pointerId=$mActivePointerId in onTouchEvent")
  150. return super.onTouchEvent(event)
  151. }
  152.  
  153. val y = event.getY(activePointerIndex).toInt()
  154. var deltaY = mLastMotionY - y
  155.  
  156. if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset, ViewCompat.TYPE_TOUCH)) {
  157. deltaY -= mScrollConsumed[1]
  158. mNestedOffsetY += mScrollOffset[1]
  159. }
  160. if (!mIsBeingDragged && abs(deltaY) > mTouchSlop) {
  161. parent?.requestDisallowInterceptTouchEvent(true)
  162. mIsBeingDragged = true
  163. if (deltaY > 0) {
  164. deltaY -= mTouchSlop
  165. } else {
  166. deltaY += mTouchSlop
  167. }
  168. }
  169. if (mIsBeingDragged) {
  170. mLastMotionY = y - mScrollOffset[1]
  171.  
  172. val oldY = scrollY
  173. val range = getScrollRange()
  174. val overscrollMode = overScrollMode
  175. val canOverscroll =
  176. overscrollMode == View.OVER_SCROLL_ALWAYS || overscrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0
  177.  
  178. if (overScrollByCompat(
  179. 0, deltaY, 0, oldY, 0, range, 0,
  180. 0, true
  181. ) && !hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)
  182. ) {
  183. mVelocityTracker?.clear()
  184. }
  185.  
  186. val scrolledDeltaY = scrollY - oldY
  187. val dyUnconsumed = deltaY - scrolledDeltaY
  188.  
  189. mScrollConsumed[1] = 0
  190.  
  191. dispatchNestedScroll(
  192. 0, scrolledDeltaY, 0, dyUnconsumed, mScrollOffset, ViewCompat.TYPE_TOUCH, mScrollConsumed
  193. )
  194.  
  195. mLastMotionY -= mScrollOffset[1]
  196. mNestedOffsetY += mScrollOffset[1]
  197.  
  198. if (canOverscroll) {
  199. deltaY -= mScrollConsumed[1]
  200. }
  201.  
  202. }
  203. }
  204. MotionEvent.ACTION_UP -> {
  205. mVelocityTracker?.let {velocityTracker ->
  206. velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity)
  207. val initialVelocity = velocityTracker.getYVelocity(mActivePointerId).toInt()
  208. if (abs(initialVelocity) > mMinimumVelocity) {
  209. if (!dispatchNestedPreFling(0f, (-initialVelocity).toFloat())) {
  210. fling(-initialVelocity)
  211. }
  212. } else if (mScroller.springBack(
  213. scrollX, scrollY, 0, 0, 0,
  214. getScrollRange())) {
  215. ViewCompat.postInvalidateOnAnimation(this)
  216. }
  217. }
  218. mActivePointerId = INVALID_POINTER
  219. endDrag()
  220. }
  221. MotionEvent.ACTION_CANCEL -> {
  222. if (mIsBeingDragged) {
  223. if (mScroller.springBack(
  224. scrollX, scrollY, 0, 0, 0,
  225. getScrollRange()
  226. )
  227. ) {
  228. ViewCompat.postInvalidateOnAnimation(this)
  229. }
  230. }
  231. mActivePointerId = INVALID_POINTER
  232. endDrag()
  233. }
  234. }
  235.  
  236. mVelocityTracker?.addMovement(trackedEvent)
  237. trackedEvent.recycle()
  238.  
  239. return super.onTouchEvent(event)
  240. }
  241.  
  242. private fun abortAnimatedScroll() {
  243. mScroller.abortAnimation()
  244. stopNestedScroll(ViewCompat.TYPE_NON_TOUCH)
  245. }
  246.  
  247. private fun endDrag() {
  248. mIsBeingDragged = false
  249. recycleVelocityTracker()
  250. stopNestedScroll()
  251. }
  252.  
  253. private fun getScrollRange(): Int {
  254. return computeVerticalScrollRange()
  255. }
  256.  
  257. private fun fling(velocityY: Int) {
  258. val height = height
  259. mScroller.fling(
  260. scrollX, scrollY, // start
  261. 0, velocityY, // velocities
  262. 0, 0, // x
  263. Integer.MIN_VALUE, Integer.MAX_VALUE, // y
  264. 0, height / 2
  265. )
  266. runAnimatedScroll(true)
  267. }
  268.  
  269. private fun runAnimatedScroll(participateInNestedScrolling: Boolean) {
  270. if (participateInNestedScrolling) {
  271. startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH)
  272. } else {
  273. stopNestedScroll(ViewCompat.TYPE_NON_TOUCH)
  274. }
  275. mLastMotionY = scrollY
  276. ViewCompat.postInvalidateOnAnimation(this)
  277. }
  278.  
  279. override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
  280. if (disallowIntercept) {
  281. recycleVelocityTracker()
  282. }
  283. super.requestDisallowInterceptTouchEvent(disallowIntercept)
  284. }
  285.  
  286. private fun initOrResetVelocityTracker() {
  287. if (mVelocityTracker == null) {
  288. mVelocityTracker = VelocityTracker.obtain()
  289. } else {
  290. mVelocityTracker?.clear()
  291. }
  292. }
  293.  
  294. private fun initVelocityTrackerIfNotExists() {
  295. if (mVelocityTracker == null) {
  296. mVelocityTracker = VelocityTracker.obtain()
  297. }
  298. }
  299.  
  300. private fun recycleVelocityTracker() {
  301. mVelocityTracker?.recycle()
  302. mVelocityTracker = null
  303. }
  304.  
  305. override fun overScrollBy(
  306. deltaX: Int, deltaY: Int,
  307. scrollX: Int, scrollY: Int,
  308. scrollRangeX: Int, scrollRangeY: Int,
  309. maxOverScrollX: Int, maxOverScrollY: Int,
  310. isTouchEvent: Boolean
  311. ): Boolean {
  312. // this is causing double scroll call (doubled speed), but this WebView isn't overscrollable
  313. // all overscrolls are passed to appbar, so commenting this out during drag
  314. if (!mIsBeingDragged) {
  315. overScrollByCompat(
  316. deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
  317. maxOverScrollX, maxOverScrollY, isTouchEvent
  318. )
  319. }
  320. // without this call webview won't scroll to top when url change or when user pick input
  321. // (webview should move a bit making input still in viewport when "adjustResize")
  322. return true
  323. }
  324.  
  325. // NestedScrollingChild
  326.  
  327. override fun isNestedScrollingEnabled(): Boolean {
  328. return mChildHelper.isNestedScrollingEnabled
  329. }
  330.  
  331. override fun setNestedScrollingEnabled(enabled: Boolean) {
  332. mChildHelper.isNestedScrollingEnabled = enabled
  333. }
  334.  
  335. override fun startNestedScroll(axes: Int, type: Int): Boolean {
  336. return mChildHelper.startNestedScroll(axes, type)
  337. }
  338.  
  339. override fun startNestedScroll(axes: Int): Boolean {
  340. return startNestedScroll(axes, ViewCompat.TYPE_TOUCH)
  341. }
  342.  
  343. override fun stopNestedScroll(type: Int) {
  344. mChildHelper.stopNestedScroll(type)
  345. }
  346.  
  347. override fun stopNestedScroll() {
  348. stopNestedScroll(ViewCompat.TYPE_TOUCH)
  349. }
  350.  
  351. override fun hasNestedScrollingParent(type: Int): Boolean {
  352. return mChildHelper.hasNestedScrollingParent(type)
  353. }
  354.  
  355. override fun hasNestedScrollingParent(): Boolean {
  356. return hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)
  357. }
  358.  
  359. override fun dispatchNestedScroll(
  360. dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int,
  361. offsetInWindow: IntArray?
  362. ): Boolean {
  363. return dispatchNestedScroll(
  364. dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
  365. offsetInWindow, ViewCompat.TYPE_TOUCH
  366. )
  367. }
  368.  
  369. override fun dispatchNestedScroll(
  370. dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int,
  371. offsetInWindow: IntArray?, type: Int
  372. ): Boolean {
  373. return mChildHelper.dispatchNestedScroll(
  374. dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
  375. offsetInWindow, type
  376. )
  377. }
  378.  
  379. override fun dispatchNestedScroll(
  380. dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int,
  381. offsetInWindow: IntArray?, type: Int, consumed: IntArray
  382. ) {
  383. mChildHelper.dispatchNestedScroll(
  384. dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
  385. offsetInWindow, type, consumed
  386. )
  387. }
  388.  
  389. override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?): Boolean {
  390. return dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH)
  391. }
  392.  
  393.  
  394. override fun dispatchNestedPreScroll(
  395. dx: Int,
  396. dy: Int,
  397. consumed: IntArray?,
  398. offsetInWindow: IntArray?,
  399. type: Int
  400. ): Boolean {
  401. return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type)
  402. }
  403.  
  404. override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean): Boolean {
  405. return mChildHelper.dispatchNestedFling(velocityX, velocityY, false)
  406. }
  407.  
  408. override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
  409. return mChildHelper.dispatchNestedPreFling(velocityX, velocityY)
  410. }
  411.  
  412. override fun getNestedScrollAxes(): Int {
  413. return ViewCompat.SCROLL_AXIS_VERTICAL
  414. }
  415.  
  416. override fun computeScroll() {
  417. if (mScroller.isFinished) {
  418. return
  419. }
  420.  
  421. mScroller.computeScrollOffset()
  422. val y = mScroller.currY
  423. var unconsumed = y - mLastMotionY
  424. mLastMotionY = y
  425.  
  426. // Nested Scrolling Pre Pass
  427. mScrollConsumed[1] = 0
  428. dispatchNestedPreScroll(
  429. 0, unconsumed, mScrollConsumed, null,
  430. ViewCompat.TYPE_NON_TOUCH
  431. )
  432. unconsumed -= mScrollConsumed[1]
  433.  
  434.  
  435. if (unconsumed != 0) {
  436. // Internal Scroll
  437. val oldScrollY = scrollY
  438. overScrollByCompat(
  439. 0, unconsumed, scrollX, oldScrollY, 0, computeVerticalScrollRange(),
  440. 0, 0, false
  441. )
  442. val scrolledByMe = scrollY - oldScrollY
  443. unconsumed -= scrolledByMe
  444.  
  445. // Nested Scrolling Post Pass
  446. mScrollConsumed[1] = 0
  447. dispatchNestedScroll(
  448. 0, 0, 0, unconsumed, mScrollOffset,
  449. ViewCompat.TYPE_NON_TOUCH, mScrollConsumed
  450. )
  451. unconsumed -= mScrollConsumed[1]
  452. }
  453.  
  454. if (unconsumed != 0) {
  455. abortAnimatedScroll()
  456. }
  457.  
  458. if (!mScroller!!.isFinished) {
  459. ViewCompat.postInvalidateOnAnimation(this)
  460. }
  461. }
  462.  
  463. // copied from NestedScrollView exacly as it looks, leaving overscroll related code, maybe future use
  464. private fun overScrollByCompat(
  465. deltaX: Int, deltaY: Int,
  466. scrollX: Int, scrollY: Int,
  467. scrollRangeX: Int, scrollRangeY: Int,
  468. maxOverScrollX: Int, maxOverScrollY: Int,
  469. isTouchEvent: Boolean
  470. ): Boolean {
  471. var maxOverScrollX = maxOverScrollX
  472. var maxOverScrollY = maxOverScrollY
  473. val overScrollMode = overScrollMode
  474. val canScrollHorizontal = computeHorizontalScrollRange() > computeHorizontalScrollExtent()
  475. val canScrollVertical = computeVerticalScrollRange() > computeVerticalScrollExtent()
  476. val overScrollHorizontal =
  477. overScrollMode == View.OVER_SCROLL_ALWAYS || overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal
  478. val overScrollVertical =
  479. overScrollMode == View.OVER_SCROLL_ALWAYS || overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical
  480.  
  481. var newScrollX = scrollX + deltaX
  482. if (!overScrollHorizontal) {
  483. maxOverScrollX = 0
  484. }
  485.  
  486. var newScrollY = scrollY + deltaY
  487. if (!overScrollVertical) {
  488. maxOverScrollY = 0
  489. }
  490.  
  491. // Clamp values if at the limits and record
  492. val left = -maxOverScrollX
  493. val right = maxOverScrollX + scrollRangeX
  494. val top = -maxOverScrollY
  495. val bottom = maxOverScrollY + scrollRangeY
  496.  
  497. var clampedX = false
  498. if (newScrollX > right) {
  499. newScrollX = right
  500. clampedX = true
  501. } else if (newScrollX < left) {
  502. newScrollX = left
  503. clampedX = true
  504. }
  505.  
  506. var clampedY = false
  507. if (newScrollY > bottom) {
  508. newScrollY = bottom
  509. clampedY = true
  510. } else if (newScrollY < top) {
  511. newScrollY = top
  512. clampedY = true
  513. }
  514.  
  515. if (clampedY && !hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
  516. mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, computeVerticalScrollRange())
  517. }
  518.  
  519. onOverScrolled(newScrollX, newScrollY, clampedX, clampedY)
  520.  
  521. return clampedX || clampedY
  522. }
  523.  
  524. companion object {
  525. val TAG = NestedScrollingWebView::class.java.simpleName
  526. }
  527.  
  528. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement