package ca.allanwang.kau.ui.views
import android.animation.ValueAnimator
import android.view.View
import ca.allanwang.kau.utils.*
import java.lang.ref.WeakReference
/**
* Created by Allan Wang on 2017-08-03.
*
* Delegation class for collapsible views
*
* Views that implement this MUST call [initCollapsible] before using any of the methods
* Additionally, you will need to call [getCollapsibleDimension] and use the response for
* [View.setMeasuredDimension] during [View.onMeasure]
* (That method is protected so we cannot access it here)
*
* With reference to ExpandableLayout
*/
interface CollapsibleView {
var expansion: Float
val state: Int
val expanded: Boolean
fun initCollapsible(view: View)
fun resetCollapsibleAnimation()
fun getCollapsibleDimension(): Pair
fun toggleExpansion()
fun toggleExpansion(animate: Boolean)
fun expand()
fun expand(animate: Boolean)
fun collapse()
fun collapse(animate: Boolean)
fun setExpanded(expand: Boolean)
fun setExpanded(expand: Boolean, animate: Boolean)
}
class CollapsibleViewDelegate : CollapsibleView {
private lateinit var viewRef: WeakReference
private inline val view: View?
get() = viewRef.get()
private var animator: ValueAnimator? = null
override var expansion = 0f
set(value) {
if (value == field) return
var v = value
if (v > 1) v = 1f
else if (v < 0) v = 0f
stateHolder =
if (v == 0f) KAU_COLLAPSED
else if (v == 1f) KAU_EXPANDED
else if (v - field < 0) KAU_COLLAPSING
else KAU_EXPANDING
field = v
view?.goneIf(state == KAU_COLLAPSED)
view?.requestLayout()
}
private var stateHolder = KAU_COLLAPSED
override val state
get() = stateHolder
override val expanded
get() = stateHolder == KAU_EXPANDED || stateHolder == KAU_EXPANDING
override fun initCollapsible(view: View) {
this.viewRef = WeakReference(view)
}
override fun resetCollapsibleAnimation() {
animator?.cancel()
animator = null
if (stateHolder == KAU_COLLAPSING) stateHolder = KAU_COLLAPSED
else if (stateHolder == KAU_EXPANDING) stateHolder = KAU_EXPANDED
}
override fun getCollapsibleDimension(): Pair {
val v = view ?: return Pair(0, 0)
val size = v.measuredHeight
v.goneIf(expansion == 0f && size == 0)
return Pair(v.measuredWidth, Math.round(size * expansion))
}
private fun animateSize(target: Float) {
resetCollapsibleAnimation()
animator = ValueAnimator.ofFloat(expansion, target).apply {
addUpdateListener { expansion = it.animatedValue as Float }
start()
}
}
override fun toggleExpansion() = toggleExpansion(true)
override fun toggleExpansion(animate: Boolean) = setExpanded(!expanded, animate)
override fun expand() = expand(true)
override fun expand(animate: Boolean) = setExpanded(true, animate)
override fun collapse() = collapse(true)
override fun collapse(animate: Boolean) = setExpanded(false, animate)
override fun setExpanded(expand: Boolean) = setExpanded(expand, true)
override fun setExpanded(expand: Boolean, animate: Boolean) {
if (expand == expanded) return //state already matches
val target = if (expand) 1f else 0f
if (animate) animateSize(target) else expansion = target
}
}