Skip to content

Instantly share code, notes, and snippets.

@mcassiano
Last active April 23, 2018 13:53
Show Gist options
  • Save mcassiano/637efbfe4b81ea03dba33d85b45c2774 to your computer and use it in GitHub Desktop.
Save mcassiano/637efbfe4b81ea03dba33d85b45c2774 to your computer and use it in GitHub Desktop.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ProgressStepperView">
<attr name="sl_line_color" format="color"/>
<attr name="sl_line_width" format="dimension"/>
<attr name="sl_title_text_color" format="color"/>
<attr name="sl_subtitle_text_color" format="color"/>
<attr name="sl_icon_size" format="dimension"/>
<attr name="sl_icon_horizontal_spacing" format="dimension"/>
<attr name="sl_font" format="reference"/>
<attr name="sl_title_text_size" format="dimension"/>
<attr name="sl_subtitle_text_size" format="dimension"/>
<attr name="sl_vertical_spacing" format="dimension"/>
<attr name="sl_icon_ok" format="reference"/>
<attr name="sl_icon_pending" format="reference"/>
</declare-styleable>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="sl_title_text_color">#2a7bbf</color>
<color name="sl_title_pending_text_color">#8b8b8b</color>
<color name="sl_line_color">#e8e8e8</color>
<color name="sl_subtitle_text_color">#8b8b8b</color>
<color name="sl_subtitle_pending_text_color">#8b8b8b</color>
<color name="sl_pending_border_bg_color">#d6d6d6</color>
<color name="sl_pending_bg_color">#f8fcff</color>
<color name="sl_check_color">#fff</color>
<color name="sl_check_bg_color">#2a7bbf</color>
</resources>
package br.com.concrete.stepper
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.drawable.Drawable
import android.support.annotation.AttrRes
import android.support.annotation.ColorInt
import android.support.annotation.Dimension
import android.support.v7.content.res.AppCompatResources
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
import android.widget.TextView
data class ProgressItem(
val title: CharSequence,
val subtitle: CharSequence?,
val pending: Boolean
)
class ProgressStepperView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private val items = mutableListOf<ProgressItem>()
private val defaultIconSize = context.resolveDimension(R.attr.sl_icon_size)
private val defaultLineWidth = context.resolveDimension(R.attr.sl_line_width)
private val defaultLineColor = context.resolveColor(R.attr.sl_line_color)
private val defaultIconHorizontalSpace = context.resolveDimension(R.attr.sl_icon_horizontal_spacing)
private val defaultVerticalSpace = context.resolveDimension(R.attr.sl_vertical_spacing)
private val iconCheck = context.resolveDrawable(R.attr.sl_icon_ok)
private val iconPending = context.resolveDrawable(R.attr.sl_icon_pending)
private val linePaint = Paint().apply {
strokeWidth = defaultLineWidth.toFloat()
isAntiAlias = true
color = defaultLineColor
}
override fun onFinishInflate() {
super.onFinishInflate()
setWillNotDraw(false)
orientation = VERTICAL
iconCheck.setBounds(0, 0, defaultIconSize, defaultIconSize)
iconPending.setBounds(0, 0, defaultIconSize, defaultIconSize)
if (isInEditMode) {
addItem(ProgressItem("Title 1", "Subtitle 1", false))
addItem(ProgressItem("Title 2", "Subtitle 2", false))
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (items.isEmpty()) return
if (items.size > 1) {
val first = getChildAt(0)
val last = getChildAt(childCount - 1)
drawVerticalLine(
canvas,
first.y + defaultIconSize / 2,
last.y + defaultIconSize / 2
)
}
items.forEachIndexed { index, it ->
val view = getChildAt(index)
drawIcon(canvas, view.y, it)
}
}
fun addItem(item: ProgressItem) {
val view = createView(item)
addViewToThis(view)
items.add(item)
invalidate()
}
private fun createView(item: ProgressItem): View {
val view = View.inflate(context, R.layout.view_step_title, null)
view.minimumHeight = defaultIconSize
val title = view.findViewById<TextView>(R.id.textViewTitle)
val subtitle = view.findViewById<TextView>(R.id.textViewSubtitle)
title.text = item.title
title.isEnabled = !item.pending
item.subtitle?.let {
subtitle.visibility = View.VISIBLE
subtitle.text = it
title.isEnabled = !item.pending
}
return view
}
private fun drawVerticalLine(canvas: Canvas, yFirst: Float, yLast: Float) {
val x = paddingStart + defaultIconSize / 2f
canvas.save()
canvas.translate(x, 0f)
canvas.drawLine(0f, yFirst, 0f, yLast, linePaint)
canvas.restore()
}
private fun drawIcon(canvas: Canvas, startY: Float, item: ProgressItem) {
canvas.save()
canvas.translate(paddingStart.toFloat(), startY)
if (item.pending)
iconPending.draw(canvas)
else
iconCheck.draw(canvas)
canvas.restore()
}
private fun addViewToThis(view: View) {
val xStart = defaultIconSize + defaultIconHorizontalSpace
addView(view.apply {
layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT).apply {
marginStart = xStart
if (items.size > 0)
topMargin = defaultVerticalSpace
}
})
}
}
@ColorInt
private fun Context.resolveColor(@AttrRes attributeColor: Int): Int {
val typedValue = TypedValue()
theme.resolveAttribute(attributeColor, typedValue, true)
return typedValue.data
}
@Dimension
private fun Context.resolveDimension(@AttrRes attributeDimen: Int): Int {
val typedValue = TypedValue()
theme.resolveAttribute(attributeDimen, typedValue, true)
val attributes = intArrayOf(attributeDimen)
val typedArray = obtainStyledAttributes(typedValue.data, attributes)
val res = typedArray.getDimensionPixelSize(0, 0)
typedArray.recycle()
return res
}
@Dimension
private fun Context.resolveDrawable(@AttrRes attributeDimen: Int): Drawable {
val typedValue = TypedValue()
theme.resolveAttribute(attributeDimen, typedValue, true)
return AppCompatResources.getDrawable(this, typedValue.resourceId)!!
}
<resources>
<style name="Stepper" parent="Theme.AppCompat">
<item name="sl_title_text_color">@color/title_text_color</item>
<item name="sl_subtitle_text_color">@color/subtitle_text_color</item>
<item name="sl_line_color">@color/sl_line_color</item>
<item name="sl_icon_horizontal_spacing">10dp</item>
<item name="sl_vertical_spacing">12dp</item>
<item name="sl_icon_ok">@drawable/ic_step_ok</item>
<item name="sl_icon_pending">@drawable/ic_step_pending</item>
</style>
<style name="Stepper.Small">
<item name="sl_line_width">9dp</item>
<item name="sl_icon_size">20dp</item>
<item name="sl_title_text_size">14sp</item>
<item name="sl_subtitle_text_size">12sp</item>
</style>
<style name="Stepper.Large">
<item name="sl_line_width">15dp</item>
<item name="sl_icon_size">30dp</item>
<item name="sl_title_text_size">24sp</item>
<item name="sl_subtitle_text_size">16sp</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/sl_subtitle_pending_text_color" android:state_enabled="false"/>
<item android:color="@color/sl_subtitle_text_color"/>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/sl_title_pending_text_color" android:state_enabled="false"/>
<item android:color="@color/sl_title_text_color"/>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/textViewTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:textColor="?attr/sl_title_text_color"
android:textSize="?attr/sl_title_text_size"
android:fontFamily="?attr/sl_font"
android:gravity="start"
tools:text="Pedido recebido"/>
<TextView
android:id="@+id/textViewSubtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:textColor="?attr/sl_subtitle_text_color"
android:textSize="?attr/sl_subtitle_text_size"
android:fontFamily="?attr/sl_font"
android:visibility="gone"
tools:visibility="visible"
tools:text="Entrega em 04/04/2018"
/>
</LinearLayout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment