From b45156a92f9b2f3fcce62fd58d25474196048f1a Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Wed, 28 Jun 2017 20:14:52 -0700 Subject: Starting drawable cutout support --- .../ca/allanwang/kau/about/AboutActivityBase.kt | 6 +- .../kotlin/ca/allanwang/kau/kotlin/LazyContext.kt | 2 +- .../main/kotlin/ca/allanwang/kau/utils/Utils.kt | 23 ++++++- .../ca/allanwang/kau/views/CutoutTextView.kt | 70 +++++++++++++++++----- .../kau/widgets/ElasticDragDismissFrameLayout.kt | 6 +- .../ca/allanwang/kau/widgets/InkPageIndicator.java | 2 +- 6 files changed, 84 insertions(+), 25 deletions(-) (limited to 'library/src/main/kotlin/ca') diff --git a/library/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt b/library/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt index 63af3cf..c518ca6 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt @@ -13,7 +13,6 @@ import android.view.ViewGroup import android.widget.FrameLayout import android.widget.TextView import ca.allanwang.kau.R -import ca.allanwang.kau.logging.KL import ca.allanwang.kau.utils.bindView import ca.allanwang.kau.utils.dimenPixelSize import ca.allanwang.kau.utils.string @@ -67,7 +66,7 @@ abstract class AboutActivityBase(val rClass: Class<*>, val configBuilder: Config inner class Configs { var cutoutTextRes: Int = -1 - val cutoutText: String? = null + val cutoutText: String? = "KAU" //todo make null var mainPageTitleRes: Int = -1 var mainPageTitle: String = "Kau test" var libPageTitleRes: Int = -1 @@ -80,7 +79,6 @@ abstract class AboutActivityBase(val rClass: Class<*>, val configBuilder: Config open val pageCount: Int = 2 open fun getPage(position: Int, layoutInflater: LayoutInflater, parent: ViewGroup): View { - KL.e("Get page $position") return when (position) { 0 -> inflateMainPage(layoutInflater, parent) pageCount - 1 -> inflateLibPage(layoutInflater, parent) @@ -134,7 +132,6 @@ abstract class AboutActivityBase(val rClass: Class<*>, val configBuilder: Config override fun instantiateItem(collection: ViewGroup, position: Int): Any { val layout = getPage(position, collection) - KL.e("Add view") collection.addView(layout) return layout } @@ -149,7 +146,6 @@ abstract class AboutActivityBase(val rClass: Class<*>, val configBuilder: Config override fun isViewFromObject(view: View, `object`: Any): Boolean = view === `object` private fun getPage(position: Int, parent: ViewGroup): View { - KL.e("Create page $position ${views[position] == null}") if (views[position] == null) views[position] = getPage(position, layoutInflater, parent) return views[position]!! } diff --git a/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt b/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt index a7dc09e..8b59539 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt @@ -28,7 +28,7 @@ class LazyContext(private val initializer: (context: Context) -> T, _value = UNINITIALIZED } - operator fun get(context: Context): T { + operator fun invoke(context: Context): T { val _v1 = _value if (_v1 !== UNINITIALIZED) @Suppress("UNCHECKED_CAST") diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt index c44fc9a..6c1b6a1 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt @@ -1,9 +1,11 @@ package ca.allanwang.kau.utils import android.content.Context -import android.content.pm.PackageManager import android.content.res.Resources -import android.os.Build +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable import android.os.Looper import android.support.annotation.IntRange import ca.allanwang.kau.R @@ -56,4 +58,21 @@ annotation class KauUtils val formatter = DecimalFormat(expression.toString()) formatter.roundingMode = RoundingMode.HALF_UP return formatter.format(this) +} + +@KauUtils fun Drawable.toBitmap(scaling: Float = 1f): Bitmap { + if (this is BitmapDrawable && bitmap != null) { + if (scaling == 1f) return bitmap + val width = (bitmap.width * scaling).toInt() + val height = (bitmap.height * scaling).toInt() + return Bitmap.createScaledBitmap(bitmap, width, height, false) + } + val bitmap = if (intrinsicWidth <= 0 || intrinsicHeight <= 0) + Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) // Single color bitmap will be created of 1x1 pixel + else + Bitmap.createBitmap((intrinsicWidth * scaling).toInt(), (intrinsicHeight * scaling).toInt(), Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + setBounds(0, 0, canvas.width, canvas.height) + draw(canvas) + return bitmap } \ No newline at end of file diff --git a/library/src/main/kotlin/ca/allanwang/kau/views/CutoutTextView.kt b/library/src/main/kotlin/ca/allanwang/kau/views/CutoutTextView.kt index b113c3d..83ed6dd 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/views/CutoutTextView.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/views/CutoutTextView.kt @@ -18,16 +18,17 @@ package ca.allanwang.kau.views import android.content.Context import android.graphics.* +import android.graphics.drawable.Drawable 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 +import ca.allanwang.kau.utils.toBitmap /** * A view which punches out some text from an opaque color block, allowing you to see through it. @@ -35,14 +36,34 @@ import ca.allanwang.kau.utils.parentVisibleHeight class CutoutTextView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { + + companion object { + const val PHI = 1.6182f + const val TYPE_TEXT = 100 + const val TYPE_DRAWABLE = 101 + } + private val textPaint: TextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG) + private val bitmapPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) + private var bitmapScaling: Float = 1f private var cutout: Bitmap? = null var foregroundColor = Color.MAGENTA var text: String? = "Text" - var overlayType: Int = 0 //todo add vector overlay options + set(value) { + field = value + if (value != null) cutoutType = TYPE_TEXT + else if (drawable != null) cutoutType = TYPE_DRAWABLE + } + var cutoutType: Int = TYPE_TEXT private var textSize: Float = 0f - private var textY: Float = 0f - private var textX: Float = 0f + private var cutoutY: Float = 0f + private var cutoutX: Float = 0f + private var drawable: Drawable? = null + set(value) { + field = value + if (value != null) cutoutType = TYPE_DRAWABLE + else if (text != null) cutoutType = TYPE_TEXT + } private var heightPercentage: Float = 0f private var minHeight: Float = 0f private val maxTextSize: Float @@ -63,9 +84,15 @@ class CutoutTextView @JvmOverloads constructor( override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) - calculateTextPosition() + calculatePosition() createBitmap() - KL.d("Size changed") + } + + private fun calculatePosition() { + when (cutoutType) { + TYPE_TEXT -> calculateTextPosition() + TYPE_DRAWABLE -> calculateImagePosition() + } } private fun calculateTextPosition() { @@ -75,11 +102,20 @@ class CutoutTextView @JvmOverloads constructor( textPaint.textSize = textSize // measuring text is fun :] see: https://chris.banes.me/2014/03/27/measuring-text/ - textX = (width - textPaint.measureText(text)) / 2 + cutoutX = (width - textPaint.measureText(text)) / 2 val textBounds = Rect() textPaint.getTextBounds(text, 0, text!!.length, textBounds) val textHeight = textBounds.height().toFloat() - textY = (height + textHeight) / 2 + cutoutY = (height + textHeight) / 2 + } + + private fun calculateImagePosition() { + if (drawable!!.intrinsicHeight <= 0 || drawable!!.intrinsicWidth <= 0) throw IllegalArgumentException("Drawable's intrinsic size cannot be less than 0") + val targetWidth = width / PHI + val targetHeight = height / PHI + bitmapScaling = Math.min(targetHeight / drawable!!.intrinsicHeight, targetWidth / drawable!!.intrinsicWidth) + cutoutX = (width - drawable!!.intrinsicWidth * bitmapScaling) / 2 + cutoutY = (height - drawable!!.intrinsicHeight * bitmapScaling) / 2 } /** @@ -130,9 +166,18 @@ class CutoutTextView @JvmOverloads constructor( 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) + when (cutoutType) { + TYPE_TEXT -> { + // this is the magic – Clear mode punches out the bitmap + textPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) + cutoutCanvas.drawText(text, cutoutX, cutoutY, textPaint) + } + TYPE_DRAWABLE -> { + bitmapPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) + cutoutCanvas.drawBitmap(drawable!!.toBitmap(bitmapScaling), cutoutX, cutoutY, bitmapPaint) + } + + } } override fun onDraw(canvas: Canvas) { @@ -141,7 +186,4 @@ class CutoutTextView @JvmOverloads constructor( override fun hasOverlappingRendering(): Boolean = true - companion object { - val PHI = 1.6182f - } } diff --git a/library/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt b/library/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt index 081db22..a9e9ee2 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt @@ -94,16 +94,17 @@ class ElasticDragDismissFrameLayout @JvmOverloads constructor( override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray) { // if we're in a drag gesture and the user reverses up the we should take those events + KL.e("Pre $dy ${consumed[1]}") if (draggingDown && dy > 0 || draggingUp && dy < 0) { dragScale(dy) consumed[1] = dy + KL.e("Pre consumed") } } override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int) { dragScale(dyUnconsumed) - KL.e("On $dyUnconsumed") } override fun onStopNestedScroll(child: View) { @@ -115,7 +116,7 @@ class ElasticDragDismissFrameLayout @JvmOverloads constructor( .scaleX(1f) .scaleY(1f) .setDuration(200L) - .setInterpolator(AnimHolder.fastOutSlowInInterpolator[context]) + .setInterpolator(AnimHolder.fastOutSlowInInterpolator(context)) .setListener(null) .start() totalDrag = 0f @@ -144,6 +145,7 @@ class ElasticDragDismissFrameLayout @JvmOverloads constructor( if (scroll == 0) return totalDrag += scroll.toFloat() +// KL.e("Drag $scroll $totalDrag") // track the direction & set the pivot point for scaling // don't double track i.e. if start dragging down and then reverse, keep tracking as diff --git a/library/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java b/library/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java index 8bdfd5c..b6ec0d0 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java +++ b/library/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java @@ -150,7 +150,7 @@ public class InkPageIndicator extends View implements ViewPager.OnPageChangeList unselectedPaint.setColor(unselectedColour); selectedPaint = new Paint(Paint.ANTI_ALIAS_FLAG); selectedPaint.setColor(selectedColour); - interpolator = AnimHolder.INSTANCE.getFastOutSlowInInterpolator().get(context); + interpolator = AnimHolder.INSTANCE.getFastOutSlowInInterpolator().invoke(context); // create paths & rect now – reuse & rewind later combinedUnselectedPath = new Path(); -- cgit v1.2.3