用Canvas绘制一个数字键盘

举报
程序员一鸣 发表于 2023/09/26 18:42:55 2023/09/26
【摘要】 用Canvas轻轻松松搞一个数字键盘,本身没什么难度,这种效果实现的方式也是多种多样,这篇只是其中的一种,要说本篇有什么特别之处,可能就是纯绘制,没有用到其它的任何资源,一个类就搞定了

Hello啊老铁们,这篇文章还是阐述自定义View相关的内容,用Canvas轻轻松松搞一个数字键盘,本身没什么难度,这种效果实现的方式也是多种多样,这篇只是其中的一种,要说本篇有什么特别之处,可能就是纯绘制,没有用到其它的任何资源,一个类就搞定了,文中不足之处,各位老铁多包含,多指正。

今天的内容大概如下:

1、效果展示

2、快速使用及属性介绍

3、具体代码实现

4、源文件地址及总结

一、效果展示

很常见的数字键盘,背景,颜色,文字大小,点击的事件等等,均已配置好,大家可以看第2项中相关介绍。

静态效果展示:

9216736b55d8565b62326db034558de9.png



动态效果展示,录了一个gif,大家可以看下具体的触摸效果。

47103cbd7eabacd5f86df02ef6c487ee.gif


二、快速使用及属性介绍

鉴于本身就一个类,不值当去打一个远程的Maven,大家用的话可以直接下载,把文件复制到项目里即可,复制到项目中,就可以按照下面的步骤去使用。

引用

1、xml中引用,可以根据需要,设置宽高及相关属性

<KeyboardView
  android:id="@+id/key_board_view"
  android:layout_width="match_parent"
  android:layout_height="wrap_content" />

2、代码直接创建,然后追加到相关视图里即可

val keyboardView=KeyboardView(this)

方法及属性介绍

单个点击的触发监听:

keyboardView.setOnSingleClickListener {
            //键盘点击的数字

}

获取最终的字符串点击监听,其实就是把你点击的数字拼接起来,一起输出,特别在密码使用的时候,省的你再自己拼接了,配合number_size属性和setNumberSize方法一起使用,默认是6个长度,可以根据需求,动态设置。

keyboardView.setOnNumClickListener {
        //获取最终的点击数字字符串,如:123456,通过number_size属性或setNumberSize方法,设置最长字符
    }

其它方法

方法

参数

概述

hintLetter

无参

隐藏字母

setBackGroundColor

int类型的颜色值

设置整体背景色

setRectBackGroundColor

int类型的颜色值

设置数字格子背景色

setTextColor

int类型的颜色值

设置文字颜色

setTextSize

Float

设置数字大小

setNumberSize

int类型

设置按下的数字总长度

setRectHeight

Float

设置数字键盘每格高度

setSpacing

Float

设置数字键盘每格间隔距离

属性介绍

属性

类型

概述

background_color

color

背景颜色

rect_background_color

color

数字格子背景色

down_background_color

color

手指按下的背景颜色

text_color

color

文字颜色

text_size

dimension

文字大小

letter_text_size

dimension

字母的文字大小

rect_height

dimension

数字格子高度

rect_spacing

dimension

格子间距

is_rect_letter

boolean

是否显示字母

number_size

integer

按下的数字总长度字符

三、具体代码实现

代码实现上其实也没有什么难的,主要就是用到了自定义View中的onDraw方法,简单的初始化,设置画笔,默认属性就不一一介绍了,直接讲述主要的绘制部分,我的实现思路如下,第一步,绘制方格,按照UI效果图,应该是12个方格,简图如下,需要注意的是,第10个是空的,也就是再绘制的时候,需要进行跳过,最后一个是一个删除的按钮,绘制的时候也需要跳过,直接绘制删除按钮即可。

1、关于方格的绘制

方格的宽度计算很简单,(手机的宽度-方格间距*4)/3即可,绘制方格,直接调用canvas的drawRoundRect方法,单纯的和数字一起绘制,直接遍历12个数即可,记住9的位置跳过,11的位置,绘制删除按钮。

mRectWidth = (width - mSpacing * 4) / 3
mPaint!!.strokeWidth = 10f
for (i in 0..11) {
            //设置方格
            val rect = RectF()
            val iTemp = i / 3
            val rectTop = mHeight * iTemp + mSpacing * (iTemp + 1f)
            rect.top = rectTop
            rect.bottom = rect.top + mHeight
            var leftSpacing = (mSpacing * (i % 3f))
            leftSpacing += mSpacing
            rect.left = mRectWidth!! * (i % 3f) + leftSpacing
            rect.right = rect.left + mRectWidth!!
            //9的位置是空的,跳过不绘制
            if (i == 9) {
                continue
            }
            //11的位置,是删除按钮,直接绘制删除按钮
            if (i == 11) {
                drawDelete(canvas, rect.right, rect.top)
                continue
            }
            mPaint!!.textSize = mTextSize
            mPaint!!.style = Paint.Style.FILL
            //按下的索引 和 方格的 索引一致,改变背景颜色
            if (mDownPosition == (i + 1)) {
                mPaint!!.color = mDownBackGroundColor
            } else {
                mPaint!!.color = mRectBackGroundColor
            }
            //绘制方格
            canvas!!.drawRoundRect(rect, 10f, 10f, mPaint!!)
}

2、关于数字的绘制

没有字母显示的情况下,数字要绘制到中间的位置,有字母的情况下,数字应该往上偏移,让整体进行居中,通过方格的宽高和自身文字内容的宽高来计算显示的位置。

//绘制数字
            mPaint!!.color = mTextColor
            var keyWord = "${i + 1}"
            //索引等于 10 从新赋值为 0
            if (i == 10) {
                keyWord = "0"
            }
            val rectWord = Rect()
            mPaint!!.getTextBounds(keyWord, 0, keyWord.length, rectWord)
            val wWord = rectWord.width()
            val htWord = rectWord.height()
            var yWord = rect.bottom - mHeight / 2 + (htWord / 2)
            //上移
            if (i != 0 && i != 10 && mIsShowLetter) {
                yWord -= htWord / 3
            }
            canvas.drawText(
                keyWord,
                rect.right - mRectWidth!! / 2 - (wWord / 2),
                yWord,
                mPaint!!
            )

3、关于字母的绘制

因为字母是和数字一起绘制的,所以需要对应的字母则向下偏移,否则不会达到整体居中的效果,具体的绘制如下,和数字的绘制类似,拿到方格的宽高,以及字母的宽高,进行计算横向和纵向位置。

//绘制字母
            if ((i in 1..8) && mIsShowLetter) {
                mPaint!!.textSize = mLetterTextSize
                val s = mWordArray[i - 1]
                val rectW = Rect()
                mPaint!!.getTextBounds(s, 0, s.length, rectW)
                val w = rectW.width()
                val h = rectW.height()
                canvas.drawText(
                    s,
                    rect.right - mRectWidth!! / 2 - (w / 2),
                    rect.bottom - mHeight / 2 + h * 2,
                    mPaint!!
                )
            }

4、关于删除按钮的绘制

删除按钮是纯线条的绘制,没有使用图片资源,不过大家可以使用图片资源,因为图片资源还是比较的靠谱。

/**
     * AUTHOR:AbnerMing
     * INTRODUCE:绘制删除按键,直接canvas自绘,不使用图片
     */
    private fun drawDelete(canvas: Canvas?, right: Float, top: Float) {
        val rWidth = 15
        val lineWidth = 35
        val x = right - mRectWidth!! / 2 - (rWidth + lineWidth) / 4
        val y = top + mHeight / 2
        val path = Path()
        path.moveTo(x - rWidth, y)
        path.lineTo(x, y - rWidth)
        path.lineTo(x + lineWidth, y - rWidth)
        path.lineTo(x + lineWidth, y + rWidth)
        path.lineTo(x, y + rWidth)
        path.lineTo(x - rWidth, y)
        path.close()
        mPaint!!.strokeWidth = 2f
        mPaint!!.style = Paint.Style.STROKE
        mPaint!!.color = mTextColor
        canvas!!.drawPath(path, mPaint!!)

        //绘制小×号
        mPaint!!.style = Paint.Style.FILL
        mPaint!!.textSize = 30f
        val content = "×"
        val rectWord = Rect()
        mPaint!!.getTextBounds(content, 0, content.length, rectWord)
        val wWord = rectWord.width()
        val htWord = rectWord.height()
        canvas.drawText(
            content,
            right - mRectWidth!! / 2 - wWord / 2 + 3,
            y + htWord / 3 * 2 + 2,
            mPaint!!
        )

    }

5、按下效果的处理

按下的效果处理,重写onTouchEvent方法,然后在down事件里通过,手指触摸的XY坐标,判断当前触摸的是那个方格,记录下索引,并使用invalidate进行刷新View,在onDraw里进行改变画笔的颜色即可。

根据XY坐标,返回触摸的位置

/**
     * AUTHOR:AbnerMing
     * INTRODUCE:返回触摸的位置
     */
    private fun getTouch(upX: Float, upY: Float): Int {
        var position = -2
        for (i in 0..11) {
            val iTemp = i / 3
            val rectTop = mHeight * iTemp + mSpacing * (iTemp + 1f)
            val top = rectTop
            val bottom = top + mHeight
            var leftSpacing = (mSpacing * (i % 3f))
            leftSpacing += 10f
            val left = mRectWidth!! * (i % 3f) + leftSpacing
            val right = left + mRectWidth!!
            if (upX > left && upX < right && upY > top && upY < bottom) {
                position = i + 1
                //位置11默认为 数字  0
                if (position == 11) {
                    position = 0
                }
                //位置12  数字为 -1 意为删除
                if (position == 12) {
                    position = -1
                }
            }
        }
        return position
    }

在onDraw里进行改变画笔的颜色。

 //按下的索引 和 方格的 索引一致,改变背景颜色
            if (mDownPosition == (i + 1)) {
                mPaint!!.color = mDownBackGroundColor
            } else {
                mPaint!!.color = mRectBackGroundColor
            }

6、wrap_content处理

在使用当前控件的时候,需要处理wrap_content的属性,否则效果就会和match_parent一样了,具体的处理如下,重写onMeasure方法,获取高度的模式后进行单独的设置。

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val heightSpecMode = MeasureSpec.getMode(heightMeasureSpec)
        val widthSpecSize = MeasureSpec.getSize(widthMeasureSpec)
        if (heightSpecMode == MeasureSpec.AT_MOST) {
            //当高度为 wrap_content 时 设置一个合适的高度
            setMeasuredDimension(widthSpecSize, (mHeight * 4 + mSpacing * 5 + 10).toInt())
        }
    }

四、源文件地址及总结

源文件地址:

https://github.com/AbnerMing888/KeyboardView

源文件不是一个项目,是一个单纯的文件,大家直接复制到项目中使用即可,对于26个英文字母键盘绘制,基本上思路是一致的,大家可以在此基础上进行拓展,本文就先到这里吧,整体略有瑕疵,忘包含。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。