一、简介
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>