package ca.allanwang.kau.ui.views
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import ca.allanwang.kau.R
import ca.allanwang.kau.utils.parentViewGroup
/**
* Created by Allan Wang on 2017-07-14.
*
* Handles relative sizes for any view
* You may delegate all methods to [MeasureSpecDelegate]
* and call the two methods: [initAttrs] and [onMeasure]
*/
interface MeasureSpecContract {
/**
* Width will be calculated as a percentage of the parent
* This takes precedence over relativeWidth
*/
var relativeWidthToParent: Float
/**
* Height will be calculated as a percentage of the parent
* This takes precedence over relativeHeight
*/
var relativeHeightToParent: Float
/**
* Width will be calculated based on the measured height
*/
var relativeWidth: Float
/**
* Height will be calculated based on the measure width
*/
var relativeHeight: Float
/**
* Width will be once again calculated from the current measured height
* This is the last step
*/
var postRelativeWidth: Float
/**
* Height will be once again calculated from the current measured width
* This is the last step
*/
var postRelativeHeight: Float
/**
* Retrieves relative values from the [AttributeSet]
* Call this on init
*/
fun initAttrs(context: Context, attrs: AttributeSet?)
/**
* Calculates the final measure specs
* Call this from [View.onMeasure] and send the Pair result as the specs
* The pair is of the format (width, height)
*
* Example:
*
* {@code
* override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
* val result = onMeasure(this, widthMeasureSpec, heightMeasureSpec)
* super.onMeasure(result.first, result.second)
* }
* }
*
*/
fun onMeasure(view: View, widthMeasureSpec: Int, heightMeasureSpec: Int): Pair
}
class MeasureSpecDelegate : MeasureSpecContract {
override var relativeWidth = -1f
override var relativeHeight = -1f
override var relativeWidthToParent = -1f
override var relativeHeightToParent = -1f
override var postRelativeWidth: Float = -1f
override var postRelativeHeight: Float = -1f
private val parentFrame = Rect()
override fun initAttrs(context: Context, attrs: AttributeSet?) {
if (attrs == null) return
val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.MeasureSpecDelegate)
relativeWidth = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeWidth, relativeWidth)
relativeHeight = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeHeight, relativeHeight)
relativeWidthToParent = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeWidthToParent, relativeWidthToParent)
relativeHeightToParent = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeHeightToParent, relativeHeightToParent)
postRelativeWidth = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_postRelativeWidth, postRelativeWidth)
postRelativeHeight = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_postRelativeHeight, postRelativeHeight)
styledAttrs.recycle()
}
override fun onMeasure(view: View, widthMeasureSpec: Int, heightMeasureSpec: Int): Pair {
view.parentViewGroup.getWindowVisibleDisplayFrame(parentFrame)
var width = View.MeasureSpec.getSize(widthMeasureSpec).toFloat()
var height = View.MeasureSpec.getSize(heightMeasureSpec).toFloat()
//first cycle - relative to parent
if (relativeHeightToParent > 0)
height = relativeHeightToParent * parentFrame.height()
if (relativeWidthToParent > 0)
width = relativeWidthToParent * parentFrame.width()
//second cycle - relative to each other
if (relativeHeight > 0)
height = relativeHeight * width
else if (relativeWidth > 0)
width = relativeWidth * height
//third cycle - relative to each other
if (postRelativeHeight > 0)
height = postRelativeHeight * width
else if (postRelativeWidth > 0)
width = postRelativeWidth * height
return Pair(width.measureSpec, height.measureSpec)
}
private val Float.measureSpec: Int
get() = View.MeasureSpec.makeMeasureSpec(this.toInt(), View.MeasureSpec.EXACTLY)
}