diff options
Diffstat (limited to 'core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt')
-rw-r--r-- | core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt | 237 |
1 files changed, 0 insertions, 237 deletions
diff --git a/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt b/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt deleted file mode 100644 index c39a278..0000000 --- a/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ca.allanwang.kau.widgets - -import android.app.Activity -import android.content.Context -import android.graphics.Color -import android.util.AttributeSet -import android.view.View -import android.widget.FrameLayout -import ca.allanwang.kau.R -import ca.allanwang.kau.utils.* - -/** - * A [FrameLayout] which responds to nested scrolls to create drag-dismissable layouts. - * Applies an elasticity factor to reduce movement as you approach the given dismiss distance. - * Optionally also scales down content during drag. - */ -class ElasticDragDismissFrameLayout @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) { - - // configurable attribs - var dragDismissDistance = context.dimen(R.dimen.kau_drag_dismiss_distance).dpToPx - var dragDismissFraction = -1f - var dragDismissScale = 1f - set(value) { - field = value - shouldScale = value != 1f - } - private var shouldScale = false - var dragElacticity = 0.8f - - // state - private var totalDrag: Float = 0f - private var draggingDown = false - private var draggingUp = false - - private var callbacks: MutableList<ElasticDragDismissCallback> = mutableListOf() - - init { - if (attrs != null) { - val a = getContext().obtainStyledAttributes(attrs, R.styleable.ElasticDragDismissFrameLayout, 0, 0) - dragDismissDistance = a.getDimensionPixelSize(R.styleable.ElasticDragDismissFrameLayout_dragDismissDistance, Int.MAX_VALUE).toFloat() - dragDismissFraction = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragDismissFraction, dragDismissFraction) - dragDismissScale = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragDismissScale, dragDismissScale) - dragElacticity = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragElasticity, dragElacticity) - a.recycle() - } - } - - abstract class ElasticDragDismissCallback { - - /** - * Called for each drag event. - - * @param elasticOffset Indicating the drag offset with elasticity applied i.e. may - * * exceed 1. - * * - * @param elasticOffsetPixels The elastically scaled drag distance in pixels. - * * - * @param rawOffset Value from [0, 1] indicating the raw drag offset i.e. - * * without elasticity applied. A value of 1 indicates that the - * * dismiss distance has been reached. - * * - * @param rawOffsetPixels The raw distance the user has dragged - */ - internal open fun onDrag(elasticOffset: Float, elasticOffsetPixels: Float, - rawOffset: Float, rawOffsetPixels: Float) { - } - - /** - * Called when dragging is released and has exceeded the threshold dismiss distance. - */ - internal open fun onDragDismissed() {} - - } - - override fun onStartNestedScroll(child: View, target: View, nestedScrollAxes: Int): Boolean { - return nestedScrollAxes and View.SCROLL_AXIS_VERTICAL != 0 - } - - override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray) { - // if we're in a drag gesture and the user reverses up the we should take those events - if (draggingDown && dy > 0 || draggingUp && dy < 0) { - dragScale(dy) - consumed[1] = dy - } - } - - override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int, - dxUnconsumed: Int, dyUnconsumed: Int) { - dragScale(dyUnconsumed) - } - - override fun onStopNestedScroll(child: View) { - if (Math.abs(totalDrag) >= dragDismissDistance) { - dispatchDismissCallback() - } else { // settle back to natural position - animate() - .translationY(0f) - .scaleX(1f) - .scaleY(1f) - .setDuration(200L) - .setInterpolator(AnimHolder.fastOutSlowInInterpolator(context)) - .setListener(null) - .start() - totalDrag = 0f - draggingUp = false - draggingDown = draggingUp - dispatchDragCallback(0f, 0f, 0f, 0f) - } - } - - override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { - super.onSizeChanged(w, h, oldw, oldh) - if (dragDismissFraction > 0f) { - dragDismissDistance = h * dragDismissFraction - } - } - - fun addListener(listener: ElasticDragDismissCallback) { - callbacks.add(listener) - } - - fun removeListener(listener: ElasticDragDismissCallback) { - callbacks.remove(listener) - } - - private fun dragScale(scroll: Int) { - if (scroll == 0) return - - totalDrag += scroll.toFloat() - - // track the direction & set the pivot point for scaling - // don't double track i.e. if start dragging down and then reverse, keep tracking as - // dragging down until they reach the 'natural' position - if (scroll < 0 && !draggingUp && !draggingDown) { - draggingDown = true - if (shouldScale) pivotY = height.toFloat() - } else if (scroll > 0 && !draggingDown && !draggingUp) { - draggingUp = true - if (shouldScale) pivotY = 0f - } - // how far have we dragged relative to the distance to perform a dismiss - // (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit - var dragFraction = Math.log10((1 + Math.abs(totalDrag) / dragDismissDistance).toDouble()).toFloat() - - // calculate the desired translation given the drag fraction - var dragTo = dragFraction * dragDismissDistance * dragElacticity - - if (draggingUp) { - // as we use the absolute magnitude when calculating the drag fraction, need to - // re-apply the drag direction - dragTo *= -1f - } - translationY = dragTo - - if (shouldScale) { - val scale = 1 - (1 - dragDismissScale) * dragFraction - scaleX = scale - scaleY = scale - } - - // if we've reversed direction and gone past the settle point then clear the flags to - // allow the list to get the scroll events & reset any transforms - if (draggingDown && totalDrag >= 0 || draggingUp && totalDrag <= 0) { - dragFraction = 0f - dragTo = dragFraction - totalDrag = dragTo - draggingUp = false - draggingDown = draggingUp - translationY = 0f - scaleX = 1f - scaleY = 1f - } - dispatchDragCallback(dragFraction, dragTo, - Math.min(1f, Math.abs(totalDrag) / dragDismissDistance), totalDrag) - } - - private fun dispatchDragCallback(elasticOffset: Float, elasticOffsetPixels: Float, - rawOffset: Float, rawOffsetPixels: Float) { - callbacks.forEach { - it.onDrag(elasticOffset, elasticOffsetPixels, - rawOffset, rawOffsetPixels) - } - } - - private fun dispatchDismissCallback() { - callbacks.forEach { it.onDragDismissed() } - } - - /** - * An [ElasticDragDismissCallback] which fades system chrome (i.e. status bar and - * navigation bar) whilst elastic drags are performed and - * [finishes][Activity.finishAfterTransition] the activity when drag dismissed. - */ - open class SystemChromeFader(private val activity: Activity) : ElasticDragDismissCallback() { - private val statusBarAlpha: Int = Color.alpha(activity.statusBarColor) - private val navBarAlpha: Int = Color.alpha(activity.navigationBarColor) - private val fadeNavBar: Boolean = activity.isNavBarOnBottom - - public override fun onDrag(elasticOffset: Float, elasticOffsetPixels: Float, - rawOffset: Float, rawOffsetPixels: Float) { - if (elasticOffsetPixels > 0) { - // dragging downward, fade the status bar in proportion - activity.statusBarColor = activity.statusBarColor.withAlpha(((1f - rawOffset) * statusBarAlpha).toInt()) - } else if (elasticOffsetPixels == 0f) { - // reset - activity.statusBarColor = activity.statusBarColor.withAlpha(statusBarAlpha) - activity.navigationBarColor = activity.navigationBarColor.withAlpha(navBarAlpha) - } else if (fadeNavBar) { - // dragging upward, fade the navigation bar in proportion - activity.navigationBarColor = activity.navigationBarColor.withAlpha(((1f - rawOffset) * navBarAlpha).toInt()) - } - } - - public override fun onDragDismissed() { - activity.finishAfterTransition() - } - } - -} |