aboutsummaryrefslogtreecommitdiff
path: root/library/src/main/kotlin/ca/allanwang/kau/views
diff options
context:
space:
mode:
Diffstat (limited to 'library/src/main/kotlin/ca/allanwang/kau/views')
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/views/KauBoundedCardView.kt8
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/views/KauCutoutTextView.kt147
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/views/KauTextSlider.kt125
3 files changed, 148 insertions, 132 deletions
diff --git a/library/src/main/kotlin/ca/allanwang/kau/views/KauBoundedCardView.kt b/library/src/main/kotlin/ca/allanwang/kau/views/KauBoundedCardView.kt
index a07a118..60f5176 100644
--- a/library/src/main/kotlin/ca/allanwang/kau/views/KauBoundedCardView.kt
+++ b/library/src/main/kotlin/ca/allanwang/kau/views/KauBoundedCardView.kt
@@ -6,6 +6,7 @@ import android.support.v7.widget.CardView
import android.util.AttributeSet
import ca.allanwang.kau.R
import ca.allanwang.kau.utils.parentViewGroup
+import ca.allanwang.kau.utils.parentVisibleHeight
/**
@@ -19,13 +20,6 @@ class KauBoundedCardView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : CardView(context, attrs, defStyleAttr) {
- val parentVisibleHeight: Int
- get() {
- val r = Rect()
- parentViewGroup.getWindowVisibleDisplayFrame(r)
- return r.height()
- }
-
/**
* Maximum height possible, defined in dp (will be converted to px)
* Defaults to parent's visible height
diff --git a/library/src/main/kotlin/ca/allanwang/kau/views/KauCutoutTextView.kt b/library/src/main/kotlin/ca/allanwang/kau/views/KauCutoutTextView.kt
new file mode 100644
index 0000000..8df604a
--- /dev/null
+++ b/library/src/main/kotlin/ca/allanwang/kau/views/KauCutoutTextView.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ca.allanwang.kau.views
+
+import android.content.Context
+import android.graphics.*
+import android.text.TextPaint
+import android.util.AttributeSet
+import android.util.DisplayMetrics
+import android.util.TypedValue
+import android.view.View
+import ca.allanwang.kau.R
+import ca.allanwang.kau.logging.KL
+import ca.allanwang.kau.utils.dimenPixelSize
+import ca.allanwang.kau.utils.getFont
+import ca.allanwang.kau.utils.parentVisibleHeight
+
+/**
+ * A view which punches out some text from an opaque color block, allowing you to see through it.
+ */
+class KauCutoutTextView @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
+) : View(context, attrs, defStyleAttr) {
+ private val textPaint: TextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+ private var cutout: Bitmap? = null
+ var foregroundColor = Color.MAGENTA
+ var text: String? = "Text"
+ var overlayType: Int = 0 //todo add vector overlay options
+ private var textSize: Float = 0f
+ private var textY: Float = 0f
+ private var textX: Float = 0f
+ private var heightPercentage: Float = 0f
+ private var minHeight: Float = 0f
+ private val maxTextSize: Float
+
+ init {
+ if (attrs != null) {
+ val a = context.obtainStyledAttributes(attrs, R.styleable.KauCutoutTextView, 0, 0)
+ if (a.hasValue(R.styleable.KauCutoutTextView_kau_font))
+ textPaint.typeface = context.getFont(a.getString(R.styleable.KauCutoutTextView_kau_font))
+ foregroundColor = a.getColor(R.styleable.KauCutoutTextView_kau_foregroundColor, foregroundColor)
+ text = a.getString(R.styleable.KauCutoutTextView_android_text) ?: text
+ minHeight = a.getDimension(R.styleable.KauCutoutTextView_android_minHeight, minHeight)
+ heightPercentage = a.getFloat(R.styleable.KauCutoutTextView_kau_heightPercentageToScreen, heightPercentage)
+ a.recycle()
+ }
+ maxTextSize = context.dimenPixelSize(R.dimen.kau_display_4_text_size).toFloat()
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ calculateTextPosition()
+ createBitmap()
+ KL.d("Size changed")
+ }
+
+ private fun calculateTextPosition() {
+ val targetWidth = width / PHI
+ textSize = getSingleLineTextSize(text!!, textPaint, targetWidth, 0f, maxTextSize,
+ 0.5f, resources.displayMetrics)
+ textPaint.textSize = textSize
+
+ // measuring text is fun :] see: https://chris.banes.me/2014/03/27/measuring-text/
+ textX = (width - textPaint.measureText(text)) / 2
+ val textBounds = Rect()
+ textPaint.getTextBounds(text, 0, text!!.length, textBounds)
+ val textHeight = textBounds.height().toFloat()
+ textY = (height + textHeight) / 2
+ }
+
+ /**
+ * If height percent is specified, ensure it is met
+ */
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ val minHeight = Math.max(minHeight, heightPercentage * parentVisibleHeight)
+ val trueHeightMeasureSpec = if (minHeight > 0)
+ MeasureSpec.makeMeasureSpec(Math.max(minHeight.toInt(), measuredHeight), MeasureSpec.EXACTLY)
+ else heightMeasureSpec
+ super.onMeasure(widthMeasureSpec, trueHeightMeasureSpec)
+ }
+
+ /**
+ * Recursive binary search to find the best size for the text.
+
+ * Adapted from https://github.com/grantland/android-autofittextview
+ */
+ fun getSingleLineTextSize(text: String,
+ paint: TextPaint,
+ targetWidth: Float,
+ low: Float,
+ high: Float,
+ precision: Float,
+ metrics: DisplayMetrics): Float {
+ val mid = (low + high) / 2.0f
+
+ paint.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid, metrics)
+ val maxLineWidth = paint.measureText(text)
+
+ if (high - low < precision) {
+ return low
+ } else if (maxLineWidth > targetWidth) {
+ return getSingleLineTextSize(text, paint, targetWidth, low, mid, precision, metrics)
+ } else if (maxLineWidth < targetWidth) {
+ return getSingleLineTextSize(text, paint, targetWidth, mid, high, precision, metrics)
+ } else {
+ return mid
+ }
+ }
+
+ private fun createBitmap() {
+ if (!(cutout?.isRecycled ?: true))
+ cutout?.recycle()
+ if (width == 0 || height == 0) return
+ cutout = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ cutout!!.setHasAlpha(true)
+ val cutoutCanvas = Canvas(cutout!!)
+ cutoutCanvas.drawColor(foregroundColor)
+
+ // this is the magic – Clear mode punches out the bitmap
+ textPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
+ cutoutCanvas.drawText(text, textX, textY, textPaint)
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ canvas.drawBitmap(cutout!!, 0f, 0f, null)
+ }
+
+ override fun hasOverlappingRendering(): Boolean = true
+
+ companion object {
+ val PHI = 1.6182f
+ }
+}
diff --git a/library/src/main/kotlin/ca/allanwang/kau/views/KauTextSlider.kt b/library/src/main/kotlin/ca/allanwang/kau/views/KauTextSlider.kt
deleted file mode 100644
index 8294f9f..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/views/KauTextSlider.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-package ca.allanwang.kau.views
-
-import android.content.Context
-import android.graphics.Color
-import android.support.v4.widget.TextViewCompat
-import android.text.TextUtils
-import android.util.AttributeSet
-import android.view.Gravity
-import android.view.animation.Animation
-import android.view.animation.AnimationUtils
-import android.widget.TextSwitcher
-import android.widget.TextView
-import ca.allanwang.kau.R
-import java.util.*
-
-/**
- * Created by Allan Wang on 2017-06-21.
- *
- * Text switcher with global text color and embedded sliding animations
- * Also has a stack to keep track of title changes
- */
-class KauTextSlider @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
-) : TextSwitcher(context, attrs) {
-
- val titleStack: Stack<CharSequence?> = Stack()
-
- /**
- * Holds a mapping of animation types to their respective animations
- */
- val animationMap = mapOf(
- ANIMATION_NONE to null,
- ANIMATION_SLIDE_HORIZONTAL to AnimationBundle(
- R.anim.kau_slide_in_right, R.anim.kau_slide_out_left,
- R.anim.kau_slide_in_left, R.anim.kau_slide_out_right),
- ANIMATION_SLIDE_VERTICAL to AnimationBundle(
- R.anim.kau_slide_in_bottom, R.anim.kau_slide_out_top,
- R.anim.kau_slide_in_top, R.anim.kau_slide_out_bottom
- )
- )
-
- /**
- * Holds lazy instances of the animations
- */
- inner class AnimationBundle(private val nextIn: Int, private val nextOut: Int, private val prevIn: Int, private val prevOut: Int) {
- val NEXT_IN: Animation by lazy { AnimationUtils.loadAnimation(context, nextIn) }
- val NEXT_OUT: Animation by lazy { AnimationUtils.loadAnimation(context, nextOut) }
- val PREV_IN: Animation by lazy { AnimationUtils.loadAnimation(context, prevIn) }
- val PREV_OUT: Animation by lazy { AnimationUtils.loadAnimation(context, prevOut) }
- }
-
- companion object {
- const val ANIMATION_NONE = 1000
- const val ANIMATION_SLIDE_HORIZONTAL = 1001
- const val ANIMATION_SLIDE_VERTICAL = 1002
- }
-
- var animationType: Int = ANIMATION_SLIDE_HORIZONTAL
-
- var textColor: Int = Color.WHITE
- get() = field
- set(value) {
- field = value
- (getChildAt(0) as TextView).setTextColor(value)
- (getChildAt(1) as TextView).setTextColor(value)
- }
- val isRoot: Boolean
- get() = titleStack.size <= 1
-
- init {
- if (attrs != null) {
- val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.KauTextSlider)
- animationType = styledAttrs.getInteger(R.styleable.KauTextSlider_kau_animation_type, ANIMATION_SLIDE_HORIZONTAL)
- styledAttrs.recycle()
- }
- }
-
- override fun setText(text: CharSequence?) {
- if ((currentView as TextView).text == text) return
- super.setText(text)
- }
-
- override fun setCurrentText(text: CharSequence?) {
- if (titleStack.isNotEmpty()) titleStack.pop()
- titleStack.push(text)
- super.setCurrentText(text)
- }
-
- fun setNextText(text: CharSequence?) {
- if (titleStack.isEmpty()) {
- setCurrentText(text)
- return
- }
- titleStack.push(text)
- val anim = animationMap[animationType]
- inAnimation = anim?.NEXT_IN
- outAnimation = anim?.NEXT_OUT
- setText(text)
- }
-
- /**
- * Sets the text as the previous title
- * No further checks are done, so be sure to verify with [isRoot]
- */
- @Throws(EmptyStackException::class)
- fun setPrevText() {
- titleStack.pop()
- val anim = animationMap[animationType]
- inAnimation = anim?.PREV_IN
- outAnimation = anim?.PREV_OUT
- val text = titleStack.peek()
- setText(text)
- }
-
- init {
- setFactory {
- TextView(context).apply {
- //replica of toolbar title
- gravity = Gravity.START
- setSingleLine()
- ellipsize = TextUtils.TruncateAt.END
- TextViewCompat.setTextAppearance(this, R.style.TextAppearance_AppCompat_Title)
- }
- }
- }
-} \ No newline at end of file