安卓自定義view中 繪畫基本圖形點線面,矩形,方形,圓,扇形,文字及沿着特定方向佈局,自定義圓角ImageView圖片等等相關api使用方法及舉例

安卓自定義view中 繪畫基本圖形點線面,矩形,方形,圓,扇形,文字及沿着特定方向佈局,自定義圓角ImageView圖片等等相關api使用方法及舉例,圖片壓縮處理邏輯


本文旨在介紹自定義View的實現及流程
着重介紹安卓中的Canvas的繪製方法,讓你從容面對各種view
並附帶自定義圓角ImageView,和圖片壓縮處理等功能

詳細內容可轉連接:https://mp.weixin.qq.com/s/DL9XFo8Zd4osHvAy7efgog

簡單那說一下 View 繪製的三個流程

 在自定義View的時候一般需要重寫父類的onMeasure()、onLayout()、onDraw()三個方法,來完成視圖的展示過程。當然,這三個暴露給開發者重寫的方法只不過是整個繪製流程的冰山一角,更多複雜的幕後工作,都讓系統給代勞了。一個完整的繪製流程包括measure、layout、draw三個步驟,其中:

 measure:測量。系統會先根據xml佈局文件和代碼中對控件屬性的設置,來獲取或者計算出每個View和ViewGrop的尺寸,並將這些尺寸保存下來。

 layout:佈局。根據測量出的結果以及對應的參數,來確定每一個控件應該顯示的位置。

 draw:繪製。確定好位置後,就將這些控件繪製到屏幕上。
 
 重中之重:draw

Canvas 簡單理解:畫布

Canvas繪圖有三個基本要素:Canvas、繪圖座標系以及Paint。

Canvas是畫布,我們通過Canvas的各種drawXXX方法將圖形繪製到Canvas上面,在drawXXX方法中我們需要傳入要繪製的圖形的座標形狀,還要傳入一個畫筆Paint。

基礎知識:

Canvas的常用操作速查表

操作類型 相關API 備註
繪製顏色 drawColor, drawRGB, drawARGB 使用單一顏色填充整個畫布
繪製基本形狀 drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc 依次爲 點、線、矩形、圓角矩形、橢圓、圓、圓弧
繪製圖片 drawBitmap, drawPicture 繪製位圖和圖片
繪製文本 drawText, drawPosText, drawTextOnPath 依次爲 繪製文字、繪製文字時指定每個文字位置、根據路徑繪製文字
繪製路徑 drawPath 繪製路徑,繪製貝塞爾曲線時也需要用到該函數
頂點操作 drawVertices, drawBitmapMesh 通過對頂點操作可以使圖像形變,drawVertices直接對畫布作用、 drawBitmapMesh只對繪製的Bitmap作用
畫布剪裁 clipPath, clipRect 設置畫布的顯示區域
畫布快照 save, restore, saveLayerXxx, restoreToCount, getSaveCount 依次爲 保存當前狀態、 回滾到上一次保存的狀態、 保存圖層狀態、 回滾到指定狀態、 獲取保存次數
畫布變換 translate, scale, rotate, skew 依次爲 位移、縮放、 旋轉、錯切
Matrix(矩陣) getMatrix, setMatrix, concat 實際畫布的位移,縮放等操作的都是圖像矩陣Matrix,只不過Matrix比較難以理解和使用,故封裝了一些常用的方法。

下方會對對應的API進行詳解和舉例使用

  • 1,繪製單點 多點

基本思路
繼承VIew,重寫onDraw方法,進行繪製
//獲取畫筆 設置畫筆屬性
//調起畫布 canvas 的drawPoint方法 輸入相應的參數即可 繪畫出 對應的點

Paint 對於 Text 的相關設置
1,普通設置

paint.setStrokeWidth(5):設置畫筆寬度
paint.setAntiAlias(true):設置是否使用抗鋸齒功能,如果使用,會導致繪圖速度變慢
paint.setStyle(Paint.Style.FILL):設置繪圖樣式,對於設置文字和幾何圖形都有效,可取值有三種 :

1、Paint.Style.FILL:填充內部 
2、Paint.Style.FILL_AND_STROKE:填充內部和描邊 
3、Paint.Style.STROKE:僅描邊
paint.setTextAlign(Align.CENTER):設置文字對齊方式
paint.setTextSize(12):設置文字大小

2,樣式設置

paint.setFakeBoldText(true):設置是否爲粗體文字
paint.setUnderlineText(true):設置下劃線
paint.setTextSkewX((float) -0.25):設置字體水平傾斜度,普通斜體字是 -0.25
paint.setStrikeThruText(true):設置帶有刪除線效果

3,其他設置

paint.setTextScaleX(2):設置水平拉伸,高度不會變
下面是繪製的相應內容圖例展示:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-HEoatD9l-1591362905856)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p204)]

詳細代碼實現如下:

首先準備畫筆工作:

 關於畫筆 Paint 需強調說明一下 onDraw方法中不要創建Paint等對象,因爲這個方法是多次調用的;
所以我這邊做了一個處理
在項目初起就已經把Paint進行全局初始化,然後在其他地方調用的時候,直接設其屬性即可,代碼如下:

`lateinit var projectResources: ProjectResources

class ProjectResources(private val resources: Resources) {

val paintLight by lazy {
    getBasePaint().apply {
        color = Color.LTGRAY
        alpha = 128
        strokeWidth = resources.dpToPx(2)
        textSize = resources.dpToPx(30)
    }
}

val paintDark by lazy {
    getBasePaint().apply {
        color = Color.BLACK
        alpha = 128
        strokeWidth = resources.dpToPx(4)
        textSize = resources.dpToPx(30)
    }
}

private fun getBasePaint(): Paint {
    return Paint().apply {
        style = Paint.Style.STROKE
        strokeCap = Paint.Cap.ROUND
        strokeJoin = Paint.Join.ROUND
        isAntiAlias = true
        textAlign = Paint.Align.CENTER
        textSize = resources.dpToPx(30)
    }
}

}`

繪製單點 drawPoint
/**
 * 繪製單點  drawPoint
 */
class DrawPointView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawPoint(
            0f + paddingStart,//x座標
            0f + paddingTop,//Y座標
            getPaint()//畫筆paint
        )
    }
//獲取畫筆 設置畫筆屬性
    private fun getPaint(): Paint {
        return Paint().apply {
            style = Paint.Style.STROKE
            strokeCap = Paint.Cap.ROUND
            strokeJoin = Paint.Join.ROUND
            isAntiAlias = true
            textAlign = Paint.Align.CENTER
            textSize = resources.dpToPx(30)
            color = Color.GREEN//畫筆顏色
            strokeWidth = resources.dpToPx(16)//畫筆寬度
        }
    }
}

說明一下:我這邊貼出來的代碼 繪製過程中在ondraw調用getPaint方法;
是爲了更直觀的展示出來,看到必須設這個屬性
其實demo中都已經做了全局處理,不會說每次都會創建paint這個對象。

繪製多點 drawPoints

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1VjkOkRq-1591362905859)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p412)]


/**
 * 繪製多點  drawPoints
 */
class DrawPointsView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    override fun onDraw(canvas: Canvas) {

        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawPoints(
            listOf(
                0f + paddingStart, 0f + paddingTop,//第1個點的 x,y
                ((width - paddingEnd)).toFloat(), 0f + paddingTop, //第2個點的 x,y
                ((width - paddingEnd)/2).toFloat(),((height - paddingBottom)/2).toFloat(), //第3個點的 x,y
                0f + paddingStart, ((height - paddingBottom)).toFloat(), //第4個點的 x,y
                (width - paddingEnd).toFloat(), (height - paddingBottom).toFloat() //第5個點的 x,y
            ).toFloatArray(),
            projectResources.paintPoint
        )
    }
}
繪製直線 drawLine

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-h3qfWz8M-1591362905862)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p413)]

//繪製線條  drawLine
class DrawLineView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawLine(
            0f + paddingStart,//起始位置 x座標
            0f + paddingTop,//起始位置 y座標
            (width - paddingEnd).toFloat(),//終點位置  X座標
            (height - paddingBottom).toFloat(),//終點位置 Y座標
            projectResources.paint// 獲取畫筆
        )
    }
}
同時繪製多條線(1) drawLines

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-hqYHyB7a-1591362905868)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p414)]

/**
 * 同時繪製多條線(1) drawLines
 */

class DrawLinesView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawLines(
            listOf(
                //第一條線的起始x座標和Y座標  末尾位置的X座標和Y座標
                0f + paddingStart, 0f + paddingTop, (width / 2f), (height - paddingBottom).toFloat(),
                //第二條線的起始x座標和Y座標  末尾位置的X座標和Y座標
                width / 2f, (height - paddingBottom).toFloat(), (width - paddingEnd).toFloat(), (0f + paddingStart),
                //第三條線的起始x座標和Y座標  末尾位置的X座標和Y座標
                    (width - paddingEnd).toFloat(), (0f + paddingStart),    width / 2f, (height/2f).toFloat()
                ).toFloatArray(),
            projectResources.paint//畫筆
        )
    }
}
同時繪製多條線(2)

//使用指定的畫筆繪製指定的路徑(線路)。路徑(線路)將根據繪畫的風格填充或裝裱。
兩條線交叉

同時繪製多條線(2)
//使用指定的畫筆繪製指定的路徑(線路)。路徑(線路)將根據繪畫的風格填充或裝裱。
兩條線交叉
class DrawPathView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    private val path by lazy {
        val path = Path()
        path.moveTo(0f + paddingStart, 0f + paddingTop)
        path.lineTo((width - paddingEnd).toFloat(), (height - paddingBottom).toFloat())
        path.moveTo(0f + paddingStart, (height - paddingBottom).toFloat())
        path.lineTo((width - paddingEnd).toFloat(), 0f + paddingTop)
        path
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        //  * Draw the specified path using the specified paint. The path will be filled or framed based on
        //     * the Style in the paint.
        canvas.drawPath(path, projectResources.paint)
    }
}
繪畫圓形圖 drawCircle

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-AdOq31FE-1591362905875)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p415)]

/**
 * 繪畫圓形圖  drawCircle
 */
class DrawCircleView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawCircle(
            width/2f,//圓點中心 X座標
            height/2f,//圓點中心 Y座標
            height/2f - paddingTop,//圓半徑
            projectResources.paint//畫筆
        )
    }
}
繪製矩形框 drawRect

確定確定一個矩形最少需要四個數據,就是對角線的兩個點的座標值,這裏一般採用左上角和右下角的兩個點的座標。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yY0cEP3s-1591362905878)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p416)]

/**
 * 繪製矩形框  drawRect
確定確定一個矩形最少需要四個數據,就是對角線的兩個點的座標值,這裏一般採用左上角和右下角的兩個點的座標。
 */
class DrawRectView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {


    private val rect by lazy {
        Rect(
            0 + paddingStart,//左
            0 + paddingTop,//頂
            width - paddingEnd,//右
            height - paddingBottom//底
        )
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawRect(rect, projectResources.paint)
    }

}

繪製圓角矩形框 drawRoundRect

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Mpqx07qz-1591362905881)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p417)]

class DrawRoundRectView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {


    private val rect by lazy {
        RectF(
            0f + paddingStart,
            0f + paddingTop,
            (width - paddingEnd).toFloat(),
            (height - paddingBottom).toFloat()
        )
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawRoundRect(rect, resources.dpToPx(10), resources.dpToPx(10), projectResources.paint)
    }
}
繪製橢圓框 drawOval

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-IuIeYbHy-1591362905886)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p418)]

//繪製橢圓框  drawOval
class DrawOvalView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    private val rect by lazy {
        RectF(
           0f + paddingStart,//包圍橢圓的矩形左上角端點x
            0f + paddingTop,//包圍橢圓的矩形左上角端點Y
            width.toFloat() - paddingEnd,//橢圓的高度
            height.toFloat() - paddingBottom//橢圓的寬度  當高度和寬度相等時 就是圓
        )
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawOval(rect, projectResources.paint)
    }
}
扇形繪製 (橢圓和圓的)

Canvas中提供了drawArc方法用於繪製弧,這裏的弧指兩種:弧面和弧線,弧面即用弧圍成的填充面,弧線即爲弧面的輪廓線。
api參數解析
oval是RecF類型的對象,其定義了橢圓的形狀。
startAngle指的是繪製的起始角度,如果傳入的startAngle小於0或者大於等於360,那麼用startAngle對360進行取模後作爲起始繪製角度。
sweepAngle指的是從startAngle開始沿着鐘錶的順時針方向旋轉掃過的角度。如果sweepAngle大於等於360,那麼會繪製完整的橢圓弧。如果sweepAngle小於0,那麼會用sweepAngle對360進行取模後作爲掃過的角度。
useCenter是個boolean值,如果爲true,表示在繪製完弧之後,用橢圓的中心點連接弧上的起點和終點以閉合弧;如果值爲false,表示在繪製完弧之後,弧的起點和終點直接連接,不經過橢圓的中心點。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yh6tzYb1-1591362905889)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p419)]

class DrawArcView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    private val rect by lazy {
        RectF(
            0f + paddingStart,
            0f + paddingTop,
            height.toFloat() - paddingBottom,
            height.toFloat() - paddingBottom
        )//// 設置方形區域
    }

    private val rect2 by lazy {
        RectF(
            0f + paddingStart,
            0f + paddingTop,
            width.toFloat() - paddingEnd,
            height.toFloat() - paddingBottom
        )//// 設置矩形區域
    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        /* 設置漸變色 這個正方形的顏色是改變的 */
        val mShader = LinearGradient(
            0f,
            0f,
            100f,
            100f,
            intArrayOf(Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.LTGRAY),
            null,
            Shader.TileMode.REPEAT
        ) // 一個材質,打造出一個線性梯度沿著一條線。
        projectResources.paint.shader = mShader
        if (width == 0 || height == 0) return
        canvas.drawArc(rect,
            0f,//從哪個弧度開始
            330f,//扇形弧度
            true, //是否經過圓心
            projectResources.paint)
        canvas.drawArc(rect2,
            0f,//從哪個弧度開始
            30f,//扇形弧度
            true, //是否經過圓心
            projectResources.paint)
    }
}
文字繪製 在Android開發中,Canvas.drawText不會換行,即使一個很長的字符串也只會顯示一行,超出部分會隱藏在屏幕之外。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Ix1Bln5T-1591362905891)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p420)]

class DrawTextView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    companion object {
        private const val TEXT = "三國演義-西遊記-"
    }
    private val textBound by lazy {
        val textBound = Rect()
        projectResources.paint.getTextBounds(TEXT, 0, TEXT.length, textBound)
        textBound
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawText(TEXT,//:要繪製的文字
            width/2f,//繪製原點x座標
            height/2f - textBound.exactCenterY(),//繪製原點y座標   基線的位置。
            projectResources.paint)
    }
}
可換行的文字 StaticLayout是android中處理文字的一個工具類,StaticLayout 處理了文字換行的問題,

在這裏插入圖片描述

class DrawStaticLayoutTextView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    companion object {
        private const val TEXT = "Too long a line to display in one line, and let it auto wrap go to next line.\nCan handle \"\\n\" as well."
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return

        val staticLayout =
            StaticLayout.Builder.obtain(
                TEXT, 0, TEXT.length, projectResources.textPaint, width
            ).build()

        canvas.save()
        canvas.translate(width / 2f, (height - staticLayout.height)/2f)
        staticLayout.draw(canvas)
        canvas.restore()
    }
}
可以自定義每個文字的位置

在這裏插入圖片描述

class DrawPosTextView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    companion object {
        private const val TEXT = "Testing 123"
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawPosText(TEXT.toCharArray(),//:要繪製的文字數組
            1, //第一個要繪製的文字的索引
            3,//要繪製的文字的個數,用來算最後一個文字的位置,從第一個繪製的文字開始算起
            listOf(width/1.5f, height/1.5f,
                width/2f, height/2f,
                width/3f, height/3f).toFloatArray(),//每個字體的位置,同樣兩兩一組,
            projectResources.paint)
    }
}
繪製文字 沿着路線走

在這裏插入圖片描述

class DrawTextOnPathView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    companion object {
        private const val TEXT = "Test 123"
    }

    private val path by lazy {
        val path = Path()
        path.moveTo(0f + paddingStart, 0f + paddingTop)
        path.lineTo((width/2f), (height - paddingBottom).toFloat())
        path.lineTo((width - paddingEnd).toFloat(), 0f + paddingTop)
        path
    }

    private val textBound by lazy {
        val textBound = Rect()
        projectResources.paint.getTextBounds(TEXT, 0, TEXT.length, textBound)
        textBound
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawPath(path, projectResources.paintLight)
        canvas.drawTextOnPath(TEXT, //文字
            path,//路徑 軌跡
            0f,//與路徑起始點的水平偏移距離
            -textBound.exactCenterY(),//與路徑中心的垂直偏移量
            projectResources.paint)
    }
}
將圖片直接繪製到canvas

在這裏插入圖片描述

class DrawBitmapView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    private val bitmap by lazy {
        BitmapFactory.decodeResource(resources, R.drawable.image)
    }

    private val rect by lazy {
        Rect(0 + paddingLeft, 0 + paddingTop, width - paddingRight, height - paddingBottom)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return

        canvas.drawBitmap(bitmap, null, rect, null)
    }
}
用顏色充填canvas

在這裏插入圖片描述

class DrawColorView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawColor(context.getColor(R.color.colorPrimary))
    }
}
用RGB顏色充填

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-qsx6s3fG-1591362905913)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p426)]

class DrawRGBView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawRGB(255, 0 , 0)
    }
}
圖片和顏色重合

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Zos3SPX2-1591362905916)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p427)]

class DrawARGBView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    private val bitmap by lazy {
        BitmapFactory.decodeResource(resources, R.drawable.image)
    }

    private val rect by lazy {
        Rect(0 + paddingLeft, 0 + paddingTop, width - paddingRight, height - paddingBottom)
    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return

        canvas.drawBitmap(bitmap, null, rect, null)
        canvas.drawARGB(128, 255, 0 , 0)
    }
}
用pain筆的顏色去填充整個畫布

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oVDpMHk7-1591362905919)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p428)]

class DrawPaintView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    companion object {
        private const val TEXT = "Testing 123"
    }
    private val gradientPaint by lazy {
        Paint().apply {
            shader = RadialGradient(
                width/2f,
                height/2f,
                height/2f,
                Color.GREEN,
                Color.RED,
                Shader.TileMode.MIRROR
            )
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawPaint(gradientPaint)
    }
}
通過對頂點操作可以使圖像形變, drawBitmapMesh只對繪製的Bitmap作用

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2oJ6nsew-1591362905921)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p201)]

//通過對頂點操作可以使圖像形變, drawBitmapMesh只對繪製的Bitmap作用
class DrawBitmapMeshView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    private val bitmap by lazy {
        BitmapFactory.decodeResource(resources, R.drawable.santou)
    }

    private val firstX by lazy { 0f + paddingLeft }
    private val firstY by lazy { 0f + paddingTop }
    private val secondX by lazy { width/5f }
    private val secondY by lazy { height/3f }
    private val thirdX by lazy { width.toFloat() - paddingRight }
    private val thirdY by lazy { height.toFloat() - paddingBottom }


    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return

        canvas.drawBitmapMesh(bitmap, 2, 2,
            floatArrayOf(
                firstX, firstY, secondX, firstY, thirdX, firstY,
                firstX, secondY, secondX, secondY, thirdX, secondY,
                firstX, thirdY, secondX, thirdY, thirdX, thirdY),
            0, null, 0, null)

        canvas.drawLine(firstX, firstY, firstX, thirdY, projectResources.paintLight)
        canvas.drawLine(secondX, firstY, secondX, thirdY, projectResources.paintLight)
        canvas.drawLine(thirdX, firstY, thirdX, thirdY, projectResources.paintLight)

        canvas.drawLine(firstX, firstY, thirdX, firstY, projectResources.paintLight)
        canvas.drawLine(firstX, secondY, thirdX, secondY, projectResources.paintLight)
        canvas.drawLine(firstX, thirdY, thirdX, thirdY, projectResources.paintLight)
    }
}
對圖片進行放大切割,及對應點可以給各個頂點顏色紋理渲染

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-y9452AJO-1591362905924)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p202)]

//	通過對頂點操作可以使圖像形變,drawVertices直接對畫布作用、
class DrawVerticesView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    companion object {
        const val DUMMYCOLOR =  -0x100000
    }

    val paint = Paint().apply {
        val bitmap = BitmapFactory.decodeResource(
            resources, R.drawable.santou)
        val shader = BitmapShader(
            bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
        this.shader = shader
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return

        setLayerType(View.LAYER_TYPE_SOFTWARE, null)
        val indices = shortArrayOf(0, 1, 2, 0, 2, 3)

        val verticesColors = intArrayOf(
            Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW,
            DUMMYCOLOR, DUMMYCOLOR, DUMMYCOLOR, DUMMYCOLOR, DUMMYCOLOR, DUMMYCOLOR)

        val verts = floatArrayOf(width/2f, 0f, 0f, height/2f, width/2f, height.toFloat(), width.toFloat(), height/2f)

        canvas.drawVertices(
            Canvas.VertexMode.TRIANGLES,//頂點類型    比如他是三角形(連續3個頂點)或者 四邊形  (連續4個頂點)等等
            verts.size,//頂點數   總共有多少個頂點繪製。
            verts, //頂點數組  [0,0,0,1,1,0,...]  前面有xy 3組 如果是類型是三角形 他就構成一個三角形的繪製基元,往後類推。
            0,//頂點數據 起始位置  可能全部繪製,也可能只繪製部分頂點。與 vertexCount 配置使用 一般爲0
            verts,//   紋理數組  就是對圖片等進行採樣,然後去渲染頂點。
            0,//同上offset  就是偏移量
            verticesColors, //   顏色數組  直接用顏色渲染頂點
            0,//同上offset  就是偏移量
            indices,// 頂點索引 可能只繪製部分頂點 這個就是存放那些頂點的index ,  即verts[index]
            0,// 同上offset  就是偏移量
            indices.size,//繪製多少個索引點。
            paint
        )
    }
}
對圖片進行縮小和放大,位移等等操作

//如果想對繪製位置和比例進行控制用Canvas提供的drawPicture方法繪製
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6snC5Uux-1591362905927)(evernotecid://4DB6EA16-19D7-4CBC-AB5D-D5F3117BCFFB/appyinxiangcom/26133165/ENResource/p203)]

//如果想對繪製位置和比例進行控制用Canvas提供的drawPicture方法繪製
class DrawPictureView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    private val bitmap by lazy {
        BitmapFactory.decodeResource(resources, R.drawable.santou)
    }

    private val rect by lazy {//將高減爲一般半,繪製出的Picture在y軸方向壓縮了一半,也就是rect並不是對圖形進行裁剪,而是縮小或放大,
        Rect(0 + paddingLeft, 0 + paddingTop, width - paddingRight, (height - paddingBottom)/2)
    }
    private val rect1 by lazy {//看到繪製的形狀未變,只不過在y軸上上移了200px,
        Rect(0 + paddingLeft, 200, width - paddingRight, 200+height - paddingBottom)
    }

    private val picture by lazy {
        val picture = Picture()
        val pCanvas = picture.beginRecording(width, height)//開始錄製,在返回的Canvas上進行繪製
        pCanvas.drawBitmap(bitmap, null, rect, null)
        picture.endRecording()//結束錄製
        picture
    }
    private val picture1 by lazy {
        val picture = Picture()
        val pCanvas = picture.beginRecording(width, height)//開始錄製,在返回的Canvas上進行繪製
        pCanvas.drawBitmap(bitmap, null, rect1, null)
        picture.endRecording()//結束錄製
        picture
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) return
        canvas.drawPicture(picture)
        canvas.drawPicture(picture1)
    }
}

以上就是調用onDraw方法繪製View的具體內容。。。

下面在對bitmap着重說明一下,大家應該對Bitmap這個詞有很深的印象,做不好處理,就會經常在你的BUGLIST中出現OOM字段,內存溢出,app莫名其妙的就死了,奔潰掉了,最後看到日誌才發現問題所在。
下面介紹一下我們項目中是如何處理這一塊內容的:
先說一下情景:我們這邊有一個圖片搜索功能,簡單倆說就是根據圖片搜索出對應的商品,用戶可以拍照,從相冊選取。
這其中包括圖片存儲,壓縮,上傳,刪除等一系列操作:
我這邊會根據圖片路徑然後先去判斷該圖片的大小像素等等然後去壓縮圖片:
(1),圖片比例壓縮

`

 //圖片按比例大小壓縮方法(根據路徑獲取圖片並壓縮)
private static Bitmap getImage(String srcPath) {
    BitmapFactory.Options newOpts = new BitmapFactory.Options();
    newOpts.inJustDecodeBounds = true;
    Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此時返回bm爲空

    newOpts.inJustDecodeBounds = false;
    int w = newOpts.outWidth;
    int h = newOpts.outHeight;
    // 現在主流手機比較多是800*480分辨率,所以高和寬我們設置爲
    float hh = 800f;// 這裏設置高度爲800f
    float ww = 480f;// 這裏設置寬度爲480f
    // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
    int be = 1;// be=1表示不縮放
    if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放
        be = (int) (newOpts.outWidth / ww);
    } else if (w < h && h > hh) {// 如果高度高的話根據寬度固定大小縮放
        be = (int) (newOpts.outHeight / hh);
    }
    if (be <= 0)
        be = 1;
    newOpts.inSampleSize = be;// 設置縮放比例
    // 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了
    bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
    return compressImage(bitmap);// 壓縮好比例大小後再進行質量壓縮
}`

(2)圖片質量壓縮

 /**
     * 質量壓縮方法
     *
     * @param image
     * @return
     */
    private static Bitmap compressImage(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        int options = 100;
        while (baos.toByteArray().length / 1024 > 100) {
            baos.reset();
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);
            options -= 10;
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
        return bitmap;
    }

(3)將壓縮的圖片保存至本地

 /**
     * 將壓縮的bitmap保存到SDCard卡臨時文件夾,用於上傳
     *
     * @param filename
     * @param bit
     * @return
     */
    private static String saveMyBitmap(String filename, Bitmap bit) {
        String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/chdd/";
        String filePath = baseDir + filename;
        File dir = new File(baseDir);
        if (!dir.exists()) {
            dir.mkdir();
        }

        File f = new File(filePath);
        try {
            f.createNewFile();
            FileOutputStream fOut = null;
            fOut = new FileOutputStream(f);
            bit.compress(Bitmap.CompressFormat.PNG, 100, fOut);
            fOut.flush();
            fOut.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }

        return filePath;
    }

(4)最後在適當的時候清楚緩存文件和刪除圖片

 /**
     * 清除緩存文件
     */
    public static void deleteCacheFile() {
        File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/chdd/");
        RecursionDeleteFile(file);
    }

    /**
     * 遞歸刪除
     */
    private static void RecursionDeleteFile(File file) {
        if (file.isFile()) {
            file.delete();
            return;
        }
        if (file.isDirectory()) {
            File[] childFile = file.listFiles();
            if (childFile == null || childFile.length == 0) {
                file.delete();
                return;
            }
            for (File f : childFile) {
                RecursionDeleteFile(f);
            }
            file.delete();
        }
    }

**下面還做了一個自定義圓角圖形的類 **,繼承自ImageView,可以在佈局中直接使用,如下

  <com.hexun.logic.widget.RoundImageView
                android:id="@+id/ivAvatar"
                android:layout_width="60dp"
                android:layout_height="60dp"
                android:layout_centerVertical="true"
                android:layout_marginLeft="@dimen/space_20"
                android:src="@drawable/personal_t" />

下面是代碼實現:

下面是圓形圖像的代碼,如何你想實現自定義弧度圓角,調整方法setUp()中的
mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
這行代碼即可,我這邊寫的 橫豎一樣,所以是繪製出圓形圖像。

‘public class RoundImageView extends ImageView {

private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;

private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 2;

private static final int DEFAULT_BORDER_WIDTH = 0;
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
private static final boolean DEFAULT_BORDER_OVERLAY = false;

private final RectF mDrawableRect = new RectF();
private final RectF mBorderRect = new RectF();

private final Matrix mShaderMatrix = new Matrix();
private final Paint mBitmapPaint = new Paint();
private final Paint mBorderPaint = new Paint();
private final Paint mFillPaint = new Paint();

private int mBorderColor = DEFAULT_BORDER_COLOR;
private int mBorderWidth = DEFAULT_BORDER_WIDTH;
private int mFillColor = DEFAULT_FILL_COLOR;

private Bitmap mBitmap;
private BitmapShader mBitmapShader;
private int mBitmapWidth;
private int mBitmapHeight;

private float mDrawableRadius;
private float mBorderRadius;

private ColorFilter mColorFilter;

private boolean mReady;
private boolean mSetupPending;
private boolean mBorderOverlay;

public RoundImageView(Context context) {
    super(context);
    init();
}

public RoundImageView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);

    mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
    mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
    mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
    mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);

    a.recycle();

    init();
}

private void init() {
    super.setScaleType(SCALE_TYPE);
    mReady = true;

    if (mSetupPending) {
        setup();
        mSetupPending = false;
    }
}

@Override
public ScaleType getScaleType() {
    return SCALE_TYPE;
}

@Override
public void setScaleType(ScaleType scaleType) {
    if (scaleType != SCALE_TYPE) {
        throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
    }
}

@Override
public void setAdjustViewBounds(boolean adjustViewBounds) {
    if (adjustViewBounds) {
        throw new IllegalArgumentException("adjustViewBounds not supported.");
    }
}

@Override
protected void onDraw(Canvas canvas) {
    if (mBitmap == null) {
        return;
    }

    if (mFillColor != Color.TRANSPARENT) {
        canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mFillPaint);
    }
    canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mBitmapPaint);
    if (mBorderWidth != 0) {
        canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mBorderRadius, mBorderPaint);
    }
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    setup();
}

public int getBorderColor() {
    return mBorderColor;
}

public void setBorderColor(int borderColor) {
    if (borderColor == mBorderColor) {
        return;
    }

    mBorderColor = borderColor;
    mBorderPaint.setColor(mBorderColor);
    invalidate();
}

public void setBorderColorResource(@ColorRes int borderColorRes) {
    setBorderColor(ContextCompat.getColor(getContext(), borderColorRes));
}

public int getFillColor() {
    return mFillColor;
}

public void setFillColor(int fillColor) {
    if (fillColor == mFillColor) {
        return;
    }

    mFillColor = fillColor;
    mFillPaint.setColor(fillColor);
    invalidate();
}

public void setFillColorResource(@ColorRes int fillColorRes) {
    setFillColor(ContextCompat.getColor(getContext(), fillColorRes));
}

public int getBorderWidth() {
    return mBorderWidth;
}

public void setBorderWidth(int borderWidth) {
    if (borderWidth == mBorderWidth) {
        return;
    }

    mBorderWidth = borderWidth;
    setup();
}

public boolean isBorderOverlay() {
    return mBorderOverlay;
}

public void setBorderOverlay(boolean borderOverlay) {
    if (borderOverlay == mBorderOverlay) {
        return;
    }

    mBorderOverlay = borderOverlay;
    setup();
}

@Override
public void setImageBitmap(Bitmap bm) {
    super.setImageBitmap(bm);
    mBitmap = bm;
    setup();
}

@Override
public void setImageDrawable(Drawable drawable) {
    super.setImageDrawable(drawable);
    mBitmap = getBitmapFromDrawable(drawable);
    setup();
}

@Override
public void setImageResource(@DrawableRes int resId) {
    super.setImageResource(resId);
    mBitmap = getBitmapFromDrawable(getDrawable());
    setup();
}

@Override
public void setImageURI(Uri uri) {
    super.setImageURI(uri);
    mBitmap = uri != null ? getBitmapFromDrawable(getDrawable()) : null;
    setup();
}

@Override
public void setColorFilter(ColorFilter cf) {
    if (cf == mColorFilter) {
        return;
    }

    mColorFilter = cf;
    mBitmapPaint.setColorFilter(mColorFilter);
    invalidate();
}

private Bitmap getBitmapFromDrawable(Drawable drawable) {
    if (drawable == null) {
        return null;
    }

    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    }

    try {
        Bitmap bitmap;

        if (drawable instanceof ColorDrawable) {
            bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
        } else {
            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
        }

        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return bitmap;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

private void setup() {
    if (!mReady) {
        mSetupPending = true;
        return;
    }

    if (getWidth() == 0 && getHeight() == 0) {
        return;
    }

    if (mBitmap == null) {
        invalidate();
        return;
    }

    mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

    mBitmapPaint.setAntiAlias(true);
    mBitmapPaint.setShader(mBitmapShader);

    mBorderPaint.setStyle(Paint.Style.STROKE);
    mBorderPaint.setAntiAlias(true);
    mBorderPaint.setColor(mBorderColor);
    mBorderPaint.setStrokeWidth(mBorderWidth);

    mFillPaint.setStyle(Paint.Style.FILL);
    mFillPaint.setAntiAlias(true);
    mFillPaint.setColor(mFillColor);

    mBitmapHeight = mBitmap.getHeight();
    mBitmapWidth = mBitmap.getWidth();

    mBorderRect.set(0, 0, getWidth(), getHeight());
    mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);

    mDrawableRect.set(mBorderRect);
    if (!mBorderOverlay) {
        mDrawableRect.inset(mBorderWidth, mBorderWidth);
    }
    mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);

    updateShaderMatrix();
    invalidate();
}

private void updateShaderMatrix() {
    float scale;
    float dx = 0;
    float dy = 0;

    mShaderMatrix.set(null);

    if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
        scale = mDrawableRect.height() / (float) mBitmapHeight;
        dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
    } else {
        scale = mDrawableRect.width() / (float) mBitmapWidth;
        dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
    }

    mShaderMatrix.setScale(scale, scale);
    mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);

    mBitmapShader.setLocalMatrix(mShaderMatrix);
}

}`

View的子類需要重寫onDraw方法,並根據自身的特點來編寫繪製View內容的邏輯。

詳細內容可轉連接:https://mp.weixin.qq.com/s/DL9XFo8Zd4osHvAy7efgog

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章