Same old rule. Show us a sketch, draw a lot of fire.

Believe this effect can be the result that you are satisfied with, say the main technical point that this function uses simply below.

  • Custom View property implementation (not explained in this article)
  • GradientDrawable

Here’s a look at the general parameters and use of GradientDrawable

GradientDrawable and ShapeDrawable are very similar, and you’re probably familiar with both, though you probably wouldn’t normally write them in code.

  • GradientDrawable uses XML

      
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <corners android:radius="8dp" />

    <solid android:color="@android:color/white" />

    <stroke
        android:color="#EEEEEE"
        android:dashWidth="0.5 dp"
        android:dashGap="2dip" />
</shape>
Copy the code
  • ShapeDrawable uses XML

      
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <corners android:radius="8dp" />

    <solid android:color="@android:color/white" />

    <gradient
        android:angle="90"
        android:endColor="# 334455"
        android:gradientRadius="5dp"
        android:startColor="#F14400" />

    <stroke
        android:color="#EEEEEE"
        android:dashWidth="0.5 dp"
        android:dashGap="2dip" />
</shape>
Copy the code

It’s a bit of a realisation that we’re just using code to do the same thing with XML, because with XML we’re writing a lot of shape. XML files, and with custom we can control them directly with attributes, which makes it easier to write.

  • GradientDrawable is implemented in code
val mGradientDrawable = GradientDrawable()
// Set the color, corresponding to solid
mGradientDrawable.setColor()
// Set multiple colors to support gradient drawing
mGradientDrawable.setColors()
/ / set the stroke
mGradientDrawable.setStroke(mBorderWidth, borderColor, mDashWidth, mDashGapWidth)
// Set rounded corners. Support for each Angle
mGradientDrawable.cornerRadii = mCornerRadiiArrayList
// Set the gradient Angle
mGradientDrawable.orientation = GradientDrawable.Orientation.xxx
Copy the code

The gradient Angle setting GradientDrawable. Orientation corresponding ways for:

/ / TOP_BOTTOM from top to bottom - > GradientDrawable. Orientation. TOP_BOTTOM / / upper right to lower left TR_BL - > GradientDrawable. Orientation. TR_BL / / from right to left RIGHT_LEFT - > GradientDrawable. Orientation. RIGHT_LEFT / / from the lower right to upper left BR_TL - > GradientDrawable. Orientation. BR_TL / / from the bottom up BOTTOM_TOP - > GradientDrawable. Orientation. BOTTOM_TOP / / from lower left to upper right BL_TR - > GradientDrawable. Orientation. BL_TR / / from left to right LEFT_RIGHT - > GradientDrawable. Orientation. LEFT_RIGHT / / from upper left to lower right TL_BR - > GradientDrawable. Orientation. TL_BR / / from left to right LEFT_RIGHT -> GradientDrawable.Orientation.LEFT_RIGHTCopy the code

And the thing to notice about setting rounded corners here is that we’re passing in a Float set of size 8, and the set works like this

private fun setDefaultCornerRadii(
    topLeft: Float,
    topRight: Float,
    bottomRight: Float,
    bottomLeft: Float
) {
    mCornerRadiiArrayList[0] = topLeft
    mCornerRadiiArrayList[1] = topLeft
    mCornerRadiiArrayList[2] = topRight
    mCornerRadiiArrayList[3] = topRight
    mCornerRadiiArrayList[4] = bottomRight
    mCornerRadiiArrayList[5] = bottomRight
    mCornerRadiiArrayList[6] = bottomLeft
    mCornerRadiiArrayList[7] = bottomLeft
}
Copy the code

Use GradientDrawable

background = mGradientDrawable
Copy the code

So the next step is code. Because it’s not that difficult, I just put the code in.

package org.fireking.ap.custom.textview.custom

import android.content.Context
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import org.fireking.ap.R

class ShapeTextView(context: Context, attrs: AttributeSet?) : AppCompatTextView(context, attrs) {

    private var mSolidColor = DEFAULT_SOLID_COLOR
    private var mUnusableSolidColor = DEFAULT_SOLID_COLOR
    private var mBorderColor = DEFAULT_SOLID_COLOR
    private var mUnusableTextColor: Int = DEFAULT_SOLID_COLOR
    private var mUnusableBorderColor = DEFAULT_SOLID_COLOR
    private var mGradientEnable = false
    private var mBorderWidth: Int = 3
    private var mDashWidth: Float = 0F
    private var mDashGapWidth: Float = 0F
    private lateinit var mGradientDrawable: GradientDrawable
    private var mCornerRadiiArrayList = FloatArray(8)
    private var mGradientColors = IntArray(2)
    private var mUnusableGradientColors = IntArray(2)
    private var mAngle: Int = 0

    companion object {
        const val TOP_BOTTOM = 1
        const val TR_BL = 2
        const val RIGHT_LEFT = 3
        const val BR_TL = 4
        const val BOTTOM_TOP = 5
        const val BL_TR = 6
        const val LEFT_RIGHT = 7
        const val TL_BR = 8
        private const val DEFAULT_SOLID_COLOR = Color.WHITE
    }

    init {
        obtainAttr(attrs)
    }

    private fun setDefaultCornerRadii(
        topLeft: Float,
        topRight: Float,
        bottomRight: Float,
        bottomLeft: Float
    ) {
        mCornerRadiiArrayList[0] = topLeft
        mCornerRadiiArrayList[1] = topLeft
        mCornerRadiiArrayList[2] = topRight
        mCornerRadiiArrayList[3] = topRight
        mCornerRadiiArrayList[4] = bottomRight
        mCornerRadiiArrayList[5] = bottomRight
        mCornerRadiiArrayList[6] = bottomLeft
        mCornerRadiiArrayList[7] = bottomLeft
    }

    private fun obtainAttr(attrs: AttributeSet?). {
        val attr = context.obtainStyledAttributes(attrs, R.styleable.ShapeTextView)
        mBorderWidth = attr.getDimension(
            R.styleable.ShapeTextView_stv_width,
            mBorderWidth.toFloat()
        ).toInt()
        mSolidColor = attr.getColor(R.styleable.ShapeTextView_stv_solid, DEFAULT_SOLID_COLOR)
        mUnusableSolidColor =
            attr.getColor(R.styleable.ShapeTextView_stv_unusable_solid, DEFAULT_SOLID_COLOR)
        mBorderColor =
            attr.getColor(R.styleable.ShapeTextView_stv_border_color, DEFAULT_SOLID_COLOR)
        mUnusableBorderColor =
            attr.getColor(R.styleable.ShapeTextView_stv_unusable_border_color, DEFAULT_SOLID_COLOR)
        val commonCornerSize = attr.getDimension(R.styleable.ShapeTextView_stv_corners, 0F)
        setDefaultCornerRadii(
            commonCornerSize,
            commonCornerSize,
            commonCornerSize,
            commonCornerSize
        )
        var topLeftCornerSize = commonCornerSize
        if (attr.hasValue(R.styleable.ShapeTextView_stv_topLeft_corners)) {
            topLeftCornerSize = attr.getDimension(R.styleable.ShapeTextView_stv_topLeft_corners, 0F)}var topRightCornerSize = commonCornerSize
        if (attr.hasValue(R.styleable.ShapeTextView_stv_topRight_corners)) {
            topRightCornerSize =
                attr.getDimension(R.styleable.ShapeTextView_stv_topRight_corners, 0F)}var bottomLeftCornerSize = commonCornerSize
        if (attr.hasValue(R.styleable.ShapeTextView_stv_bottomLeft_corners)) {
            bottomLeftCornerSize =
                attr.getDimension(R.styleable.ShapeTextView_stv_bottomLeft_corners, 0F)}var bottomRightCornerSize = commonCornerSize
        if (attr.hasValue(R.styleable.ShapeTextView_stv_bottomRight_corners)) {
            bottomRightCornerSize =
                attr.getDimension(R.styleable.ShapeTextView_stv_bottomRight_corners, 0F)
        }
        setDefaultCornerRadii(
            topLeftCornerSize,
            topRightCornerSize,
            bottomRightCornerSize,
            bottomLeftCornerSize
        )
        mGradientEnable = attr.getBoolean(R.styleable.ShapeTextView_stv_gradient_enable, false)
        if (mGradientEnable) {
            mGradientColors[0] =
                attr.getColor(R.styleable.ShapeTextView_stv_gradient_start, DEFAULT_SOLID_COLOR)
            mGradientColors[1] =
                attr.getColor(
                    R.styleable.ShapeTextView_stv_unusable_gradient_end,
                    DEFAULT_SOLID_COLOR
                )
            mUnusableGradientColors[0] =
                attr.getColor(R.styleable.ShapeTextView_stv_gradient_start, DEFAULT_SOLID_COLOR)
            mUnusableGradientColors[1] =
                attr.getColor(
                    R.styleable.ShapeTextView_stv_unusable_gradient_end,
                    DEFAULT_SOLID_COLOR
                )
            mAngle = attr.getInt(R.styleable.ShapeTextView_stv_gradient_angle, 0)
        }
        mDashGapWidth =
            attr.getDimension(R.styleable.ShapeTextView_stv_dash_gap, mDashWidth)
        mDashWidth =
            attr.getDimension(R.styleable.ShapeTextView_stv_dash_width, mDashWidth)
        mUnusableTextColor =
            attr.getColor(R.styleable.ShapeTextView_stv_unusable_textColor, textColors.defaultColor)
        attr.recycle()
    }

    private fun getGradientOrientation(mAngle: Int): GradientDrawable.Orientation {
        return when (mAngle) {
            TOP_BOTTOM -> GradientDrawable.Orientation.TOP_BOTTOM
            TR_BL -> GradientDrawable.Orientation.TR_BL
            RIGHT_LEFT -> GradientDrawable.Orientation.RIGHT_LEFT
            BR_TL -> GradientDrawable.Orientation.BR_TL
            BOTTOM_TOP -> GradientDrawable.Orientation.BOTTOM_TOP
            BL_TR -> GradientDrawable.Orientation.BL_TR
            LEFT_RIGHT -> GradientDrawable.Orientation.LEFT_RIGHT
            TL_BR -> GradientDrawable.Orientation.TL_BR
            else -> GradientDrawable.Orientation.LEFT_RIGHT
        }
    }

    override fun refreshDrawableState(a) {
        setEnableStateDrawable(isEnabled)
        super.refreshDrawableState()
    }

    private fun setEnableStateDrawable(isEnabled: Boolean) {
        mGradientDrawable = GradientDrawable()
        if (mGradientEnable) {
            mGradientDrawable.colors = if (isEnabled) {
                mGradientColors
            } else {
                mUnusableGradientColors
            }
            if (mAngle > 0) {
                mGradientDrawable.orientation = getGradientOrientation(mAngle)
            }
        } else {
            mGradientDrawable.setColor(
                if (isEnabled) {
                    mSolidColor
                } else {
                    mUnusableSolidColor
                }
            )
        }
        if (isEnabled) {
            setTextColor(textColors.defaultColor)
        } else {
            setTextColor(mUnusableTextColor)
        }
        val borderColor = if (isEnabled) {
            mBorderColor
        } else {
            mUnusableBorderColor
        }
        mGradientDrawable.setStroke(mBorderWidth, borderColor, mDashWidth, mDashGapWidth)
        mGradientDrawable.cornerRadii = mCornerRadiiArrayList
        background = mGradientDrawable
    }
}
Copy the code

Resource files used

<declare-styleable name="ShapeTextView">
        <! -- Text color (not available)-->
        <attr name="stv_unusable_textColor" format="color" />
        <! Stroke width -->
        <attr name="stv_width" format="dimension" />
        <! Fill color -->
        <attr name="stv_solid" format="color" />
        <! -- Fill color (unavailable) -->
        <attr name="stv_unusable_solid" format="color" />
        <! -- Border color -->
        <attr name="stv_border_color" format="color" />
        <! -- Border color (not available)-->
        <attr name="stv_unusable_border_color" format="color" />
        <! - the rounded - >
        <attr name="stv_corners" format="dimension" />
        <! -- Rounded corner -- upper left corner -->
        <attr name="stv_topLeft_corners" format="dimension" />
        <! -- Rounded corner -- top right -->
        <attr name="stv_topRight_corners" format="dimension" />
        <! -- Rounded corner -- lower left corner -->
        <attr name="stv_bottomLeft_corners" format="dimension" />
        <! -- Rounded corner -- lower right corner -->
        <attr name="stv_bottomRight_corners" format="dimension" />
        <! -- Support gradient -->
        <attr name="stv_gradient_enable" format="boolean" />
        <! Gradient start color -->
        <attr name="stv_gradient_start" format="color" />
        <! -- Gradient start color (unavailable)-->
        <attr name="stv_unusable_gradient_start" format="color" />
        <! Gradient end color -->
        <attr name="stv_gradient_end" format="color" />
        <! -- Gradient end color (unavailable)-->
        <attr name="stv_unusable_gradient_end" format="color" />
        <! -- Gradient Angle -->
        <attr name="stv_gradient_angle">
            <enum name="TOP_BOTTOM" value="1" />
            <enum name="TR_BL" value="2" />
            <enum name="RIGHT_LEFT" value="3" />
            <enum name="BR_TL" value="4" />
            <enum name="BOTTOM_TOP" value="5" />
            <enum name="BL_TR" value="6" />
            <enum name="LEFT_RIGHT" value="Seven" />
            <enum name="TL_BR" value="8" />
        </attr>
        <! -- Dashed line mode spacing -->
        <attr name="stv_dash_gap" format="dimension" />
        <! -- Dashed mode width -->
        <attr name="stv_dash_width" format="dimension" />
    </declare-styleable>
Copy the code

If you want to understand the system, welcome to subscribe to my TextView special lecture, so that you can thoroughly understand the principle of TextView drawing, from the perspective of the source code to understand Google design ideas.

Welcome to the Textview – to show you how to draw a Textview

Ok, that’s it, see you! ~