@@ -6,14 +6,14 @@ import android.graphics.Rect
66import android.support.annotation.ColorInt
77import android.support.annotation.StyleRes
88import android.util.AttributeSet
9- import android.util.SparseArray
9+ import android.util.Log
10+ import android.view.Gravity
1011import android.view.View
1112import android.view.ViewGroup
1213import android.view.ViewTreeObserver
1314import android.view.animation.LinearInterpolator
1415import android.widget.FrameLayout
1516import kotlinx.android.synthetic.main.sequence_layout.view.*
16- import kotlinx.android.synthetic.main.sequence_step.view.*
1717
1818/* *
1919 * Vertical step tracker that contains {@link com.transferwise.sequencelayout.SequenceStep}s and animates to the first active step.
@@ -61,8 +61,10 @@ public class SequenceLayout(context: Context?, attrs: AttributeSet?, defStyleAtt
6161 start()
6262 }
6363
64- @ColorInt private var progressBackgroundColor: Int = 0
65- @ColorInt private var progressForegroundColor: Int = 0
64+ @ColorInt
65+ private var progressBackgroundColor: Int = 0
66+ @ColorInt
67+ private var progressForegroundColor: Int = 0
6668
6769 public fun start () {
6870 viewTreeObserver.addOnGlobalLayoutListener(this )
@@ -113,75 +115,108 @@ public class SequenceLayout(context: Context?, attrs: AttributeSet?, defStyleAtt
113115 val item = adapter.getItem(i)
114116 val view = SequenceStep (context)
115117 adapter.bindView(view, item)
116- addView(view)
118+ stepsWrapper. addView(view)
117119 }
118120 start()
119121 }
120122
121123 private fun applyAttributes (attributes : TypedArray ) {
122124 setupProgressForegroundColor(attributes)
123- setupDotBackground (attributes)
125+ setupProgressBackgroundColor (attributes)
124126 }
125127
126128 private fun setupProgressForegroundColor (attributes : TypedArray ) {
127129 setProgressForegroundColor(attributes.getColor(R .styleable.SequenceLayout_progressForegroundColor , 0 ))
128130 }
129131
130- private fun setupDotBackground (attributes : TypedArray ) {
132+ private fun setupProgressBackgroundColor (attributes : TypedArray ) {
131133 setProgressBackgroundColor(attributes.getColor(R .styleable.SequenceLayout_progressBackgroundColor , 0 ))
132134 }
133135
134136 private fun setProgressBarHorizontalOffset () {
135- val firstAnchor: View = stepsWrapper.getChildAt(0 ).anchor.findViewById(R .id.anchor)
136- val firstDot: View = stepsWrapper.getChildAt(0 ).findViewById(R .id.dot)
137- progressBarWrapper.translationX = firstAnchor.measuredWidth + (firstDot.measuredWidth - progressBarWrapper.measuredWidth) / 2f
137+ val firstAnchor: View = stepsWrapper.getChildAt(0 ).findViewById(R .id.anchor)
138+ progressBarWrapper.translationX = firstAnchor.measuredWidth + 4 .toPx() - (progressBarWrapper.measuredWidth / 2f ) // TODO dynamic dot size
138139 }
139140
140- private fun animateToActive () {
141- val stepsToAnimate = getStepOffsets()
142- if (stepsToAnimate.size() > 0 ) {
143- val lastChild = stepsWrapper.getChildAt(stepsWrapper.childCount - 1 )
144- val lastChildOffset = lastChild.top + lastChild.paddingTop
145- progressBarForeground.visibility = VISIBLE
146- progressBarForeground.pivotY = 0f
147- progressBarForeground.scaleY = 0f
148- progressBarForeground
149- .animate()
150- .setStartDelay(resources.getInteger(R .integer.sequence_step_duration).toLong())
151- .scaleY(stepsToAnimate.keyAt(stepsToAnimate.size() - 1 ) / lastChildOffset.toFloat())
152- .setInterpolator(LinearInterpolator ())
153- .setDuration((stepsToAnimate.size() - 1 ) * resources.getInteger(R .integer.sequence_step_duration).toLong())
154- .setUpdateListener({
155- val v = (progressBarForeground.scaleY * lastChildOffset)
156-
157- for (i in 0 .. stepsToAnimate.size()) {
158- if (stepsToAnimate.valueAt(i) != null ) {
159- val step = stepsToAnimate.valueAt(i) as SequenceStep
160- if (stepsToAnimate.keyAt(i) <= v + step.paddingTop) {
161- if (i == stepsToAnimate.size() - 1 ) {
162- step.getDot().isActivated = true
163- } else {
164- step.getDot().isEnabled = true
165- }
166- stepsToAnimate.setValueAt(i, null )
167- }
168- }
169- }
170- })
171- .start()
141+ private fun placeDots () {
142+ dotsWrapper.removeAllViews()
143+ var firstOffset = 0
144+ var lastOffset = 0
145+
146+ stepsWrapper.children().forEachIndexed { i, view ->
147+ val sequenceStep = view as SequenceStep
148+ val sequenceStepDot = SequenceStepDot (context)
149+ sequenceStepDot.setDotBackground(progressForegroundColor, progressBackgroundColor)
150+ sequenceStepDot.setPulseColor(progressForegroundColor)
151+ sequenceStepDot.clipChildren = false
152+ sequenceStepDot.clipToPadding = false
153+ val layoutParams = FrameLayout .LayoutParams (8 .toPx(), 8 .toPx()) // TODO dynamic dot size
154+ val totalDotOffset = getRelativeTop(sequenceStep, stepsWrapper) + sequenceStep.paddingTop + sequenceStep.getDotOffset() + 2 .toPx() // TODO dynamic dot size
155+ layoutParams.topMargin = totalDotOffset
156+ layoutParams.gravity = Gravity .CENTER_HORIZONTAL
157+ dotsWrapper.addView(sequenceStepDot, layoutParams)
158+ if (i == 0 ) {
159+ firstOffset = totalDotOffset
160+ }
161+ lastOffset = totalDotOffset
172162 }
163+
164+ val layoutParams = progressBarBackground.layoutParams as MarginLayoutParams
165+ layoutParams.topMargin = firstOffset + 4 .toPx() // TODO dynamic dot size
166+ layoutParams.height = lastOffset - firstOffset
167+ progressBarBackground.requestLayout()
168+
169+ val layoutParams2 = progressBarForeground.layoutParams as MarginLayoutParams
170+ layoutParams2.topMargin = firstOffset + 4 .toPx() // TODO dynamic dot size
171+ layoutParams2.height = lastOffset - firstOffset
172+ progressBarForeground.requestLayout()
173173 }
174174
175- private fun adaptProgressBarHeight () {
176- val first = stepsWrapper.getChildAt(0 ) as SequenceStep
177- val last = stepsWrapper.getChildAt(stepsWrapper.childCount - 1 ) as SequenceStep
178- val height = (getRelativeTop(last.getDot(), this ) + last.getDot().translationY) -
179- (getRelativeTop(first.getDot(), this ) + first.getDot().translationY)
175+ private fun animateToActive () {
176+ progressBarForeground.visibility = VISIBLE
177+ progressBarForeground.pivotY = 0f
178+ progressBarForeground.scaleY = 0f
179+
180+ val activeStepIndex = stepsWrapper.children().indexOfFirst { it is SequenceStep && it.isActive() }
181+
182+ if (activeStepIndex == - 1 ) {
183+ return
184+ }
180185
181- val layoutParams = progressBarWrapper.layoutParams as MarginLayoutParams
182- layoutParams.height = height.toInt()
183- layoutParams.topMargin = (getRelativeTop(first.getDot(), this ) + (first.getDot().measuredHeight / 2 ) + first.getDot().translationY).toInt()
184- progressBarWrapper.requestLayout()
186+ val activeDot = dotsWrapper.getChildAt(activeStepIndex)
187+ val activeDotTopMargin = (activeDot.layoutParams as LayoutParams ).topMargin
188+ val progressBarForegroundTopMargin = (progressBarForeground.layoutParams as LayoutParams ).topMargin
189+ val scaleEnd = (activeDotTopMargin + (activeDot.measuredHeight / 2 ) - progressBarForegroundTopMargin) /
190+ progressBarBackground.measuredHeight.toFloat()
191+
192+ progressBarForeground
193+ .animate()
194+ .setStartDelay(resources.getInteger(R .integer.sequence_step_duration).toLong())
195+ .scaleY(scaleEnd)
196+ .setInterpolator(LinearInterpolator ())
197+ .setDuration(activeStepIndex * resources.getInteger(R .integer.sequence_step_duration).toLong())
198+ .setUpdateListener({
199+ val animatedOffset = progressBarForeground.scaleY * progressBarBackground.measuredHeight
200+ dotsWrapper
201+ .children()
202+ .forEachIndexed { i, view ->
203+ if (i > activeStepIndex) {
204+ return @forEachIndexed
205+ }
206+ val dot = view as SequenceStepDot
207+ val dotTopMargin = (dot.layoutParams as LayoutParams ).topMargin -
208+ progressBarForegroundTopMargin -
209+ (dot.measuredHeight / 2 )
210+ if (animatedOffset >= dotTopMargin) {
211+ if (i < activeStepIndex && ! dot.isEnabled) {
212+ dot.isEnabled = true
213+ } else if (i == activeStepIndex && ! dot.isActivated) {
214+ dot.isActivated = true
215+ }
216+ }
217+ }
218+ })
219+ .start()
185220 }
186221
187222 private fun getRelativeTop (child : View , parent : ViewGroup ): Int {
@@ -191,28 +226,6 @@ public class SequenceLayout(context: Context?, attrs: AttributeSet?, defStyleAtt
191226 return offsetViewBounds.top
192227 }
193228
194- private fun getStepOffsets (): SparseArray <View > {
195- val childCount = stepsWrapper.childCount
196- val stepOffsets = SparseArray <View >()
197- var containsActiveStep = false
198- for (i in 0 until childCount) {
199- val step = stepsWrapper.getChildAt(i) as SequenceStep
200- stepOffsets.append(step.top - progressBarForeground.top + step.getDot().top, step)
201- if (step.isActive()) {
202- containsActiveStep = true
203- if (i == childCount - 1 ) {
204- // remove bottom padding if active step is last step
205- step.setPadding(step.paddingLeft, step.paddingTop, step.paddingRight, 0 )
206- }
207- break
208- }
209- }
210- if (! containsActiveStep) {
211- stepOffsets.clear()
212- }
213- return stepOffsets
214- }
215-
216229 override fun addView (child : View , index : Int , params : ViewGroup .LayoutParams ) {
217230 if (child is SequenceStep ) {
218231 if (child.isActive()) {
@@ -223,21 +236,21 @@ public class SequenceLayout(context: Context?, attrs: AttributeSet?, defStyleAtt
223236 resources.getDimensionPixelSize(R .dimen.sequence_active_step_padding_bottom)
224237 )
225238 }
226- val dot = child.getDot()
227- dot.setDotBackground(progressForegroundColor, progressBackgroundColor)
228- dot.setPulseColor(progressForegroundColor)
229239 stepsWrapper.addView(child, params)
230240 return
231241 }
232242 super .addView(child, index, params)
233243 }
234244
235245 override fun onGlobalLayout () {
236- if (stepsWrapper.childCount > 0 ) {
237- adaptProgressBarHeight()
238- setProgressBarHorizontalOffset()
239- animateToActive()
240- viewTreeObserver.removeOnGlobalLayoutListener(this )
241- }
246+ Log .d(" testcount" , stepsWrapper.childCount.toString())
247+ handler.postDelayed({
248+ if (stepsWrapper.childCount > 0 ) {
249+ setProgressBarHorizontalOffset()
250+ placeDots()
251+ animateToActive()
252+ viewTreeObserver.removeOnGlobalLayoutListener(this )
253+ }
254+ }, 500 )
242255 }
243256}
0 commit comments