Skip to content

Instantly share code, notes, and snippets.

@MotasemF
Created February 23, 2019 20:12
Show Gist options
  • Save MotasemF/ae00f4a91c3ffd1640f3a9c64703db3f to your computer and use it in GitHub Desktop.
Save MotasemF/ae00f4a91c3ffd1640f3a9c64703db3f to your computer and use it in GitHub Desktop.
Multi Line radioButton
<resources>
<declare-styleable name="FlowRadioGroup">
<attr format="dimension" name="itemSpacing"/>
<attr format="dimension" name="lineSpacing"/>
</declare-styleable>
</resources>
class FlowRadioGroup(context: Context) : RadioGroup(context) {
private var lineSpacing: Int = 0
private var itemSpacing: Int = 0
private var singleLine: Boolean = false
constructor (context: Context, attrs: AttributeSet) : this(context, attrs, 0) {}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : this(context) {
singleLine = false
loadFromAttributes(context, attrs)
}
private fun loadFromAttributes(context: Context, attrs: AttributeSet) {
val array = context.theme.obtainStyledAttributes(attrs, R.styleable.FlowRadioGroup, 0, 0)
lineSpacing = array.getDimensionPixelSize(R.styleable.FlowRadioGroup_lineSpacing, 0)
itemSpacing = array.getDimensionPixelSize(R.styleable.FlowRadioGroup_itemSpacing, 0)
array.recycle()
}
protected fun isSingleLine(): Boolean {
return singleLine
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = View.MeasureSpec.getSize(widthMeasureSpec)
val widthMode = View.MeasureSpec.getMode(widthMeasureSpec)
val height = View.MeasureSpec.getSize(heightMeasureSpec)
val heightMode = View.MeasureSpec.getMode(heightMeasureSpec)
val maxWidth = if (widthMode == View.MeasureSpec.AT_MOST || widthMode == View.MeasureSpec.EXACTLY)
width
else
Integer.MAX_VALUE
var childLeft = paddingLeft
var childTop = paddingTop
var childBottom = childTop
var childRight = childLeft
var maxChildRight = 0
val maxRight = maxWidth - paddingRight
for (i in 0 until childCount) {
val child = getChildAt(i)
if (child.visibility == View.GONE) {
continue
}
measureChild(child, widthMeasureSpec, heightMeasureSpec)
val lp = child.layoutParams
var leftMargin = 0
var rightMargin = 0
if (lp is ViewGroup.MarginLayoutParams) {
leftMargin += lp.leftMargin
rightMargin += lp.rightMargin
}
childRight = childLeft + leftMargin + child.measuredWidth
// If the current child's right bound exceeds Flowlayout's max right bound and flowlayout is
// not confined to a single line, move this child to the next line and reset its left bound to
// flowlayout's left bound.
if (childRight > maxRight && !isSingleLine()) {
childLeft = paddingLeft
childTop = childBottom + lineSpacing
}
childRight = childLeft + leftMargin + child.measuredWidth
childBottom = childTop + child.measuredHeight
// Updates Flowlayout's max right bound if current child's right bound exceeds it.
if (childRight > maxChildRight) {
maxChildRight = childRight
}
childLeft += leftMargin + rightMargin + child.measuredWidth + itemSpacing
// For all preceding children, the child's right margin is taken into account in the next
// child's left bound (childLeft). However, childLeft is ignored after the last child so the
// last child's right margin needs to be explicitly added to Flowlayout's max right bound.
if (i == childCount - 1) {
maxChildRight += rightMargin
}
}
maxChildRight += paddingRight
childBottom += paddingBottom
val finalWidth = getMeasuredDimension(width, widthMode, maxChildRight)
val finalHeight = getMeasuredDimension(height, heightMode, childBottom)
setMeasuredDimension(finalWidth, finalHeight)
}
private fun getMeasuredDimension(size: Int, mode: Int, childrenEdge: Int): Int {
when (mode) {
View.MeasureSpec.EXACTLY -> return size
View.MeasureSpec.AT_MOST -> return Math.min(childrenEdge, size)
else // UNSPECIFIED:
-> return childrenEdge
}
}
override fun onLayout(sizeChanged: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
if (childCount == 0) {
// Do not re-layout when there are no children.
return
}
val isRtl = ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL
val paddingStart = if (isRtl) paddingRight else paddingLeft
val paddingEnd = if (isRtl) paddingLeft else paddingRight
var childStart = paddingStart
var childTop = paddingTop
var childBottom = childTop
var childEnd: Int
val maxChildEnd = right - left - paddingEnd
for (i in 0 until childCount) {
val child = getChildAt(i)
if (child.visibility == View.GONE) {
continue
}
val lp = child.layoutParams
var startMargin = 0
var endMargin = 0
if (lp is ViewGroup.MarginLayoutParams) {
startMargin = MarginLayoutParamsCompat.getMarginStart(lp)
endMargin = MarginLayoutParamsCompat.getMarginEnd(lp)
}
childEnd = childStart + startMargin + child.measuredWidth
if (!singleLine && childEnd > maxChildEnd) {
childStart = paddingStart
childTop = childBottom + lineSpacing
}
childEnd = childStart + startMargin + child.measuredWidth
childBottom = childTop + child.measuredHeight
if (isRtl) {
child.layout(
maxChildEnd - childEnd, childTop, maxChildEnd - childStart - startMargin, childBottom
)
} else {
child.layout(childStart + startMargin, childTop, childEnd, childBottom)
}
childStart += startMargin + endMargin + child.measuredWidth + itemSpacing
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment