Guest User

Untitled

a guest
Dec 17th, 2017
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.69 KB | None | 0 0
  1. package by.stylesoft.myapplication
  2.  
  3. import android.content.Context
  4. import android.graphics.Rect
  5. import android.util.AttributeSet
  6. import android.view.LayoutInflater
  7. import android.view.View
  8. import android.view.ViewGroup
  9.  
  10. class ExpansionPanel @JvmOverloads constructor(
  11. context: Context,
  12. attrs: AttributeSet? = null,
  13. defStyleAttr: Int = 0,
  14. defStyleRes: Int = 0
  15. ) : ViewGroup(context, attrs, defStyleAttr, defStyleRes) {
  16.  
  17. var headerView: View? = null
  18. set(value) {
  19. field?.let { removeView(it) }
  20.  
  21. field = value
  22. field?.let {
  23. addView(it, 0)
  24. }
  25. }
  26.  
  27. var contentView: View? = null
  28. set(value) {
  29. field?.let { removeView(it) }
  30.  
  31. field = value
  32. field?.let { addView(it, 0) }
  33. }
  34.  
  35. var isExpanded: Boolean = false
  36. private set
  37.  
  38. private var lastWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
  39. private var lastHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
  40.  
  41. init {
  42. attrs?.let {
  43. val typedArray = context.obtainStyledAttributes(
  44. it,
  45. R.styleable.ExpansionPanel,
  46. defStyleAttr,
  47. defStyleRes
  48. )
  49.  
  50. headerView = inflate(typedArray.getResourceId(
  51. R.styleable.ExpansionPanel_ep_header_layout,
  52. LAYOUT_DEFAULT
  53. ))
  54. contentView = inflate(typedArray.getResourceId(
  55. R.styleable.ExpansionPanel_ep_content_layout,
  56. LAYOUT_DEFAULT
  57. ))
  58. isExpanded = typedArray.getBoolean(R.styleable.ExpansionPanel_ep_expanded, false)
  59.  
  60. typedArray.recycle()
  61. }
  62.  
  63. removeAllViews()
  64. headerView?.let { super.addView(it, 0) }
  65. contentView?.let { super.addView(it, 1) }
  66. }
  67.  
  68. override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
  69. lastWidthMeasureSpec = widthMeasureSpec
  70. lastHeightMeasureSpec = heightMeasureSpec
  71.  
  72. var desiredWidth = suggestedMinimumWidth +
  73. Math.max(paddingStart, paddingLeft) +
  74. Math.max(paddingEnd, paddingRight)
  75. var desiredHeight = suggestedMinimumHeight + paddingTop + paddingBottom
  76.  
  77. headerView?.apply {
  78. val headerWidthMeasureSpec = calculateMeasureSpec(widthMeasureSpec, desiredWidth)
  79. val headerHeightMeasureSpec = calculateMeasureSpec(heightMeasureSpec, desiredHeight)
  80.  
  81. measure(headerWidthMeasureSpec, headerHeightMeasureSpec)
  82.  
  83. desiredWidth += measuredWidth
  84. desiredHeight += measuredHeight
  85. }
  86.  
  87. if (isExpanded) {
  88. contentView?.apply {
  89. val headerWidth = headerView?.measuredWidth ?: 0
  90. val contentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(headerWidth, MeasureSpec.EXACTLY)
  91. val contentHeightMeasureSpec = calculateMeasureSpec(heightMeasureSpec, desiredHeight)
  92.  
  93. measure(contentWidthMeasureSpec, contentHeightMeasureSpec)
  94.  
  95. desiredHeight += measuredHeight
  96. }
  97. }
  98.  
  99. setMeasuredDimension(
  100. measureDimension(desiredWidth, widthMeasureSpec),
  101. measureDimension(desiredHeight, heightMeasureSpec)
  102. )
  103. }
  104.  
  105. override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
  106. val bound = calculateBound(left, top, right, bottom)
  107. val contentTop = headerView?.let { hv ->
  108. val headerBottom = bound.top + hv.measuredHeight
  109. hv.layout(bound.left, bound.top, bound.right, headerBottom)
  110. headerBottom
  111. } ?: bound.top
  112.  
  113. contentView?.layout(bound.left, contentTop, bound.right, bound.bottom)
  114. }
  115.  
  116. fun setExpanded(expanded: Boolean, animate: Boolean = true) {
  117. isExpanded = expanded
  118. requestLayout()
  119. }
  120.  
  121. private fun measureDimension(desiredSize: Int, measureSpec: Int): Int {
  122. val mode = MeasureSpec.getMode(measureSpec)
  123. val size = MeasureSpec.getSize(measureSpec)
  124.  
  125. return when (mode) {
  126. MeasureSpec.EXACTLY -> size
  127. MeasureSpec.AT_MOST -> Math.min(desiredSize, size)
  128. MeasureSpec.UNSPECIFIED -> desiredSize
  129. else -> throw IllegalArgumentException("Unknown measure spec")
  130. }
  131. }
  132.  
  133. private fun calculateMeasureSpec(measureSpec: Int, padding: Int): Int {
  134. val mode = MeasureSpec.getMode(measureSpec)
  135. val size = MeasureSpec.getSize(measureSpec)
  136.  
  137. return when (mode) {
  138. MeasureSpec.EXACTLY, MeasureSpec.AT_MOST ->
  139. MeasureSpec.makeMeasureSpec(size - padding, mode)
  140. MeasureSpec.UNSPECIFIED ->
  141. MeasureSpec.makeMeasureSpec(size, mode)
  142. else -> throw IllegalArgumentException("Unknown measure spec mode: $mode")
  143. }
  144. }
  145.  
  146. private fun calculateBound(left: Int, top: Int, right: Int, bottom: Int): Rect {
  147. val backgroundPadding = getBackgroundPadding()
  148. return Rect(
  149. left + backgroundPadding.left + Math.max(paddingStart, paddingLeft),
  150. top + backgroundPadding.top + paddingTop,
  151. right - backgroundPadding.right - Math.max(paddingEnd, paddingStart),
  152. bottom - backgroundPadding.bottom - paddingBottom
  153. )
  154. }
  155.  
  156. private fun getBackgroundPadding() = Rect().apply { background?.getPadding(this) }
  157.  
  158. private fun inflate(layout: Int): View? {
  159. return if (layout != LAYOUT_DEFAULT) {
  160. LayoutInflater.from(context).inflate(layout, this, false)
  161. } else {
  162. null
  163. }
  164. }
  165.  
  166. companion object {
  167. val LAYOUT_DEFAULT = -1
  168. }
  169. }
Add Comment
Please, Sign In to add comment