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)

相关推荐
Estar.Lee1 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯2 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey3 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!5 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟6 小时前
Android音频采集
android·音视频
小白也想学C7 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程7 小时前
初级数据结构——树
android·java·数据结构
闲暇部落9 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX11 小时前
Android 分区相关介绍
android
大白要努力!12 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle