Android自定义View—直尺

一、简介

Android自定义View---量角器 - 掘金 (juejin.cn)

Android自定义View---等腰直角三角尺 - 掘金 (juejin.cn)

上篇文章实现了量角器,本篇文章我们以自定义View的形式来实现------直尺,老规矩我们先来看效果图:

真机设备不在身边,所以这里就用模拟器来代替了,请见谅,另外为了效果明显,这里是将View旋转,以便尺子的长度能够更长一些

二、步骤

2.1 画圆角矩形

首先我们需要画一个圆角矩形作为尺子的背景

css 复制代码
//这里的width和height分别为View视图的宽高
canvas.drawRoundRect(0f,0f,width.toFloat(),height.toFloat(),5f,5f,bgPaint)

2.2 画刻度线

  • 确定尺子的长度

toValue 指的是尺子的长度,这里指的是最大刻度

interval 指的两条刻度之间的宽度

intervalsBetweenValues 指的是数值之间的间隔数,尺子的刻度值比如0和1之间相差了10个1m

下面的代码用于尺子最大刻度取10的整数倍(1mm X 10 = 1cm),这样做的效果是为了使尺子更好看点,因为是从0开始,以整数cm结束

ini 复制代码
toValue = width/(interval * intervalsBetweenValues)
  • 刻度偏移

此时虽然最小刻度跟最大刻度画出来了,可是刻度与View的左右两边的间距不一致,导致尺子不太美观,所以我们需要使左右两边的间距一致

ini 复制代码
offset = (width - (toValue*interval*intervalsBetweenValues))/2f
  • 画刻度线

先判断是不是5的倍数,不是5的倍数则画短线,是的话判断是不是10的倍数,是的话画长线,不是则画中线,其中minLength则是自己定义的长度基数

arduino 复制代码
//偏移
var width = offset
while(true){
    //=====================画刻度============
    if(position>toValue/valuesInterval*intervalsBetweenValues) break
    //这里需要额外加上以文本"10000"的长度作为偏移量,防止值文本很长的时候,文本还没完全退出边界就消失了
    if(width>getWidth()+paint.measureText("10000")) break
    if(position%(intervalsBetweenValues/2)==0){
        if(position%intervalsBetweenValues==0){
               //===============长线============
               canvas.drawLine(width, 0f, width, minLength*2f, paint)
        
        }else{
            //============中线==============
            canvas.drawLine(width, 0f, width, minLength*1.5f, paint)
        }
    }else{
        //============短线===========
        canvas.drawLine(width, 0f, width, minLength, paint)
    }
    width+=interval
    position++
}

2.3 画整数值

每隔10个单位画一个整数,简单来说就是在长线的下边画整数,这里的width指的是循环递增的宽度,不是View的宽度

css 复制代码
val valueString = (position / intervalsBetweenValues * valuesInterval).toString()
canvas.drawText(
    valueString,
    width - paint.measureText(valueString) / 2,
    minLength * 2.5f + textHeight / 2,
    paint
)

至此大功告成了!!

三、完整代码

view

kotlin 复制代码
class RulerView @JvmOverloads constructor(
    private val context: Context?,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) :
    View(context, attrs, defStyleAttr) {
    private var defaultInt = 0

    //间隔,即两条刻度间的距离
    private var interval = defaultInt

    //结束值
    private var toValue = defaultInt

    //每两个值之间的间隔数,也指多少个最小单位,比如0cm到1cm有10个最小单位1mm
    private var intervalsBetweenValues = defaultInt

    //相邻两个值的跳跃间隔,如上面第一张图的10000到11000,中间的跳跃值就是1000\
    private var valuesInterval = defaultInt

    //值的文本大小
    private var valuesTextSize = defaultInt

    //值的文本颜色
    private var valuesTextColor = defaultInt

    //刻度的宽度
    private var linesWidth = defaultInt

    //刻度的颜色
    private var linesColor = defaultInt

    private val paint = Paint()
    private val bgPaint = Paint()
    private var textHeight = defaultInt
    private var offset = 0f //偏移

    init {
        val array = context!!.obtainStyledAttributes(attrs, R.styleable.Ruler)
        interval = array.getDimensionPixelSize(
            R.styleable.Ruler_interval,
            dp2px(intervalsBetweenValues.toFloat()).toInt()
        )
        intervalsBetweenValues =
            array.getInt(R.styleable.Ruler_intervalsBetweenValues, intervalsBetweenValues)
        valuesInterval = array.getInt(R.styleable.Ruler_valuesInterval, 1)
        valuesTextSize =
            array.getDimensionPixelSize(R.styleable.Ruler_valuesTextSize, sp2px(16f).toInt())
        valuesTextColor = array.getColor(R.styleable.Ruler_valuesTextColor, Color.BLACK)
        linesWidth = array.getDimensionPixelSize(R.styleable.Ruler_linesWidth, dp2px(1f).toInt())
        linesColor = array.getColor(R.styleable.Ruler_linesColor, Color.BLACK)

        array.recycle()

        paint.textSize = valuesTextSize.toFloat()
        paint.color = linesColor
        bgPaint.color = Color.WHITE
        //文本高度
        val fm = paint.fontMetrics
        textHeight = (fm.bottom - fm.top).toInt()
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        //根据宽度去计算toValue
        val minLength = height / 8f

        toValue = width / (interval * intervalsBetweenValues)
        if (canvas != null) {
            //画刻度
            paint.color = linesColor
            paint.strokeWidth = linesWidth.toFloat()
            offset =
                (width - (toValue * interval * intervalsBetweenValues)) / 2f
            var position: Int = defaultInt
            canvas.drawRoundRect(0f, 0f, width.toFloat(), height.toFloat(), 5f, 5f, bgPaint)
            var width = offset
            while (true) {
                //=====================画刻度============
                if (position > toValue / valuesInterval * intervalsBetweenValues) break
                //这里需要额外加上以文本"10000"的长度作为偏移量,防止值文本很长的时候,文本还没完全退出边界就消失了
                if (width > getWidth() + paint.measureText("10000")) break
                if (position % (intervalsBetweenValues / 2) == 0) {
                    if (position % intervalsBetweenValues == 0) {

                        //===============长线============
                        canvas.drawLine(width, 0f, width, minLength * 2f, paint)
                        paint.color = valuesTextColor
                        val valueString =
                            (position / intervalsBetweenValues * valuesInterval).toString()
                        canvas.drawText(
                            valueString,
                            width - paint.measureText(valueString) / 2,
                            minLength * 2.5f + textHeight / 2,
                            paint
                        )
                        paint.color = linesColor
                    } else {
                        //============中线==============
                        canvas.drawLine(width, 0f, width, minLength*1.5f, paint)
                    }
                } else {
                    //============短线===========
                    canvas.drawLine(width, 0f, width, minLength, paint)
                }
                width += interval
                position++
            }
        }

    }

    private fun sp2px(sp: Float): Float {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, resources.displayMetrics)
    }

    private fun dp2px(dp: Float): Float {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics)
    }

}

attrs.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="Ruler">
        <attr name="interval" format="dimension"/>
        <attr name="intervalsBetweenValues" format="integer"/>
        <attr name="valuesInterval" format="integer"/>
        <attr name="valuesTextSize" format="dimension"/>
        <attr name="valuesTextColor" format="color"/>
        <attr name="linesWidth" format="dimension"/>
        <attr name="linesColor" format="color"/>
    </declare-styleable>
</resources>

layout

ini 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:custom="http://schemas.android.com/apk/res-auto">

    <com.example.studytools.view.RulerView
        android:layout_marginStart="10dp"
        android:id="@+id/ruler_view"
        android:layout_width="600dp"
        android:layout_height="50dp"
        custom:interval="5dp"
        custom:intervalsBetweenValues="10"
        custom:linesColor="@color/black"
        custom:linesWidth="1dp"
        custom:valuesInterval="1"
        custom:valuesTextSize="10sp" />

</RelativeLayout>

参考

Android自定义View第三弹(反人类尺子) - 掘金 (juejin.cn)

相关推荐
深海呐3 小时前
Android AlertDialog圆角背景不生效的问题
android
ljl_jiaLiang3 小时前
android10 系统定制:增加应用使用数据埋点,应用使用时长统计
android·系统定制
花花鱼3 小时前
android 删除系统原有的debug.keystore,系统运行的时候,重新生成新的debug.keystore,来完成App的运行。
android
落落落sss4 小时前
sharding-jdbc分库分表
android·java·开发语言·数据库·servlet·oracle
消失的旧时光-19436 小时前
kotlin的密封类
android·开发语言·kotlin
服装学院的IT男8 小时前
【Android 13源码分析】WindowContainer窗口层级-4-Layer树
android
CCTV果冻爽9 小时前
Android 源码集成可卸载 APP
android
码农明明9 小时前
Android源码分析:从源头分析View事件的传递
android·操作系统·源码阅读
秋月霜风10 小时前
mariadb主从配置步骤
android·adb·mariadb
Python私教10 小时前
Python ORM 框架 SQLModel 快速入门教程
android·java·python