diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/TransactionsFragment.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/TransactionsFragment.kt index 3629f87..5695a79 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/TransactionsFragment.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/TransactionsFragment.kt @@ -1,11 +1,9 @@ package cy.agorise.bitsybitshareswallet.fragments -import android.graphics.Point import android.os.Bundle import android.preference.PreferenceManager import android.view.* import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.LinearLayoutManager @@ -21,11 +19,6 @@ class TransactionsFragment : Fragment() { private lateinit var mTransferDetailViewModel: TransferDetailViewModel - /** Variables used for the RecyclerView pull springy animation */ - private var bounceTouchListener: BounceTouchListener? = null - private var pivotY1: Float = 0.toFloat() - private var pivotY2:Float = 0.toFloat() - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -52,47 +45,11 @@ class TransactionsFragment : Fragment() { transfersDetailsAdapter.replaceAll(transfersDetails) }) - // Create bouncy effect when user tries to over scroll - rvTransactions.pivotX = getScreenWidth(activity) * 0.5f - - pivotY1 = 0f - pivotY2 = getScreenHeight(activity) * .5f - - bounceTouchListener = - BounceTouchListener.create(rvTransactions, object : BounceTouchListener.OnTranslateListener { - override fun onTranslate(translation: Float) { - if (translation > 0) { - bounceTouchListener?.setMaxAbsTranslation(-99) - rvTransactions.pivotY = pivotY1 - val scale = 2 * translation / rvTransactions.measuredHeight + 1 - rvTransactions.scaleY = Math.pow(scale.toDouble(), .6).toFloat() - } else { - bounceTouchListener?.setMaxAbsTranslation((pivotY2 * .33f).toInt()) - rvTransactions.pivotY = pivotY2 - val scale = 2 * translation / rvTransactions.measuredHeight + 1 - rvTransactions.scaleY = Math.pow(scale.toDouble(), .5).toFloat() - } - } - }) - // Sets custom touch listener to handle bounce/stretch effect + val bounceTouchListener = BounceTouchListener(rvTransactions) rvTransactions.setOnTouchListener(bounceTouchListener) } - private fun getScreenWidth(activity: FragmentActivity?): Int { - val display = activity?.windowManager?.defaultDisplay - val size = Point() - display?.getSize(size) - return size.x - } - - private fun getScreenHeight(activity: FragmentActivity?): Int { - val display = activity?.windowManager?.defaultDisplay - val size = Point() - display?.getSize(size) - return size.y - } - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.menu_transactions, menu) } diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/BounceTouchListener.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/BounceTouchListener.kt index 9d4c306..237e823 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/BounceTouchListener.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/BounceTouchListener.kt @@ -7,99 +7,36 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.animation.DecelerateInterpolator -import android.widget.ListView -import android.widget.ScrollView -import androidx.annotation.IdRes import androidx.core.view.MotionEventCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.StaggeredGridLayoutManager -class BounceTouchListener private constructor( - private val mMainView: View, - contentResId: Int, - private val onTranslateListener: OnTranslateListener? -) : - View.OnTouchListener { +class BounceTouchListener(private val mRecyclerView: RecyclerView) : View.OnTouchListener { private var downCalled = false - private val mContent: View = if (contentResId == -1) mMainView else mMainView.findViewById(contentResId) private var mDownY: Float = 0.toFloat() private var mSwipingDown: Boolean = false private var mSwipingUp: Boolean = false private val mInterpolator = DecelerateInterpolator(3f) - private val swipUpEnabled = true private var mActivePointerId = -99 private var mLastTouchY = -99f private var mMaxAbsTranslation = -99 + init { + mRecyclerView.pivotY = 0F + } override fun onTouch(view: View, motionEvent: MotionEvent): Boolean { val action = MotionEventCompat.getActionMasked(motionEvent) when (action) { MotionEvent.ACTION_DOWN -> { - run { - onDownMotionEvent(motionEvent) - view.onTouchEvent(motionEvent) - downCalled = true - if (mContent.translationY == 0f) { - return false - } - } - run { - if (mActivePointerId == -99) { - onDownMotionEvent(motionEvent) - downCalled = true - } - val pointerIndex = MotionEventCompat.findPointerIndex(motionEvent, mActivePointerId) - val y = MotionEventCompat.getY(motionEvent, pointerIndex) - - if (!hasHitTop() && !hasHitBottom() || !downCalled) { - if (!downCalled) { - downCalled = true - } - mDownY = y - view.onTouchEvent(motionEvent) - return false - } - - val deltaY = y - mDownY - if (Math.abs(deltaY) > 0 && hasHitTop() && deltaY > 0) { - mSwipingDown = true - sendCancelEventToView(view, motionEvent) - } - if (swipUpEnabled) { - if (Math.abs(deltaY) > 0 && hasHitBottom() && deltaY < 0) { - mSwipingUp = true - sendCancelEventToView(view, motionEvent) - } - } - if (mSwipingDown || mSwipingUp) { - if (deltaY <= 0 && mSwipingDown || deltaY >= 0 && mSwipingUp) { - mDownY = 0f - mSwipingDown = false - mSwipingUp = false - downCalled = false - val downEvent = MotionEvent.obtain(motionEvent) - downEvent.action = MotionEvent.ACTION_DOWN or - (MotionEventCompat.getActionIndex(motionEvent) shl MotionEventCompat.ACTION_POINTER_INDEX_SHIFT) - view.onTouchEvent(downEvent) - } - var translation = - (deltaY / Math.abs(deltaY) * Math.pow(Math.abs(deltaY).toDouble(), .8)).toInt() - if (mMaxAbsTranslation > 0) { - if (translation < 0) { - translation = Math.max(-mMaxAbsTranslation, translation) - } else { - translation = Math.min(mMaxAbsTranslation, translation) - } - } - mContent.translationY = translation.toFloat() - onTranslateListener?.onTranslate(mContent.translationY) - - return true - } + onDownMotionEvent(motionEvent) + view.onTouchEvent(motionEvent) + downCalled = true + if (this.mRecyclerView.translationY == 0f) { + return false } } MotionEvent.ACTION_MOVE -> { @@ -122,11 +59,9 @@ class BounceTouchListener private constructor( mSwipingDown = true sendCancelEventToView(view, motionEvent) } - if (swipUpEnabled) { - if (Math.abs(deltaY) > 0 && hasHitBottom() && deltaY < 0) { - mSwipingUp = true - sendCancelEventToView(view, motionEvent) - } + if (Math.abs(deltaY) > 0 && hasHitBottom() && deltaY < 0) { + mSwipingUp = true + sendCancelEventToView(view, motionEvent) } if (mSwipingDown || mSwipingUp) { if (deltaY <= 0 && mSwipingDown || deltaY >= 0 && mSwipingUp) { @@ -147,8 +82,8 @@ class BounceTouchListener private constructor( translation = Math.min(mMaxAbsTranslation, translation) } } - mContent.translationY = translation.toFloat() - onTranslateListener?.onTranslate(mContent.translationY) + mRecyclerView.translationY = translation.toFloat() + translate(mRecyclerView.translationY) return true } } @@ -156,14 +91,14 @@ class BounceTouchListener private constructor( MotionEvent.ACTION_UP -> { mActivePointerId = -99 // cancel - mContent.animate() + mRecyclerView.animate() .setInterpolator(mInterpolator) .translationY(0f) - .setDuration(DEFAULT_ANIMATION_TIME) + .setDuration(600L) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator) { (animation as ValueAnimator).addUpdateListener { - onTranslateListener?.onTranslate(mContent.translationY) + translate(mRecyclerView.translationY) } super.onAnimationStart(animation) } @@ -188,13 +123,13 @@ class BounceTouchListener private constructor( mLastTouchY = MotionEventCompat.getY(motionEvent, newPointerIndex) mActivePointerId = MotionEventCompat.getPointerId(motionEvent, newPointerIndex) - if (mContent.translationY > 0) { - mDownY = mLastTouchY - Math.pow(mContent.translationY.toDouble(), (10f / 8f).toDouble()).toInt() - mContent.animate().cancel() - } else if (mContent.translationY < 0) { + if (this.mRecyclerView.translationY > 0) { + mDownY = mLastTouchY - Math.pow(this.mRecyclerView.translationY.toDouble(), (10f / 8f).toDouble()).toInt() + this.mRecyclerView.animate().cancel() + } else if (this.mRecyclerView.translationY < 0) { mDownY = mLastTouchY + - Math.pow((-mContent.translationY).toDouble(), (10f / 8f).toDouble()).toInt() - mContent.animate().cancel() + Math.pow((-this.mRecyclerView.translationY).toDouble(), (10f / 8f).toDouble()).toInt() + this.mRecyclerView.animate().cancel() } } } @@ -202,6 +137,11 @@ class BounceTouchListener private constructor( return false } + private fun translate(translation: Float) { + val scale = 2 * translation / mRecyclerView.measuredHeight + 1 + mRecyclerView.scaleY = Math.pow(scale.toDouble(), .6).toFloat() + } + private fun sendCancelEventToView(view: View, motionEvent: MotionEvent) { (view as ViewGroup).requestDisallowInterceptTouchEvent(true) val cancelEvent = MotionEvent.obtain(motionEvent) @@ -215,46 +155,32 @@ class BounceTouchListener private constructor( mLastTouchY = MotionEventCompat.getY(motionEvent, pointerIndex) mActivePointerId = MotionEventCompat.getPointerId(motionEvent, 0) - if (mContent.translationY > 0) { - mDownY = mLastTouchY - Math.pow(mContent.translationY.toDouble(), (10f / 8f).toDouble()).toInt() - mContent.animate().cancel() - } else if (mContent.translationY < 0) { - mDownY = mLastTouchY + Math.pow((-mContent.translationY).toDouble(), (10f / 8f).toDouble()).toInt() - mContent.animate().cancel() + if (this.mRecyclerView.translationY > 0) { + mDownY = mLastTouchY - Math.pow(this.mRecyclerView.translationY.toDouble(), (10f / 8f).toDouble()).toInt() + this.mRecyclerView.animate().cancel() + } else if (this.mRecyclerView.translationY < 0) { + mDownY = mLastTouchY + Math.pow((-this.mRecyclerView.translationY).toDouble(), (10f / 8f).toDouble()).toInt() + this.mRecyclerView.animate().cancel() } else { mDownY = mLastTouchY } } private fun hasHitBottom(): Boolean { - if (mMainView is ScrollView) { - val scrollView = mMainView - val view = scrollView.getChildAt(scrollView.childCount - 1) - val diff = view.bottom - (scrollView.height + scrollView.scrollY)// Calculate the scrolldiff - return diff == 0 - } else if (mMainView is ListView) { - val listView = mMainView - if (listView.adapter != null) { - if (listView.adapter.count > 0) { - return listView.lastVisiblePosition == listView.adapter.count - 1 && listView.getChildAt(listView.childCount - 1).bottom <= listView.height - } - } - } else if (mMainView is RecyclerView) { - val recyclerView = mMainView - if (recyclerView.adapter != null && recyclerView.layoutManager != null) { - val adapter = recyclerView.adapter - if (adapter!!.itemCount > 0) { - val layoutManager = recyclerView.layoutManager - if (layoutManager is LinearLayoutManager) { - val linearLayoutManager = layoutManager as LinearLayoutManager? - return linearLayoutManager!!.findLastCompletelyVisibleItemPosition() == adapter.itemCount - 1 - } else if (layoutManager is StaggeredGridLayoutManager) { - val staggeredGridLayoutManager = layoutManager as StaggeredGridLayoutManager? - val checks = staggeredGridLayoutManager!!.findLastCompletelyVisibleItemPositions(null) - for (check in checks) { - if (check == adapter.itemCount - 1) - return true - } + val recyclerView = this.mRecyclerView + if (recyclerView.adapter != null && recyclerView.layoutManager != null) { + val adapter = recyclerView.adapter + if (adapter!!.itemCount > 0) { + val layoutManager = recyclerView.layoutManager + if (layoutManager is LinearLayoutManager) { + val linearLayoutManager = layoutManager as LinearLayoutManager? + return linearLayoutManager!!.findLastCompletelyVisibleItemPosition() == adapter.itemCount - 1 + } else if (layoutManager is StaggeredGridLayoutManager) { + val staggeredGridLayoutManager = layoutManager as StaggeredGridLayoutManager? + val checks = staggeredGridLayoutManager!!.findLastCompletelyVisibleItemPositions(null) + for (check in checks) { + if (check == adapter.itemCount - 1) + return true } } } @@ -263,31 +189,20 @@ class BounceTouchListener private constructor( } private fun hasHitTop(): Boolean { - if (mMainView is ScrollView) { - return mMainView.scrollY == 0 - } else if (mMainView is ListView) { - val listView = mMainView - if (listView.adapter != null) { - if (listView.adapter.count > 0) { - return listView.firstVisiblePosition == 0 && listView.getChildAt(0).top >= 0 - } - } - } else if (mMainView is RecyclerView) { - val recyclerView = mMainView - if (recyclerView.adapter != null && recyclerView.layoutManager != null) { - val adapter = recyclerView.adapter - if (adapter!!.itemCount > 0) { - val layoutManager = recyclerView.layoutManager - if (layoutManager is LinearLayoutManager) { - val linearLayoutManager = layoutManager as LinearLayoutManager? - return linearLayoutManager!!.findFirstCompletelyVisibleItemPosition() == 0 - } else if (layoutManager is StaggeredGridLayoutManager) { - val staggeredGridLayoutManager = layoutManager as StaggeredGridLayoutManager? - val checks = staggeredGridLayoutManager!!.findFirstCompletelyVisibleItemPositions(null) - for (check in checks) { - if (check == 0) - return true - } + val recyclerView = this.mRecyclerView + if (recyclerView.adapter != null && recyclerView.layoutManager != null) { + val adapter = recyclerView.adapter + if (adapter!!.itemCount > 0) { + val layoutManager = recyclerView.layoutManager + if (layoutManager is LinearLayoutManager) { + val linearLayoutManager = layoutManager as LinearLayoutManager? + return linearLayoutManager!!.findFirstCompletelyVisibleItemPosition() == 0 + } else if (layoutManager is StaggeredGridLayoutManager) { + val staggeredGridLayoutManager = layoutManager as StaggeredGridLayoutManager? + val checks = staggeredGridLayoutManager!!.findFirstCompletelyVisibleItemPositions(null) + for (check in checks) { + if (check == 0) + return true } } } @@ -295,42 +210,4 @@ class BounceTouchListener private constructor( return false } - - fun setMaxAbsTranslation(maxAbsTranslation: Int) { - this.mMaxAbsTranslation = maxAbsTranslation - } - - interface OnTranslateListener { - fun onTranslate(translation: Float) - } - - companion object { - private val DEFAULT_ANIMATION_TIME = 600L - - /** - * Creates a new BounceTouchListener - * - * @param mainScrollableView The main view that this touch listener is attached to - * @param onTranslateListener To perform action on translation, can be null if not needed - * @return A new BounceTouchListener attached to the given scrollable view - */ - fun create(mainScrollableView: View, onTranslateListener: OnTranslateListener?): BounceTouchListener { - return create(mainScrollableView, -1, onTranslateListener) - } - - /** - * Creates a new BounceTouchListener - * - * @param mainView The main view that this touch listener is attached to - * @param contentResId Resource Id of the scrollable view - * @param onTranslateListener To perform action on translation, can be null if not needed - * @return A new BounceTouchListener attached to the given scrollable view - */ - fun create( - mainView: View, @IdRes contentResId: Int, - onTranslateListener: OnTranslateListener? - ): BounceTouchListener { - return BounceTouchListener(mainView, contentResId, onTranslateListener) - } - } } \ No newline at end of file