android自定义数字键盘

2023-11-03

前言

最近需要做一个自定义的数字键盘,开始使用了下系统自带的KeyBoardView,但是发现UI效果不是很理想,最后还是自己画一个自定义键盘,这样在UI方面更加方便。先看效果图吧:

这里写图片描述

思路

1.键盘4行*3列的布局分为12个单元格,6条直线分隔单元格。根据单元格宽高确定数字位置。
2.点击效果根据用户按下和抬起动作做不同标记。

思路说的有点模糊,直接看代码吧,代码里的注释很详细:

实现

1:自定义view–CustomNumKeyView

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CustomNumKeyView extends View {
    /**
     * 列
     */
    private static final int TOTAL_COL = 3;
    /**
     * 行
     */
    private static final int TOTAL_ROW = 4;

    private Paint HuiseBgPaint, linePaint;
    private Paint mTextPaint;
    private int mViewWidth; // 键盘宽度
    private int mViewHight; // 键盘高度
    private float mCellWidth, mCellHight; // 单元格宽度、高度
    private Row rows[] = new Row[TOTAL_ROW];
    private Bitmap bitmap; // 删除按钮图片

    public interface CallBack {
        void clickNum(String num);// 回调点击的数字
        void deleteNum();// 回调删除
    }

    private CallBack mCallBack;// 回调

    public void setOnCallBack(CallBack callBack) {
        mCallBack = callBack;
    }

    public CustomNumKeyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);

    }

    public CustomNumKeyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);

    }

    public CustomNumKeyView(Context context) {
        super(context);
        init(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawLine(canvas);
        for (int i = 0; i < TOTAL_ROW; i++) {
            if (rows[i] != null)
                rows[i].drawCells(canvas);
        }
    }

    /**
     * 画6条直线
     * @param canvas
     */
    private void drawLine(Canvas canvas) {
        canvas.drawLine(0, 0, mViewWidth, 0, linePaint);
        canvas.drawLine(0, mCellHight, mViewWidth, mCellHight, linePaint);
        canvas.drawLine(0, mCellHight * 2, mViewWidth, mCellHight * 2, linePaint);
        canvas.drawLine(0, mCellHight * 3, mViewWidth, mCellHight * 3, linePaint);
        canvas.drawLine(mCellWidth, 0, mCellWidth, mViewHight, linePaint);
        canvas.drawLine(mCellWidth * 2, 0, mCellWidth * 2, mViewHight, linePaint);


    }

    /**
     * 初始化画笔
     * @param canvas
     */
    private void init(Context context) {
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCutTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        linePaint.setTextSize(1.0f);
        linePaint.setColor(0x90000000);

        HuiseBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        HuiseBgPaint.setStyle(Paint.Style.FILL);
        HuiseBgPaint.setColor(Color.parseColor("#e9e9e9"));
        
        initDate();
    }

    private void initDate() {
        fillDate();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mViewWidth = w;
        mViewHight = h;
        mCellWidth = mViewWidth / TOTAL_COL;
        mCellHight = mViewHight / TOTAL_ROW;
        mTextPaint.setTextSize(mCellHight / 3);

    }

    private Cell mClickCell = null;
    private float mDownX;
    private float mDownY;

    /*
     *
     * 触摸事件为了确定点击位置的数字
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownX = event.getX();
                mDownY = event.getY();
                int col = (int) (mDownX / mCellWidth);
                int row = (int) (mDownY / mCellHight);
                measureClickCell(col, row);
                break;
            case MotionEvent.ACTION_UP:
                if (mClickCell != null) {
	                // 在抬起后把状态置为默认
                    rows[mClickCell.i].cells[mClickCell.j].state = State.DEFAULT_NUM;
                    mClickCell = null;
                    invalidate();
                }
                break;
        }
        return true;
    }

    /**
     * 测量点击单元格
     * @param col 列
     * @param row 行
     */
    private void measureClickCell(int col, int row) {
        if (col >= TOTAL_COL || row >= TOTAL_ROW)
            return;
        if (rows[row] != null) {
            mClickCell = new Cell(rows[row].cells[col].num, rows[row].cells[col].state, rows[row].cells[col].i,
                    rows[row].cells[col].j);
            rows[row].cells[col].state = State.CLICK_NUM;
            if ("-5".equals(rows[row].cells[col].num)) {
                mCallBack.deleteNum();
            } else {
                mCallBack.clickNum(rows[row].cells[col].num);
            }
            invalidate();
        }
    }

    /**
     * 组 以一行为一组
     */
    private class Row {
        public int j;

        Row(int j) {
            this.j = j;
        }

        // 一行3个单元格
        public Cell[] cells = new Cell[TOTAL_COL];

        public void drawCells(Canvas canvas) {
            for (int i = 0; i < cells.length; i++) {
                if (cells[i] != null)
                    cells[i].drawSelf(canvas);
            }

        }
    }

    // 单元格
    private class Cell {
        public String num;
        public State state;
        /**
         * i = 行 j = 列
         */
        public int i;
        public int j;

        public Cell(String num, State state, int i, int j) {
            super();
            this.num = num;
            this.state = state;
            this.i = i;
            this.j = j;
        }

        // 绘制一个单元格 如果颜色需要自定义可以修改
        public void drawSelf(Canvas canvas) {
            switch (state) {
                case CLICK_NUM:
                    // 绘制点击效果灰色背景
                    canvas.drawRect((float) (mCellWidth * j), (float) (mCellHight * i),
                            (float) (mCellWidth * (j + 1)), (float) (mCellHight * (i + 1)), HuiseBgPaint);
                    break;
            }
            if ("-5".equals(num)) {
                // 绘制删除图片
                canvas.drawBitmap(bitmap, (float) (mCellWidth * 2.5 - bitmap.getWidth() / 2), (float) (mCellHight * 3.5 - bitmap.getHeight() / 2), HuiseBgPaint);
            } else {
                // 绘制数字
                canvas.drawText(num, (float) ((j + 0.5) * mCellWidth - mTextPaint.measureText(num) / 2),
                        (float) ((i + 0.5) * mCellHight + mTextPaint.measureText(num, 0, 1) / 2),
                        mTextPaint);
            }


        }
    }

    /**
     *  cell的state
     */
    private enum State {
        DEFAULT_NUM, CLICK_NUM;
    }

    private List<String> numKeys = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "0");

    /**
     * 填充数字
     */
    private void fillDate() {
        int postion = 0;
        for (int i = 0; i < TOTAL_ROW; i++) {
            rows[i] = new Row(i);
            for (int j = 0; j < TOTAL_COL; j++) {
                if (i == 3 && j == 0) {
                    rows[i].cells[j] = new Cell(".", State.DEFAULT_NUM, i, j);
                    continue;
                } else if (i == 3 && j == 2) {
                    rows[i].cells[j] = new Cell("-5", State.DEFAULT_NUM, i, j);
                    continue;
                } else {
                    rows[i].cells[j] = new Cell(numKeys.get(postion), State.DEFAULT_NUM, i, j);
                    postion++;
                }
            }
        }
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_cancel);
    }

    /**
     * 随机键盘
     * @param isRandom
     */
    public void setRandomKeyBoard(boolean isRandom) {
        if (isRandom) {
            Collections.shuffle(numKeys);
            initDate();
            invalidate();
        }
    }

}

这里主要就是要注意单元格的绘制坐标,代码应该很好理解。

键盘已经画好,接下来就是使用了:

2.MainActivity

package net.yangming.numkeyboard;

import android.graphics.drawable.ColorDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.InputType;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.PopupWindow;

public class MainActivity extends AppCompatActivity implements CustomNumKeyView.CallBack {
    private EditText mEditText;
    private NumKeyView mKeyView;
    private CustomNumKeyView mCustomKeyView;
    private LinearLayout mLinearlayout;
    private PopupWindow mPop;
    private View mPopView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mLinearlayout = (LinearLayout) findViewById(R.id.linear);
        mEditText = (EditText) findViewById(R.id.edit);
        // 设置不弹出系统键盘
        mEditText.setInputType(InputType.TYPE_NULL);
		// 自己监听EditText的点击事件弹出我们自定义的键盘
        mEditText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mPop.showAtLocation(mLinearlayout, Gravity.BOTTOM, 0, 0);
            }
        });
        mPop = new PopupWindow();
//        mCustomKeyView=new CustomNumKeyView(this);
        mPopView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.custom_keyboardview, null);
        mPop.setContentView(mPopView);
        mPop.setTouchable(true);
        mPop.setFocusable(true);
        mPop.setBackgroundDrawable(new ColorDrawable());
        mPop.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
        mPop.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        mCustomKeyView = (CustomNumKeyView) mPopView.findViewById(R.id.keyboardview);
        // 设置回调,并进行文本的插入与删除
        mCustomKeyView.setOnCallBack(this);
    }

    public void OnButtonClick(View view) {
        switch (view.getId()) {
            case R.id.btn_default:
                mCustomKeyView.setRandomKeyBoard(false);
                break;
            case R.id.btn_random:
	            // 设置随机数字键盘
                mCustomKeyView.setRandomKeyBoard(true);
                break;
        }
    }

    @Override
    public void clickNum(String num) {
        if (mEditText.getText().length() < 6) {
            mEditText.append(num);
            //文本长度为6时隐藏键盘
            if (mEditText.getText().length() == 6) {
                mPop.dismiss();
            }
        }
    }

    @Override
    public void deleteNum() {
        int last = mEditText.getText().length();
        if (last > 0) {
            //删除最后一位
            mEditText.getText().delete(last - 1, last);
        }
    }
}

3.custom_keyboardview

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <net.yangming.numkeyboard.CustomNumKeyView
        android:id="@+id/keyboardview"
        android:layout_width="match_parent"
        android:layout_height="200dp" />
</LinearLayout>

好了,自定义键盘就完成了 >_<

ps:
我这里只是做了最简单的键盘,主要是讲解实现的方法和思路。你们可以根据自己的需求画出不同的UI效果和点击效果。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android自定义数字键盘 的相关文章

  • 如何减少Android中CheckBox的宽度和高度

    谁能告诉我如何减少 CheckBox 的宽度和高度 以便在 Android 中显示非常小的 CheckBox 提前致谢 只需像使用普通按钮一样使用 setWidth w 和 setHeight h 函数即可
  • 使用 Cordova 加载应用程序时如何配置状态栏颜色?

    我用 Cordova 创建了一个应用程序 我想改进 UI UX 使其看起来尽可能像本机应用程序 当应用程序第一次加载时 状态栏 和背景 的前几毫秒 实际上不到 1 秒 没有我配置的颜色Cordova 状态栏插件 https cordova
  • 从相机、SurfaceView 或 SurfaceHolder 连续获取图像数据

    所以我设置了这个相机预览Camera SurfaceView and SurfaceHolder 我也有一个ImageView我将在其中放置相机图像的修改版本 并且我希望它更新 假设每秒更新一次 当我从 res 加载图像时 所有代码都已准备
  • Phonegap(3.0.0) 相机第一次尝试不成功

    出于测试目的 我复制了在音隙相机 API http docs phonegap com en 3 0 0 cordova camera camera md html Camera我发出警报onPhotoDataSuccess测试函数何时被触
  • 使用kivy/python访问android手电筒(相机LED闪光灯)

    我不知道如何使用 python 或 kivy 访问 android 上的 led 灯 我尝试安装 python for android 以便能够将 android 模块导入到我的代码中 但不是找不到模块 我按照此处的说明克隆了 python
  • android 弹出菜单文本颜色(AppCompat)

    我需要更改 popuo 菜单的文本颜色 但我找不到任何方法来执行此操作 我可以更改 popomenu 的背景但不能更改文本 我以这种方式编辑 style xml
  • 动态添加 TextView - Android

    如何动态添加 TextView 到此 注释掉的代码不起作用 public class myTextSwitcher extends Activity private TextView myText public myTextSwitcher
  • 在Android中的TextView下方绘制下划线

    我想在我的下面画下划线TextView 我搜索了一些内容 但找不到任何有成果的内容 有人可以帮我吗 在 TextView 中添加文本下方有三种方法 跨度字符串 http developer android com reference and
  • 无法从 https 下载 .apk

    我为我的客户开发了一个 Android 应用程序 我不想在 Play 商店上发布它 我已将其托管在我的客户端网站上 asp net 我已使用 MIME 类型 apk application vnd android package archi
  • android中viewpager中的多个视频播放器

    我想在 viewpager 中的不同片段上播放视频 我为每个片段使用多个媒体播放器和表面视图 当我向左或向右滑动时 我也想暂停和开始视频 滑动到下一个视频完全没有问题 但是当我滑动到上一个视频 已经在播放 时 表面视图重叠 同时 我可以毫无
  • 呼叫转移

    我想将所有拨打我号码的呼叫转接至新的预定义号码 自动地 可以转接来电吗 也许至少对于 Froyo 来说是可能的 我找到了名为 Easy Call Forwarding 的应用程序 http www appstorehq com easyca
  • Firebase 实时数据库 .info/connected 本应为 True 时为 False

    我有一个 Android 服务 它的调用地址为onCreate FirebaseDatabase database FirebaseDatabase getInstance database getReference info connec
  • 如何将两个 APK 合并为一个,以便两个应用程序可以同时安装

    如何将 2 个 Android 应用程序合并到捆绑包中 以便在安装捆绑包时同时安装两个应用程序 我想将 2 个 APK 合并到一个捆绑包中 以便我可以将其上传到 Android Market 当有人将其安装到设备上时 这两个应用程序都应该安
  • 使android listview布局可滚动

    我有一个 xml 文件 其布局为 ASCII 形式 ImageView TextView List
  • 在 Android 应用程序中使用传单来显示在线地图

    是否有任何示例项目展示如何正确使用传单在 Android 应用程序中显示在线地图 因为我尝试了很多示例 但每次我的应用程序中都有一个空的网络视图 这是我的代码 private WebView mWebView Override protec
  • 无法在android中使用retrofit发出@Post请求

    我正在学习如何在 android 中使用改造 但是每当我尝试从互联网检索数据时 我的应用程序不会返回任何内容我的响应没有成功 我不知道如何修复当前我正在尝试发布的错误并使用此 URL 检索数据https jsonplaceholder ty
  • 无法放置双重 SharedPreferences

    出现错误 这种类型的共享首选项编辑器的 put double 方法未定义 Eclipse 提供了一种快速修复方法 将强制类型转换添加到编辑器 但是当我这样做时 它仍然给出错误 为什么我不能 put double 代码 Override pr
  • 有没有办法检测apk是否存储在SD卡上?

    有没有办法检测apk是否存储在SD卡上 如何 使用 getApplicationInfo sourceDir http developer android com reference android content pm Applicati
  • Android Studio 3.0 中的 Gradle 构建错误

    您能帮我解决 Android 3 0 中的 Gradle 构建问题吗 我是 Android Studio 的新手 以下是我在 AS 3 0 中的配置 gradle gt wrapper gt gradle wrapper propertie
  • E/libEGL: validate_display:99 错误 3008 (EGL_BAD_DISPLAY) API 24 或更高版本

    当我使用 API 为 24 或更高版本的设备时 我收到此错误 E libEGL validate display 99 错误 3008 EGL BAD DISPLAY XML 代码 activity main xml

随机推荐

  • This exception may occur if matchers are combined with raw values

    org mockito exceptions misusing InvalidUseOfMatchersException Invalid use of argument matchers 3 matchers expected 2 rec
  • SpringCloud利用Feign访问外部http请求

    大家好 目前接手了一个项目 具体的逻辑并不复杂 主要是一个 中间商 角色 比如客户端通过我访问高德地图API 就不需要带秘钥 直接带高德API所需的入参和url后缀 就可以访问 目前遇到这样一个问题 项目架构师要求所有的项目自己写的httt
  • 【Windows系统资源】​​​​​​​iexplore.exe命令行参数解释

    资源 C Program Files Internet Explorer iexplore exe 语法 nohome 双击此快捷方式则只打开一个空白IE窗口 可以加快IE启动速度 同时如果IE主页被恶意修改了 利用此法就不会自动打开恶意主
  • Linux指令--别名alias

    文章目录 1 定义别名 2 查看别名 3 取消别名 4 起别名的弊端 5 别名的失效 6 单引号与双引号下别名的区别 7 shell脚本中的别名 1 定义别名 格式 alias name values 等号 前后不能有空格 values中有
  • codeforces Gym 101341 K Competitions

    Problem codeforces com gym 101341 problem K vjudge net contest 162325 problem K Meaning 有 n 场比赛 每一场有 开始时间 a 结束时间 b 价值 c
  • java基于微信小程序旅游管理系统 uniapp 小程序+论文

    本旅游服务软件 主要实现了管理员后端 首页 个人中心 旅游攻略管理 旅游资讯管理 景点信息管理 门票预定管理 用户管理 酒店信息管理 酒店预定管理 推荐路线管理 论坛管理 系统管理 用户前端 首页 景点信息 酒店信息 论坛中心 我的等 总体
  • 对比openai,我更喜欢ppword的近期更新

    刚刚 23年8月17日 openai宣布收购Global Illumination Global Illumination简单说 就是人工智能落地公司 来帮chatGPT找落地场景 昨天 23年8月16日 宣布更新了内容审核的解决方案 号称
  • 机器学习算法 随机森林

    文章目录 一 概述 1 1 集成学习 1 2 决策树 1 3 随机森林 二 Sklearn中的随机森林 2 1 分类树API 2 2 参数 2 2 回归树API 2 2 1 重要参数 2 3 随机森林调参 三 总结 一 概述 1 1 集成学
  • unity渲染队列render quaue

    本文转载自http blog csdn net candycat1992 article details 37345251 本系列主要参考 Unity Shaders and Effects Cookbook 一书 感谢原书作者 同时会加上
  • shell中for循环变量常见使用场景

    shell中for循环变量常见使用场景 1 目的 在shell脚本for循环使用过程中经常出现非常规使用场景 如在awk的条件语句中 输出文本中变量后有字符等 这些场景中需要对for循环中的变量做处理 希望持续完善 2不同场景 2 1 变量
  • React配置webpack(一)

    一 安装 create react app npx create react app react webpack app cd react webpack app npm start 二 初始化项目 npm init y 三 安装所需包 w
  • Linux中Hadoop的安装与配置

    1 首先关闭虚拟机的防火墙 2 查看防火墙状态 3 然后修改主机名称 配置hosts vi etc hosts 前面是自己的主机IP地址 后面是想要修改的主机名称 修改完成保存后reboot重启即可修改完成 4 把Hadoop和jdk压缩包
  • 一文读懂迭代器(iterator)在vector中的用法

    首先写好头文件vector和string的头文件是 include
  • Linux中vim下方向键变成ABCD,backspace无法删除字符的解决办法

    在linux中安装完VIM后 发现在insert模式下 按下四个方向键在屏幕中会输出ABCD这样的字符 并且Backspace无法实现删除功能 虽然可以在normal模式下通过hjkl实现光标的移动 通过x和d实现删除功能 但是每次都要进行
  • 【Python编程】删除列表中具有连续重复项的元素

    删除列表中具有连续重复项的元素 输入 1 1 1 1 1 1 2 3 4 4 5 1 2 输出 1 2 3 4 5 1 2 方法1 循环遍历 a 1 1 1 1 1 1 2 3 4 4 5 1 2 i 0 while i lt len a
  • 如何成为一名AI人工智能算法工程师?

    https www toutiao com a6707050434688713227 经常有朋友私信问 如何学python呀 如何敲代码呀 如何进入AI行业呀 正好回头看看自己这一年走过的路 进行一次经验总结 来看看你距离成为一名AI工程师
  • 时序预测:MATLAB实现时间序列回归之Bootstrapped测试

    时序预测 MATLAB实现时间序列回归之Bootstrapped测试 时序预测是实际应用中非常重要的任务 它在金融 企业经营 气象预测等领域都有广泛的应用 而时间序列回归也是时序预测中最为基础和实用的方法之一 本文将通过MATLAB对时间序
  • 【QT实战】第一章 QT实现画板工具的制作

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏目录 零基础学QT 文章导航篇 专栏资料 https pan baidu com s 192A28BTIYFHmixRcQwmaHw 提取码 qtqt 点
  • c++ 学习之 构造函数的使用规则

    上规则 默认情况下 c 编译器至少给一个类添加三个函数 1 默认构造函数 无参 函数体为空 2 默认析构函数 无参 函数体为空 3 默认拷贝函数 对其属性进行值拷贝 构造函数调用规则 如果用户定义有参构造函数 c 不再提供默认无参构造函数
  • android自定义数字键盘

    前言 最近需要做一个自定义的数字键盘 开始使用了下系统自带的KeyBoardView 但是发现UI效果不是很理想 最后还是自己画一个自定义键盘 这样在UI方面更加方便 先看效果图吧 思路 1 键盘4行 3列的布局分为12个单元格 6条直线分