首页 > 其他分享 >自定义View-字体篇

自定义View-字体篇

时间:2023-06-02 11:03:05浏览次数:60  
标签:canvas flannery 自定义 val mPaint top Paint 字体 View



文章目录

  • 来源
  • 字体medium
  • FontMetrics
  • textSize这是为70F的时候
  • 自己绘制的线条 top、ascent、descent、bottom
  • baseLine 通过ascent和descent计算, top和bottom计算出来的还不一样
  • TextPaint 和 StaticLayout
  • breakText (CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth)
  • setUnderlineText(boolean underlineText)
  • setTypeface(Typeface typeface)
  • Typyface.defaultFromStyle(int style)
  • Typyface.create(String familyName, int style)和create(Typeface family, int style)
  • Typeface.createFromAsset(AssetManager mgr, String path)、createFromFile(String path)和createFromFile(File path)
  • setTextSkewX(float skewX)
  • setTextSize (float textSize)
  • setTextScaleX (float scaleX)
  • setTextAlign (Paint.Align align)
  • setSubpixelText (boolean subpixelText)
  • setStrikeThruText (boolean strikeThruText)
  • setLinearText (boolean linearText)
  • setFakeBoldText (boolean fakeBoldText)
  • measureText (String text),measureText (CharSequence text, int start, int end),measureText (String text, int start, int end),measureText (char[] text, int index, int count)
  • setDither(boolean dither)
  • setMaskFilter(MaskFilter maskfilter)
  • BlurMaskFilter
  • EmbossMaskFilter
  • setRasterizer (Rasterizer rasterizer)
  • setPathEffect(PathEffect effect)
  • 心电图
  • setStrokeCap(Paint.Cap cap)
  • setStrokeJoin(Paint.Join join)
  • setShadowLayer(float radius, float dx, float dy, int shadowColor)


来源

TextView极细字体

字体medium

<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item> 

<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">italic</item>

<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">bold</item>

<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">bold|italic</item>

<item name="android:fontFamily">sans-serif-light</item>
<item name="android:textStyle">normal</item>

<item name="android:fontFamily">sans-serif-light</item>
<item name="android:textStyle">italic</item>

<item name="android:fontFamily">sans-serif-thin</item>
<item name="android:textStyle">normal</item>

<item name="android:fontFamily">sans-serif-thin</item>
<item name="android:textStyle">italic</item>

<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textStyle">normal</item>

<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textStyle">italic</item>

<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textStyle">bold</item>

<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textStyle">bold|italic</item>

5.0 以上
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textStyle">normal</item>

<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textStyle">italic</item>

<item name="android:fontFamily">sans-serif-black</item>
<item name="android:textStyle">italic</item>

FontMetrics

名为字体测量,是Paint的一个内部类

/**
     * Class that describes the various metrics for a font at a given text size.
     * Remember, Y values increase going down, so those values will be positive,
     * and values that measure distances going up will be negative. This class
     * is returned by getFontMetrics().
     */
    public static class FontMetrics {
        /**
         * The maximum distance above the baseline for the tallest glyph in
         * the font at a given text size.
         */
        public float   top;
        /**
         * The recommended distance above the baseline for singled spaced text.
         */
        public float   ascent;
        /**
         * The recommended distance below the baseline for singled spaced text.
         */
        public float   descent;
        /**
         * The maximum distance below the baseline for the lowest glyph in
         * the font at a given text size.
         */
        public float   bottom;
        /**
         * The recommended additional space to add between lines of text.
         */
        public float   leading;
    }

自定义View-字体篇_android

这张图很简单但是也很扼要的说明了top,ascent,descent,bottom,leading这五个参数。
在Android中,t文字的绘制都是从Baseline处开始的。
top; top的意思其实就是除了Baseline到字符顶端的距离外还应该包含这些符号的高度
ascent; baseline往上至字符最高处的距离,上坡度
descent; baseline往下至字符最底处的距离,下坡度
bottom; bottom的意思也是一样,一般情况下我们极少使用到类似的符号所以往往会忽略掉这些符号的存在
leading; 表示上一行字符的sescent到该行字符的ascent之间的距离,行间距

自定义View-字体篇_android_02

class FontView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
    val TEXT = "ap爱哥ξτβбпшㄎㄊěǔぬも┰┠№@↓";
    val mPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        this.textSize = 50F
        this.color = Color.BLACK
    }
    init {
        val fontMetrics = mPaint.fontMetrics
        Log.d("flannery", "ascent = ${fontMetrics.ascent}")
        Log.d("flannery", "top = ${fontMetrics.top}")
        Log.d("flannery", "leading = ${fontMetrics.leading}")
        Log.d("flannery", "descent = ${fontMetrics.descent}")
        Log.d("flannery", "bottom = ${fontMetrics.bottom}")
    }
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.drawText(TEXT, 0F, Math.abs(mPaint.fontMetrics.top), mPaint)
    }
}
D/flannery: ascent = -46.38672
D/flannery: top = -52.807617
D/flannery: leading = 0.0
D/flannery: descent = 12.207031
D/flannery: bottom = 13.549805

自定义View-字体篇_android_03


我们将文本绘制的起点Y坐标向下移动Math.abs(fontMetrics.top)个单位(注:top是负数),相当于把文本的Baseline向下移动Math.abs(fontmetrics.top)个单位,此时文本的顶部刚好会和屏幕底部重合

从代码中我们可以看到一个很特别的现象,在我们绘制文本之前我们便可以获取文本的FontMetrics属性

textSize这是为70F的时候

D/flannery: ascent = -64.94141
D/flannery: top = -73.930664
D/flannery: leading = 0.0
D/flannery: descent = 17.089844
D/flannery: bottom = 18.969727

自定义View-字体篇_android_04

  • setTypeface这是为Typeface.SERIF的时候
D/flannery: ascent = -74.819336
D/flannery: top = -73.34961
D/flannery: leading = 0.0
D/flannery: descent = 20.507813
D/flannery: bottom = 17.5

自定义View-字体篇_sed_05

所有的值也改变了,那么我们知道这样的一个东西有什么用呢?如上所说文本的绘制从Baseline开始,并且Baseline并非文本的分割线,当我们想让文本绘制的时候居中屏幕或其他的东西就需要计算Baseline的Y轴坐标,

比如我们让我们的文本居中画布:

自定义View-字体篇_android

自定义View-字体篇_ide_07

class FontView2(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
    val TEXT = "ap爱哥ξτβбпшㄎㄊěǔぬも┰┠№@↓";
    var baseX = 0F
    var baseY = 0F
    val mPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        this.color = Color.BLACK
    }
    val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        this.style = Paint.Style.STROKE
        this.textSize = 70F
        this.strokeWidth = 1F
        this.color = Color.RED
    }

    init {
        val fontMetrics = mPaint.fontMetrics
        Log.d("flannery", "ascent = ${fontMetrics.ascent}")
        Log.d("flannery", "top = ${fontMetrics.top}")
        Log.d("flannery", "leading = ${fontMetrics.leading}")
        Log.d("flannery", "descent = ${fontMetrics.descent}")
        Log.d("flannery", "bottom = ${fontMetrics.bottom}")
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.let { c->
            //计算Baseline绘制的起点X轴坐标
            baseX = c.width.div(2).toFloat() - textPaint.measureText(TEXT) / 2
            //计算Baseline绘制的Y坐标
            baseY = c.height.div(2).toFloat() - (textPaint.descent() + textPaint.ascent()).div(2)

            c.drawText(TEXT, baseX, baseY, textPaint)
            // 为了便于理解我们在画布中新绘制一条中线
            c.drawLine(0F, c.height.div(2).toFloat(), c.width.toFloat(),c.height.div(2).toFloat(), mPaint)
        }
    }
}

自己绘制的线条 top、ascent、descent、bottom

自定义View-字体篇_android_08

val centerTop = baseLine + top
val centerAscent = baseLine + ascent
val centerDescent = baseLine + descent
val centerBottom = baseLine + bottom
mPaint.color = Color.RED
canvas.drawLine(0f, centerTop, cwidth, centerTop, mPaint)
mPaint.color = Color.GREEN
canvas.drawLine(0f, centerAscent, cwidth, centerAscent, mPaint)
mPaint.color = Color.BLUE
canvas.drawLine(0f, centerDescent, cwidth, centerDescent, mPaint)
mPaint.color = Color.CYAN
canvas.drawLine(0f, centerBottom, cwidth, centerBottom, mPaint)

baseLine 通过ascent和descent计算, top和bottom计算出来的还不一样

自定义View-字体篇_ide_09

class FontView2(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
    val TEXT = "X赵ξτㄎ";
    val mPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        this.color = Color.BLACK
        this.style = Paint.Style.FILL_AND_STROKE
        this.strokeWidth = 2F
    }
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.let { c ->
            drawBaseline(c)
            //drawCenter(c)
        }
    }

    fun drawBaseline(canvas: Canvas) {
        mPaint.textSize = 250F //必须在测量宽度之前

        val halfTextWidth = mPaint.measureText(TEXT).div(2)


        val fontMetrics = mPaint.fontMetrics
        Log.d("flannery", "ascent = ${fontMetrics.ascent}")
        Log.d("flannery", "top = ${fontMetrics.top}")
        Log.d("flannery", "leading = ${fontMetrics.leading}")
        Log.d("flannery", "descent = ${fontMetrics.descent}")
        Log.d("flannery", "bottom = ${fontMetrics.bottom}")

        //计算Baseline绘制的起点X轴坐标
        val cwidth = canvas.width.toFloat()
        val cheight = canvas.height.toFloat()

        val halfWidth = cwidth.div(2)
        val halfHeight = cheight.div(2)

        // 十字中线
        mPaint.color = Color.BLACK
        mPaint.strokeWidth = 5F
        canvas.drawLine(0F, halfHeight, cwidth, halfHeight, mPaint)
        canvas.drawLine(halfWidth, 0F, halfWidth, cheight, mPaint)


        mPaint.strokeWidth = 1F

        val ascent = mPaint.fontMetrics.ascent
        val top = mPaint.fontMetrics.top
        val leading = mPaint.fontMetrics.leading
        val descent = mPaint.fontMetrics.descent
        val bottom = mPaint.fontMetrics.bottom

        val halfTextHeightTopBottom = (bottom + top).div(2)
        val halfTextHeightCescent = (descent + ascent).div(2)

        val baseLineHalfHeight = halfHeight //baseline 跟中心重合
        val baseLineTextCenterY = halfHeight - (ascent+descent).div(2)
        val baseLine = baseLineTextCenterY

        val centerYTopBottom = baseLine + halfTextHeightTopBottom
        val centerYSCent = baseLine + halfTextHeightCescent

        //画文字
        mPaint.color = Color.BLACK
        canvas.drawText(TEXT, halfWidth - halfTextWidth, baseLine, mPaint)

        // 画top。。。。
        if(true) {
            val centerTop = baseLine + top
            val centerAscent = baseLine + ascent
            val centerDescent = baseLine + descent
            val centerBottom = baseLine + bottom
            mPaint.color = Color.RED
            canvas.drawLine(0f, centerTop, cwidth, centerTop, mPaint)
            mPaint.color = Color.GREEN
            canvas.drawLine(0f, centerAscent, cwidth, centerAscent, mPaint)
            mPaint.color = Color.BLUE
            canvas.drawLine(0f, centerDescent, cwidth, centerDescent, mPaint)
            mPaint.color = Color.CYAN
            canvas.drawLine(0f, centerBottom, cwidth, centerBottom, mPaint)
        }

        // 中线
        if(true) {
            mPaint.color = Color.RED
            canvas.drawLine(0f, centerYTopBottom, cwidth, centerYTopBottom, mPaint)
            mPaint.color = Color.GREEN
            canvas.drawLine(0f, centerYSCent, cwidth, centerYSCent, mPaint)
        }

    }


//    fun drawCenter(c: Canvas) {
//        //计算Baseline绘制的起点X轴坐标
//        val cwidth = c.width.toFloat()
//        val cheight = c.height.toFloat()
//
//        val baseX = cwidth.div(2) - textPaint.measureText(TEXT) / 2
//        //计算Baseline绘制的Y坐标
//        val baseY = cheight.div(2) - (textPaint.descent() + textPaint.ascent()).div(2)
//
//        c.drawText(TEXT, baseX, baseY, textPaint)
//        // 为了便于理解我们在画布中新绘制一条中线
//        c.drawLine(
//            0F
//            , cheight.div(2)
//            , cwidth
//            , cheight.div(2)
//            , mPaint
//        )
//        c.drawLine(
//            textPaint.measureText(TEXT).div(2)
//            , 0F
//            , textPaint.measureText(TEXT).div(2)
//            , cheight
//            , mPaint
//        )
//    }


    override fun onTouchEvent(event: MotionEvent?): Boolean {
        Log.d("flannery", "x = ${event?.getX()} , y = ${event?.getY()}")
        return super.onTouchEvent(event)
    }
}

TextPaint 和 StaticLayout

class StaticLayoutView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
    val TEXT =
        "This is used by widgets to control text layout. You should not need to use this class directly unless you are implementing your own widget or custom display object, or would be tempted to call Canvas.drawText() directly.";
    val mPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        this.color = Color.BLACK
        this.style = Paint.Style.FILL_AND_STROKE
        this.strokeWidth = 2F
    }
    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.let { canvas ->
            val paint = TextPaint(Paint.ANTI_ALIAS_FLAG)
                .apply {
                    this.textSize = 50F
                    this.color = Color.BLACK
                }

            Log.d("flannery", "ascent = ${paint.fontMetrics.ascent}")
            Log.d("flannery", "top = ${paint.fontMetrics.top}")
            Log.d("flannery", "leading = ${paint.fontMetrics.leading}")
            Log.d("flannery", "descent = ${paint.fontMetrics.descent}")
            Log.d("flannery", "bottom = ${paint.fontMetrics.bottom}")

            Log.d("flannery", "ascent() = ${paint.ascent()}")
//            Log.d("flannery", "top() = ${paint.top()}")
//            Log.d("flannery", "leading() = ${paint.leading()}")
            Log.d("flannery", "descent() = ${paint.descent()}")
//            Log.d("flannery", "bottom() = ${paint.bottom()}")
            
            /*
                D/flannery: ascent = -46.38672
                D/flannery: top = -52.807617
                D/flannery: leading = 0.0
                D/flannery: descent = 12.207031
                D/flannery: bottom = 13.549805
                D/flannery: ascent() = -46.38672
                D/flannery: descent() = 12.207031
             */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                 StaticLayout.Builder
                .obtain(TEXT, 0, TEXT.length, paint, getWidth())
                     .setAlignment(Layout.Alignment.ALIGN_NORMAL)
                     .setLineSpacing(0.0F, 1.0F)
                     .setIncludePad(false)
                     .build()
                     .draw(canvas)
            } else {
                StaticLayout(
                    TEXT,
                    paint,
                    getWidth(),
                    Layout.Alignment.ALIGN_NORMAL,
                    1.0F,
                    0.0F,
                    false
                ).draw(canvas)
            }
            canvas.restore()
        }
    }

}

breakText (CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth)

Android 可折叠TextView

val paint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
    this.color = Color.BLACK
    this.textSize = 50F
}
var measuredHeight:FloatArray = floatArrayOf()
// TEXT 原有字符串
// start 从0开始
// 10 到第10个结束
// measureForwards表示向前还是向后测量
// maxWidth表示一个给定的最大宽度在这个宽度内能测量出几个字符
// 可以为空
val breakText = paint.breakText(TEXT, 0, 10, false, paint.measureText(TEXT), measuredHeight)
StaticLayout(
    TEXT.subSequence(0,breakText),
    paint,
    canvas.getWidth(),
    Layout.Alignment.ALIGN_NORMAL,
    1.0F,
    0.0F,
    false
).draw(canvas)

自定义View-字体篇_ide_10

setUnderlineText(boolean underlineText)

设置下划线

setTypeface(Typeface typeface)

设置字体类型,上面我们也使用过,Android中字体有四种样式:

  • BOLD(加粗),
  • BOLD_ITALIC(加粗并倾斜)
  • ITALIC(倾斜),
  • NORMAL(正常);
    而其为我们提供的字体有五种:DEFAULT,DEFAULT_BOLD,MONOSPACE,SANS_SERIF和SERIF,这些什么类型啊、字体啊之类的都很简单大家自己去试试就知道就不多说了。但是系统给我们的字体有限我们可不可以使用自己的字体呢?答案是肯定的!Typeface这个类中给我们提供了多个方法去个性化我们的字体

Typyface.defaultFromStyle(int style)

sDefaults = new Typeface[] {
    DEFAULT,
    DEFAULT_BOLD,
    create((String) null, Typeface.ITALIC),
    create((String) null, Typeface.BOLD_ITALIC),
};

封装typeface

Typyface.create(String familyName, int style)和create(Typeface family, int style)

创建typeface

textPaint.setTypeface(Typeface.create("SERIF", Typeface.NORMAL));
textPaint.setTypeface(Typeface.create(Typeface.SERIF, Typeface.NORMAL));

Typeface.createFromAsset(AssetManager mgr, String path)、createFromFile(String path)和createFromFile(File path)

允许我们使用自己的字体比如我们从asset目录读取一个字体文件

// 获取字体并设置画笔字体
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "kt.ttf");
textPaint.setTypeface(typeface);

setTextSkewX(float skewX)

值为负右倾值为正左倾,默认值为0

自定义View-字体篇_ide_11

paint.textSkewX = -0.45F

setTextSize (float textSize)

setTextScaleX (float scaleX)

setTextScaleX (float scaleX)

将文本沿X轴水平缩放,默认值为1,当值大于1会沿X轴水平放大文本,当值小于1会沿X轴水平缩放文本

缩放前

自定义View-字体篇_ide_12

缩放后

自定义View-字体篇_sed_13

setTextAlign (Paint.Align align)

下面都是从中心点开始绘制的

fun drawBaselineAlign(canvas: Canvas) {
    mPaint.textSize = 100F //必须在测量宽度之前
    //计算Baseline绘制的起点X轴坐标
    val cwidth = canvas.width.toFloat()
    val cheight = canvas.height.toFloat()
    val halfWidth = cwidth.div(2)
    val halfHeight = cheight.div(2)
    // 十字中线
    mPaint.color = Color.BLACK
    mPaint.strokeWidth = 5F
    canvas.drawLine(0F, halfHeight, cwidth, halfHeight, mPaint)
    canvas.drawLine(halfWidth, 0F, halfWidth, cheight, mPaint)
    mPaint.strokeWidth = 1F
    val ascent = mPaint.fontMetrics.ascent
    val top = mPaint.fontMetrics.top
    val leading = mPaint.fontMetrics.leading
    val descent = mPaint.fontMetrics.descent
    val bottom = mPaint.fontMetrics.bottom
    val halfTextHeightTopBottom = (bottom + top).div(2)
    val halfTextHeightCescent = (descent + ascent).div(2)
    val baseLineHalfHeight = halfHeight //baseline 跟中心重合
    val baseLineTextCenterY = halfHeight - (ascent+descent).div(2)
    val baseLine = baseLineHalfHeight
    val centerYTopBottom = baseLine + halfTextHeightTopBottom
    val centerYSCent = baseLine + halfTextHeightCescent
    //画文字
    mPaint.color = Color.BLACK
    mPaint.textAlign = Paint.Align.LEFT
    canvas.drawText(TEXT, halfWidth, baseLine, mPaint)
    // 中线
    if(true) {
        mPaint.color = Color.RED
        canvas.drawLine(0f, centerYTopBottom, cwidth, centerYTopBottom, mPaint)
        mPaint.color = Color.GREEN
        canvas.drawLine(0f, centerYSCent, cwidth, centerYSCent, mPaint)
    }
}
  • mPaint.textAlign = Paint.Align.LEFT

自定义View-字体篇_sed_14

  • mPaint.textAlign = Paint.Align.RIGHT
  • 自定义View-字体篇_ide_15

  • mPaint.textAlign = Paint.Align.CENTER
  • 自定义View-字体篇_android_16

setSubpixelText (boolean subpixelText)

设置自像素。如果该项为true,将有助于文本在LCD屏幕上的显示效果。

自定义View-字体篇_sed_17

setStrikeThruText (boolean strikeThruText)

文本删除线

setLinearText (boolean linearText)

设置是否打开线性文本标识,这玩意对大多数人来说都很奇怪不知道这玩意什么意思。想要明白这东西你要先知道文本在Android中是如何进行存储和计算的。在Android中文本的绘制需要使用一个bitmap作为单个字符的缓存,既然是缓存必定要使用一定的空间,我们可以通过setLinearText (true)告诉Android我们不需要这样的文本缓存。

setFakeBoldText (boolean fakeBoldText)

设置文本仿粗体

measureText (String text),measureText (CharSequence text, int start, int end),measureText (String text, int start, int end),measureText (char[] text, int index, int count)

测量文本宽度

setDither(boolean dither)

这玩意用来设置我们在绘制图像时的抗抖动,也称为递色,那什么叫抗抖动呢?在Android中我确实不好拿出一个明显的例子,我就在PS里模拟说明一下

自定义View-字体篇_android_18


大家看到的这张七彩渐变图是一张RGB565模式下图片,即便图片不是很大我们依然可以很清晰地看到在两种颜色交接的地方有一些色块之类的东西感觉很不柔和,因为在RGB模式下只能显示2^16=65535种色彩,因此很多丰富的色彩变化无法呈现,而Android呢为我们提供了抗抖动这么一个方法,它会将相邻像素之间颜色值进行一种“中和”以呈现一个更细腻的过渡色:

自定义View-字体篇_android_19

放大来看,其在很多相邻像素之间插入了一个“中间值”:

自定义View-字体篇_android_20

抗抖动不是Android的专利,是图形图像领域的一种解决位图精度的技术。上面说了太多理论性的东西,估计大家都疲惫了,接下来我们来瞅瞅一个比较酷的东西MaskFilter遮罩过滤器!在Paint我们有个方法来设置这东西

setMaskFilter(MaskFilter maskfilter)

javascript:void(0)

BlurMaskFilter

Android中的很多自带控件都有类似软阴影的效果,比如说Button

自定义View-字体篇_sed_21


它周围就有一圈很淡的阴影效果,这种效果看起来让控件更真实,那么是怎么做的呢?其实很简单,使用BlurMaskFilter就可以得到类似的效果

自定义View-字体篇_android_22

val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG).apply {
    this.style = Paint.Style.FILL
    this.color = 0xFF603811.toInt()
    //this.maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.OUTER)
}
canvas.drawColor(Color.WHITE)
var left = 100F; var top = 100F; var  right = 300F; var bottom=300F
//画一个矩形
paint.maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.SOLID)
canvas.drawRect(left, top, right, bottom, paint)
top = top.plus(250F); bottom = bottom.plus(250F)
paint.maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.NORMAL)
canvas.drawRect(left, top, right, bottom, paint)
top = top.plus(250F); bottom = bottom.plus(250F)
paint.maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.OUTER)
canvas.drawRect(left, top, right, bottom, paint)
top = top.plus(250F); bottom = bottom.plus(250F)
paint.maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.INNER)
canvas.drawRect(left, top, right, bottom, paint)

自定义View-字体篇_android_23

private fun drawBlurFilter(canvas: Canvas) {
    val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG).apply {
        this.style = Paint.Style.FILL
        this.color = 0xFF603811.toInt()
        this.maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.OUTER)
    }
    //获取位图
    val srcBitmap =
        BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)
    // 获取位图的Alpha通道图
    val shadowBitmap  = srcBitmap.extractAlpha()
    //绘制阴影
    canvas.drawBitmap(shadowBitmap,100F, 100F, paint)
    //再绘制位图
    canvas.drawBitmap(srcBitmap, 100F, 100F, null)
}

EmbossMaskFilter

自定义View-字体篇_sed_24

自定义View-字体篇_ide_25

private fun drawEmbossMaskFilter(canvas: Canvas) {
    val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG).apply {
        this.style = Paint.Style.FILL
        this.color = 0xFF603811.toInt()
        this.maskFilter = EmbossMaskFilter(floatArrayOf(1f, 1f, 1f), 0.1f, 10f, 20f)
    }
    canvas.drawColor(Color.WHITE)
    //画矩形
    //画8个矩形
    for (index in 1..4){
        canvas.drawRect(100F, 100F*index, 200F, 100F*index+100F, paint)
    }
    for (index in 1..4){
        canvas.drawRect(200F, 100F*index, 300F, 100F*index+100F, paint)
    }
}

setRasterizer (Rasterizer rasterizer)

设置光栅

setPathEffect(PathEffect effect)

自定义View-字体篇_android_26

自定义View-字体篇_android_27

var mPhase = 0F
val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG).apply {
    this.style = Paint.Style.STROKE
    this.color = Color.RED
    this.strokeWidth = 5F
}
val path = Path().apply {
    moveTo(0F, 0F)
    //定义路径的各个点
    Array(30) {
        lineTo(it * 100F, (Math.random() * 300).toFloat())
    }
}
private fun drawPathEffectView(canvas: Canvas) {
    val cornerPathEffect = CornerPathEffect(30F) //圆角效果
    val discretePathEffect = DiscretePathEffect(3.0F, 5.0F) //离散路径效果
    val dashPathEffect = DashPathEffect(floatArrayOf(50F, 10F, 5F, 10F), mPhase) //虚线效果
    val pathDashPathEffect = PathDashPathEffect(
        Path().apply {
            moveTo(0F, 20F)
            lineTo(10F, 0F)
            lineTo(20F, 20F)
            close()
            addCircle(0F, 0F, 3F, Path.Direction.CCW) //ccw 逆时针 CW 顺时针
        },
        52F, //spacing between each stamp of shape
        mPhase,
        PathDashPathEffect.Style.ROTATE //MORPH:变形过度角  ROTATE:旋转过度角 TRANSLATE:改变位置过度
    ) //印章效果
    val composePathEffect = ComposePathEffect(discretePathEffect, pathDashPathEffect) //合并两个特效的,有先后顺序的
    val sumPathEffect = SumPathEffect(pathDashPathEffect, dashPathEffect) 合并两个特效的,没有顺序
    val pathEffects = arrayListOf<PathEffect?>(
        null
        , cornerPathEffect
        , discretePathEffect
        , dashPathEffect
        , pathDashPathEffect
        , composePathEffect
        , sumPathEffect
    )
    //创建路径效果数组
    for (pathEffect in pathEffects) {
        paint.pathEffect = pathEffect
        canvas.drawPath(path, paint)
        canvas.translate(0F, 180F)  //每绘制一条,将画布向下平移250个像素
    }
    mPhase += 1
    Thread.sleep(50)
    invalidate()
}

心电图

自定义View-字体篇_sed_28

public class ECGView extends View {
	private Paint mPaint;// 画笔
	private Path mPath;// 路径对象
 
	private int screenW, screenH;// 屏幕宽高
	private float x, y;// 路径初始坐标
	private float initScreenW;// 屏幕初始宽度
	private float initX;// 初始X轴坐标
	private float transX, moveX;// 画布移动的距离
 
	private boolean isCanvasMove;// 画布是否需要平移
 
	public ECGView(Context context, AttributeSet set) {
		super(context, set);
 
		/*
		 * 实例化画笔并设置属性
		 */
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
		mPaint.setColor(Color.GREEN);
		mPaint.setStrokeWidth(5);
		mPaint.setStrokeCap(Paint.Cap.ROUND);
		mPaint.setStrokeJoin(Paint.Join.ROUND);
		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setShadowLayer(7, 0, 0, Color.GREEN);
 
		mPath = new Path();
		transX = 0;
		isCanvasMove = false;
	}
 
	@Override
	public void onSizeChanged(int w, int h, int oldw, int oldh) {
		/*
		 * 获取屏幕宽高
		 */
		screenW = w;
		screenH = h;
 
		/*
		 * 设置起点坐标
		 */
		x = 0;
		y = (screenH / 2) + (screenH / 4) + (screenH / 10);
 
		// 屏幕初始宽度
		initScreenW = screenW;
 
		// 初始X轴坐标
		initX = ((screenW / 2) + (screenW / 4));
 
		moveX = (screenW / 24);
 
		mPath.moveTo(x, y);
	}
 
	@Override
	public void onDraw(Canvas canvas) {
		canvas.drawColor(Color.BLACK);
 
		mPath.lineTo(x, y);
 
		// 向左平移画布
		canvas.translate(-transX, 0);
 
		// 计算坐标
		calCoors();
 
		// 绘制路径
		canvas.drawPath(mPath, mPaint);
		invalidate();
	}
 
	/**
	 * 计算坐标
	 */
	private void calCoors() {
		if (isCanvasMove == true) {
			transX += 4;
		}
 
		if (x < initX) {
			x += 8;
		} else {
			if (x < initX + moveX) {
				x += 2;
				y -= 8;
			} else {
				if (x < initX + (moveX * 2)) {
					x += 2;
					y += 14;
				} else {
					if (x < initX + (moveX * 3)) {
						x += 2;
						y -= 12;
					} else {
						if (x < initX + (moveX * 4)) {
							x += 2;
							y += 6;
						} else {
							if (x < initScreenW) {
								x += 8;
							} else {
								isCanvasMove = true;
								initX = initX + initScreenW;
							}
						}
					}
				}
			}
 
		}
	}
}

setStrokeCap(Paint.Cap cap)

private fun drawPaintCap(canvas: Canvas) {
    val p1 = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)
        .apply {
            this.strokeCap = Paint.Cap.BUTT //无线冒
            this.style = Paint.Style.STROKE
            this.strokeWidth = 20F
        }
    val p2 = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)
        .apply {
            this.strokeCap = Paint.Cap.SQUARE //方形线冒
            this.style = Paint.Style.STROKE
            this.strokeWidth = 20F
        }
    val p3 = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)
        .apply {
            this.strokeCap = Paint.Cap.ROUND //圆形线冒
            this.style = Paint.Style.STROKE
            this.strokeWidth = 20F
        }
    // 三个有宽度的线条
    canvas.drawLine(100F, 200F, 400F, 200F, p1)
    canvas.drawLine(100F, 250F, 400F, 250F, p2)
    canvas.drawLine(100F, 300F, 400F, 300F, p3)
    //画条参考线
    p1.also { paint -> paint.strokeWidth=2F; paint.color = Color.GREEN}
    canvas.drawLine(100F, 150F, 100F, 350F, p1)
    canvas.drawLine(400F, 150F, 400F, 350F, p1)
}
}

自定义View-字体篇_sed_29

setStrokeJoin(Paint.Join join)

自定义View-字体篇_sed_30

private fun drawJoin(canvas: Canvas) {
    val mPaintForJoin: Paint = Paint()
    mPaintForJoin.strokeWidth = 40F
    mPaintForJoin.color = Color.GREEN
    mPaintForJoin.style = Paint.Style.STROKE //有边框
    mPaintForJoin.isAntiAlias = true //抗锯齿
//        mPaintForJoin.strokeJoin = Paint.Join.MITER//结合处为锐角
//        mPaintForJoin.strokeJoin = Paint.Join.ROUND//结合处为圆弧
//        mPaintForJoin.strokeJoin = Paint.Join.BEVEL//结合处为直线
    val path:Path = Path()
    path.moveTo(500F, 100F)
    path.lineTo(600F, 100F)
    path.lineTo(500F, 200F)
    mPaintForJoin.strokeJoin = Paint.Join.MITER//结合处为锐角
    canvas.drawPath(path, mPaintForJoin)
    path.moveTo(500F, 200F)
    path.lineTo(600F, 200F)
    path.lineTo(500F, 300F)
    mPaintForJoin.strokeJoin = Paint.Join.ROUND//结合处为圆弧
    canvas.drawPath(path, mPaintForJoin)
    path.moveTo(500F, 300F)
    path.lineTo(600F, 300F)
    path.lineTo(500F, 400F)
    mPaintForJoin.strokeJoin = Paint.Join.BEVEL//结合处为直线
    canvas.drawPath(path, mPaintForJoin)
    val paint:Paint = Paint()
    paint.color = Color.RED
    paint.strokeWidth = 2F
    paint.style = Paint.Style.STROKE
    //左边线
    canvas.drawLine(500F,0F, 500F, 420F, paint)
    //右边线
    canvas.drawLine(600F,0F, 600F, 420F, paint)
}

setShadowLayer(float radius, float dx, float dy, int shadowColor)

设置阴影效果

自定义View-字体篇_sed_31

private fun  drawShadowView(canvas: Canvas) {
    val paint  = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)
        .apply {
            this.color = Color.RED
            this.style = Paint.Style.FILL
            this.setShadowLayer(40F, 40F, 40F, Color.DKGRAY)
        }
    canvas.drawRect(100F, 100F, 400F, 400F, paint)
}


标签:canvas,flannery,自定义,val,mPaint,top,Paint,字体,View
From: https://blog.51cto.com/u_11797608/6400797

相关文章

  • Spring Boot如何自定义监控指标
    1.创建项目pom.xml引入相关依赖<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0https://maven.apache.org/xsd/maven-4.0.0.xsd"......
  • spinner自定义[转]
    转自:https://www.jianshu.com/p/7b03ade29091spinner_drop_down_shape的代码<?xmlversion="1.0"encoding="utf-8"?><layer-listxmlns:android="http://schemas.android.com/apk/res/android"><item><shap......
  • Vue-自定义icon实现
    在项目中引入了element-ui之后,发现其内置的icon有限,无法满足项目的需求,因此需自定义icon来实现需求。在vue项目的components下新建SvgIcon目录,在SvgIcon目录下新建index.vue插入代码:<template><svgclass="svg-icon"aria-hidden="true"><use:xlink:href="ic......
  • props自定义属性
    props含义props是组件的自定义属性,在封装通用组件的时候,合理的使用props可以极大地提高组件的复用性。注意:全局组件在main.js里注册;使用者中标签直接调用(注意v-bind);组件里注意props的只读性。method单行代码简写仅单行能这么写<template><div> <button@click="add">+1<......
  • 实现表格中各单元格字段都支持自定义点灯的思路
    1.数据库,增加一个点灯信息字段:内容为json字符串存储,key即为每个列的字段名,内容就为点灯颜色。eg:lightInfo:{"name":"red","id":"blue"}2.前台用lightInfo[该列对应的具体的字段名]动态获取对应字段的点灯信息。3.前台点灯的编辑方式,可以采用vxetable右键menuConfig或是......
  • 【二十一】memoryview() 函数(1)
    【二十一】memoryview()函数(1)【1】作用memoryview()函数返回给定参数的内存查看对象(memoryview)。所谓内存查看对象是指对支持缓冲区协议的数据进行包装在不需要复制对象基础上允许Python代码访问。【2】语法memoryview(obj)obj:对象返回值:返回元组列......
  • mysql functions ,LAST_INSERT_ID() 或 自定义主键
    http://dev.mysql.com/doc/refman/5.6/en/information-functions.html LAST_INSERT_ID() 这个值如果各个table都有一个自增的id,那么各个table用各自的LAST_INSERT_ID()  自定义:#固定前缀(2位)+时间戳(13位)+随机数(7位)SELECTCONCAT('AB',#......
  • 成品直播源码推荐,Android 自定义颜色样式
    成品直播源码推荐,Android自定义颜色样式<?xmlversion="1.0"encoding="utf-8"?><resources>  <colorname="colorPrimary">#7bb736</color>  <colorname="colorPrimaryDark">#16c24b</color>  <c......
  • android ImageView 设定宽度铺满,高度自定义
    问题:imageView宽度铺满,高度自定义,会出现上下留白的现象(一般比例的还好)解决:<xxx.xxx.xxx.xxx.FullWidthImageViewandroid:id="@+id/iv_image_corp"android:layout_width="match_parent"android:layout_height="wrap_content"......
  • 规则引擎的低代码日记——自定义函数编程操作(类excel函数)
    它是技术源码可开放的JAVA规则引擎,采用springcloud+VUE的技术架构进行构建,其中对数据的灵活加工处理采用的是函数式编程的思路(类excel函数配置),是其亮点功能。它允许开发人员定义和管理应用程序的规则,并在应用程序中执行这些规则。在规则引擎中,从数据加工成变量并使用函数式编程......