Skip to content

Instantly share code, notes, and snippets.

@alana-mullen
Created October 15, 2019 00:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alana-mullen/a7d9f44fb61bbf388f597f735d98246d to your computer and use it in GitHub Desktop.
Save alana-mullen/a7d9f44fb61bbf388f597f735d98246d to your computer and use it in GitHub Desktop.
VerticalTextView that extends AppCompatTextView
package uk.co.thewirelessguy.thewirelessguy.widget
import android.content.Context
import android.graphics.Canvas
import android.text.BoringLayout
import android.text.Layout
import android.text.TextUtils
import android.util.AttributeSet
import android.view.Gravity
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.graphics.withSave
/**
* VerticalTextView that extends AppCompatTextView
*
* To use in an XML layout:
* <com.YOUR_PACKAGE_NAME.VerticalTextView>
*
* All standard styles and attributes of a AppCompatTextView can be used. By default, rotated text
* is from top to bottom. If you set android:gravity="bottom", then it's drawn from bottom to top.
*
* Adapted from:
* @link http://stackoverflow.com/questions/1258275/vertical-rotated-label-in-android
*/
class VerticalTextView(context: Context, attrs: AttributeSet) : AppCompatTextView(context, attrs) {
private val topDown = gravity.let { g ->
!(Gravity.isVertical(g) && g.and(Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM)
}
private var layout1: Layout? = null
private val metrics = BoringLayout.Metrics()
private var padLeft = 0
private var padTop = 0
/**
* onMeasure override required if using wrap_content for layout_height.
* Without onMeasure the text will be truncated to the layout_height with an ellipse.
*/
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec)
setMeasuredDimension(measuredHeight, measuredWidth)
}
override fun setText(text: CharSequence, type: BufferType) {
super.setText(text, type)
layout1 = null
}
/**
* BoringLayout is used to draw text on a view. It is called "boring" because it only handles a
* single line of left-to-right text without any interesting characters such as emoji.
* This simplification allows the class to override onDraw with more efficient logic than the
* default does.
*/
private fun makeLayout(): Layout? {
when (layout1) {
null -> {
metrics.width = height
paint.color = currentTextColor
paint.drawableState = drawableState
layout1 = BoringLayout.make(text, paint, metrics.width, Layout.Alignment.ALIGN_NORMAL, 2f, 0f, metrics, false, TextUtils.TruncateAt.END, height - compoundPaddingLeft - compoundPaddingRight)
padLeft = compoundPaddingLeft
padTop = extendedPaddingTop
}
}
return layout1
}
override fun onDraw(canvas: Canvas) {
when (layout) {
null -> return
else -> {
canvas.withSave {
when {
topDown -> {
val fm = paint.fontMetrics
translate(textSize - (fm.bottom + fm.descent), 0f)
rotate(90f)
}
else -> {
translate(textSize, height.toFloat())
rotate(-90f)
}
}
translate(padLeft.toFloat(), padTop.toFloat())
makeLayout()?.draw(this)
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment