用Canvas绘制一个数字键盘

news2025/7/13 0:32:54

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

今天的内容大概如下:

1、效果展示

2、快速使用及属性介绍

3、具体代码实现

4、源文件地址及总结

一、效果展示

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

静态效果展示:

动态效果展示,录了一个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个英文字母键盘绘制,基本上思路是一致的,大家可以在此基础上进行拓展,本文就先到这里吧,整体略有瑕疵,忘包含。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/8663.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenGL 灰度图

目录 一.OpenGL 灰度图 1.IOS Object-C 版本1.Windows OpenGL ES 版本2.Windows OpenGL 版本 二.OpenGL 灰度图 GLSL Shader三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 &…

振弦采集模块AABB 通讯协议

振弦采集模块AABB 通讯协议 AABB 通讯协议是一种非标准自定义协议&#xff0c; 相较于 MODBUS 通讯协议&#xff0c;结构更简单&#xff0c;指令生成方法更容易&#xff0c;便于进行快速测试。 AABB 通讯协议支持单寄存器读写两种指令。 &#xff08; 1&#xff09; 读取单个寄…

《Care Bears 爱心熊》人物化身来到 The Sandbox 元宇宙!

无论你想要快乐、和谐、幸运还是温暖&#xff0c;都会有一个适合你的爱心熊人物化身&#xff01;&#x1f43b; 想成为一只爱心熊吗&#xff1f; 《爱心熊》作品集是由 3060 个独特的 The Sandbox 人物化身组成的作品集&#xff0c;可在欢快而多彩的元宇宙世界中玩耍。每个人物…

【Linux】软件包管理器yum和编辑器vim(内附动图)

大家好我是沐曦希&#x1f495; 文章目录1.Linux 软件包管理器 yum1.1 什么是软件包1.2 第一个软件rzsz2.Linux编辑器-vim使用2.1 vim的基本概念2.2 vim的基本操作2.3 命令模式2.3.1 光标定位2.3.2 文本复制2.4 插入模式2.5 底行模式2.5.1 调用和取消行号2.5.2 底行&#xff01…

Node.js | 详解 Cookie-Session登录验证 的工作原理

&#x1f9d1;‍&#x1f4bc; 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; &#x1f5a5;️ 本系列专栏&#xff1a;Node.js从入门到精通 &#x1f449; 你的一键三连是我更新的最大动力❤️&#xff01; &#x1f4e2; 欢迎私信博主加入前端交流群&#x1f339; …

Maven版本3.6.1环境配置安装

官网下载安装包配置maven环境变量配置本地仓库以及阿里云镜像官网下载安装包 下载maven安装包官网地址&#xff0c;解压即可使用&#xff0c;推荐下载apache-maven-3.6.1-bin.zip 配置maven环境变量 找到此电脑右键-->点击属性-->选择高级系统设置-->点击环境变量--&g…

EPICS记录参考--计算输出记录(calcout)

计算输出或"Calcout"记录类似于Calc记录&#xff0c;其增加了能够输出的特性(一个"output link"和一个"output event")&#xff0c;根据计算结果条件地执行它们。这种特性允许在一个EPICS数据库内实现了条件分支(例如&#xff1a;只在Record_B有…

BERT预训练模型学习笔记

1.Transforme 1.1 要做一件什么事 基本组成依旧是机器翻译模型中常见的Seq2Seq网络输入输出都很直观&#xff0c;其核心架构就是中间的网络设计了MxN&#xff0c;输入M&#xff0c;输出N 1.2 传统的RNN网络有什么问题 传统RNN是一个时序模型&#xff0c;下一个RNN的输入依靠…

野火FPGA入门(4):时序逻辑电路

文章目录第11讲&#xff1a;寄存器第12讲&#xff1a;阻塞赋值与非阻塞赋值第13讲&#xff1a;计数器第14讲&#xff1a;分频器&#xff1a;偶分频第15讲&#xff1a;分频器&#xff1a;奇分频第16讲&#xff1a;按键消抖组合逻辑存在竞争冒险 第11讲&#xff1a;寄存器 寄存…

【Debug】关于 nginx 上传文件时出现 413 及 500 错误码解决方法

先简单介绍一下 Nginx…   Nginx 作为一个高性能的 HTTP 和 反向代理 web 服务器具有占用内存少, 并发能力强等特点,可以说 Nginx 专为性能和效率而生, 如 tomcat 的并发量大约在 100 多, 而 Nginx 的并发量可以达到 5 万之多;   Nginx 的主要作用还是反向代理, 实现负载均衡…

什么是扩散模型(Diffusion Model)?

扩散模型是什么&#xff1f;如何工作以及他如何解决实际的问题 在计算机视觉中&#xff0c;生成模型是一类能够生成合成图像的模型&#xff08;文本生成图像【DALL2、Stable Diffusion】、图像生成图像【Diffusion-GAN】&#xff09;。例如&#xff0c;一个被训练来生成人脸的…

2023年天津市大学软件学院高职升本科联合招生专业考试大纲

天津市大学软件学院 2023年“高职升本科”联合招生专业考试大纲一、考试性质 天津市大学软件学院“高职升本科”联合招生专业考试是由合格的高职高专毕业生参加的选拔性考试。高等院校根据考生的成绩&#xff0c;按照已确定的招生计划&#xff0c;德、智、体全面衡量&#xff0…

MATLAB if...else...end 语句

在MATLAB的 if...else...end 语句中&#xff0c;if 语句后面可以跟一个可选择的 else 语句&#xff0c;当执行的表达式为假的时候&#xff0c;执行 else 语句。 if...else...end 语句语法&#xff1a; MATLAB 中一个 if ... else 语句的语法示例&#xff1a; if <expressio…

【python】一篇玩转正则表达式

目录 前言 正则表达式 行定位符 1.^ 2.$ 元字符 常见的元字符 限定符 常用的限定符 字符类 排除字符 选择字符 转义字符 &#xff08;&#xff09; python使用正则表达式 匹配字符串 match() search() findall() sub() 替换敏感字符 split() 前言 正则表…

柯桥托业TOEIC考试和PETS哪个含金量高?

说到对职场有益的证书&#xff0c;无外乎托业和BEC证书。但还有一种面向社会人士的考试&#xff0c;也有很多小伙伴很感兴趣。那就是PETS考试。 很多小伙伴也很好奇托业和PETS的区别&#xff0c;今天来给大家科普下喽。 TOEIC-托业考试 托业考试由美国教育考试服务中心(ETS)开…

LaTeX使用(公式,表格,图片,中文字符)

是一种基于ΤΕΧ的排版系统&#xff0c;由美国计算机学家莱斯利兰伯特&#xff08;Leslie Lamport&#xff09;在20世纪80年代初期开发&#xff0c;利用这种格式&#xff0c;即使使用者没有排版和程序设计的知识也可以充分发挥由TeX所提供的强大功能&#xff0c;能在几天、甚至…

HTML小游戏8 —— 小霸王游戏机网页版(附完整源码)

&#x1f482; 网站推荐:【神级源码资源网】【摸鱼小游戏】&#x1f91f; 风趣幽默的前端学习课程&#xff1a;&#x1f449;28个案例趣学前端&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且实用的计算机相关知…

RabbitMQ-04 Hello,World

首先我们还是先看一下Rabbitmq的工作原理图 从图上我们可以看到&#xff0c;无论是生产者还是消费者我们都需要进行connection并且获取相应的channel&#xff0c;所以为了方便&#xff0c;建议把这部分操作抽取成一个工具类RabbitMqUtils。 工具类RabbitMqUtils public class…

【C++笔试强训】第二十五天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 &#x1f4a6;&a…

S7-1200通过MODBUS转PROFINET网关控制英威腾GD200A变频器的具体方法示例

S7-1200通过MODBUS转PROFINET网关控制英威腾GD200A变频器的具体方法示例 需要的设备: 西门子S7-1200PLC一台 MODBUS转PROFINET网关一台 英威腾GD200A变频器一台 具体配置方法: 1、 如下图所示,打开博途软件,新建项目并添加网关的gsd文件; 2、 如下图所示,建立profinet连…