Android自定义控件入门 06 计算等边三角形

143 阅读2分钟

1.画笔模式

    /**  
    * Set the paint's style, used for controlling how primitives'  
    * geometries are interpreted (except for drawBitmap, which always assumes  
    * Fill).  
    *  
    * @param style The new style to set in the paint  
    */  
    public void setStyle(Style style) {  
        nSetStyle(mNativePaint, style.nativeInt);  
    }
  • Paint.Style.STROKE 描边
  • Paint.Style.FILL 填充
  • Paint.Style.FILL_AND_STROKE 同时填充和描边

2.线冒样式

    /**  
    * Set the paint's Cap.  
    *  
    * @param cap set the paint's line cap style, used whenever the paint's  
    * style is Stroke or StrokeAndFill.  
    */  
    public void setStrokeCap(Cap cap) {  
        nSetStrokeCap(mNativePaint, cap.nativeInt);  
    }
  • Paint.Cap.BUTT:笔画以小路为终点,不向外投射。
  • Paint.Cap.SQUARE:笔画突出为一个正方形,中心位于路径的末端。
  • Paint.Cap.ROUND: 笔画以半圆的形式伸出,中心位于路径的末端

3.打造炫酷进度条

4314397-7cba0ce52a925422.webp

class CircleView @JvmOverloads constructor(  
    context: Context,  
    attr: AttributeSet? = null,  
    defStyle: Int = 0  
) :  
    View(context, attr, defStyle) {  

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)  
        val isWrap = (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST ||MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST)  
        val size = if (isWrap) {  
            40  
        } else {  
            val width = MeasureSpec.getSize(widthMeasureSpec)  
            val height = MeasureSpec.getSize(heightMeasureSpec)  
            min(width, height)  
        }  
        setMeasuredDimension(size, size)  
    }  

    private val bluePaint by lazy {  
        Paint().apply {  
            color = Color.BLUE  
            isAntiAlias = true  
            strokeWidth = borderWith  
            //设置画笔仅描边,不填充  
            style = Paint.Style.STROKE  
        }  
    }  

    private val redPaint by lazy {  
        Paint().apply {  
            color = Color.RED  
            isAntiAlias = true  
            strokeWidth = borderWith  
            //设置画笔仅描边,不填充  
            style = Paint.Style.STROKE  
            //两端为圆形  
            strokeCap = Paint.Cap.ROUND  
        }  
    }  

    private val textPaint by lazy {  
        Paint().apply {  
            color = Color.BLUE  
            isAntiAlias = true  
            textSize = borderWith  
        }  
    }  

    private val borderWith = 20F  
    private val circleRectF by lazy {  
        val leftAndTop = borderWith / 2  
        val rightAndBottom = width - borderWith / 2  
        RectF(leftAndTop, leftAndTop, rightAndBottom, rightAndBottom)  
    }  

    private val textRect by lazy {  Rect()  }  
    
    private var currentProgress = 0.59F  

    override fun draw(canvas: Canvas) {  
        super.draw(canvas)  
        val circleRange=360F  
        canvas.drawArc(circleRectF, 0F, circleRange, false, bluePaint)  
        val content = if (currentProgress in 0F..1F) {  
            "${(currentProgress * 100).toInt()}"  
        } else {  
            "0"  
        } + "%"  
        textPaint.getTextBounds(content, 0, content.length, textRect)  
        val x = (width / 2 - textRect.width() / 2).toFloat()  
        val dy = with(textPaint.fontMetricsInt) {  
            (bottom - top) / 2 - bottom  
        }  
        val baseLine = (height / 2 + dy).toFloat()  
        canvas.drawText(content, x, baseLine, textPaint)  
        canvas.drawArc(circleRectF, 0F, circleRange*currentProgress, false, redPaint)  
    }  

    fun refreshProgress(currentProgress:Float){  
        this.currentProgress=currentProgress  
        invalidate()  
    }  
}

4.仿58同城数据加载效果(此处为等边三角形)

4314397-a58a7012a21986e3.webp 此处是真正的等边三角形三条边都相等

绝非是常见的想当然的网上流传甚广的这种情况

private val path by lazy {  
    with(Path()) {  
        moveTo((width / 2).toFloat(), 0F)  
        lineTo(0F, height.toFloat())  
        lineTo(width.toFloat(), height.toFloat())  
        close()  
        this  
    }  
}

1709914437116.png

class RandomView @JvmOverloads constructor(  
    context: Context,  
    attr: AttributeSet? = null,  
    defStyle: Int = 0  
) :  
    View(context, attr, defStyle) {  

    enum class RefreshType {  
        Circle, Rect, Triangle  
    }  

    private var showType: RefreshType = RefreshType.Circle  

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)  
        val isWrap = (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) || (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST)  
        val size = if (isWrap) {  
            300  
        } else {  
            min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)) 
        }  
        setMeasuredDimension(size, size)  
    }  
    private val colorPaint by lazy { Paint() }  

    private val rect by lazy { Rect(0, 0, width, height) }  

    override fun onDraw(canvas: Canvas) {  
        super.onDraw(canvas)  
        when (showType) {  
            RefreshType.Circle -> {  
                colorPaint.color=Color.BLUE  
                //画圆  
                val center = (width / 2).toFloat()  
                canvas.drawCircle(center, center, center, colorPaint)  
            }  
            //画正方形  
            RefreshType.Rect -> {  
                colorPaint.color=Color.RED  
                canvas.drawRect(rect, colorPaint)  
            }  
            //画三角形  
            RefreshType.Triangle -> {  
                colorPaint.color=Color.GREEN  
                canvas.drawPath(path, colorPaint)  
            }  
        }  
    }  

    /**  
    * 根据直角三角形:a^2+b^=c^2(斜边)  
    *  
    * 等边三角形底边做垂直线可得到:  
    *  
    * 两个相等的30°,60°的RT(right triangle)直角三角形  
    *  
    * 斜边为等边三角形的边  
    * 底边为(等边三角形的边/2)  
    * 直角边为等边三角形的垂直线  
    * */  
    private val path by lazy {  
        with(Path()) {  
            moveTo((width / 2).toFloat(), 0F)  
            //斜边的平方  
            val hypotenuse= width.toDouble().pow(2)  
            //底边的平方  
            val bottom=(width/2).toDouble().pow(2)  
            //直角边开平方  
            val vertical=sqrt(hypotenuse-bottom).toFloat()  
            lineTo(0F, vertical)  
            lineTo(width.toFloat(),vertical)  
            close()  
            this  
        }  
    }  


    /**  
    * 内部切换更好  
    * */  
    fun refreshShowType() {  
        showType=when(showType){  
            RefreshType.Circle->RefreshType.Rect  
            RefreshType.Rect->RefreshType.Triangle  
            RefreshType.Triangle->RefreshType.Circle  
        }  
        invalidate()  
    }  
}