前言

最近Android开发中用到了自定义数字键盘,网上找的demo不能满足我的需求,比如删除和插入的时候只能在最后删除和插入,不能通过滑动键盘来移动光标。所以现在完成后把它总结写出来。

概述

主要实现以下功能:

(1)只有数字键,包括没有标点符号。

(2)可以在任意点插入和删除数字。

(3)可以通过手指左右滑动键盘来改变光标位置。

(4)输入框右边删除图标和按钮可随着内容有无变化。

(5)每三个数字空一格,输入框最多输入13个字符(包括空格)。

先上运行效果(请忽略UI -_-):

keyboard.gif

步骤:

1、新建一个KeyboardView.java文件。用来自定义所需要的键盘。

首先初始化绑定布局文件。

private void init(Context context, AttributeSet attrs, int defStyleAttr) {

LayoutInflater.from(context).inflate(R.layout.layout_key_board, this);

recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

rl_back=findViewById(R.id.rl_back);

rl_back.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View view) { // 点击关闭键盘

dismiss();

}

});

initData();

initView();

initAnimation();

}

填充数据(如果需要标点符号,则在i==9中填充即可)

private void initData() {

keyboardWords = new ArrayList<>();

for (int i = 0; i < 12; i++) {

if (i < 9) {

keyboardWords.add(String.valueOf(i + 1));

} else if (i == 9) {

keyboardWords.add("");

} else if (i == 10) {

keyboardWords.add("0");

} else {

keyboardWords.add("");

}

}

}

设置适配器

private void initView() {

int spanCount = 12;

recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 3));

recyclerView.setNestedScrollingEnabled(false);

recyclerView.addItemDecoration(new SpaceItemDecoration(spanCount));

adapter = new KeyboardAdapter(getContext(), keyboardWords);

recyclerView.setAdapter(adapter);

}

对键盘的操作

//判断软键盘的状态

public boolean isVisible() {

if (getVisibility() == VISIBLE) {

return true;

}

return false;

}

//弹出软键盘

public void show() {

startAnimation(animationIn);

setVisibility(VISIBLE);

}

//关闭软键盘

public void dismiss() {

if (isVisible()) {

startAnimation(animationOut);

setVisibility(GONE);

}

}

2、Adapter操作。

private Context context;

private List datas;

private OnKeyboardClickListener listener;

public KeyboardAdapter(Context context, List datas) {

this.context = context;

this.datas = datas;

}

@Override

public KeyboardHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View view = LayoutInflater.from(context).inflate(R.layout.item_key_board, parent, false);

KeyboardHolder holder = new KeyboardHolder(view);

setListener(holder);

return holder;

}

private void setListener(final KeyboardHolder holder) {

holder.tvKey.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

if (listener != null) {

if (holder.getAdapterPosition() != 9) {

listener.onKeyClick(view, holder, holder.getAdapterPosition());

}

}

}

});

holder.rlDel.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

if (listener != null) {

listener.onDeleteClick(view, holder, holder.getAdapterPosition());

}

}

});

}

@Override

public void onBindViewHolder(KeyboardHolder holder, int position) {

if (position == 9) {

holder.setVisibility(false);

} else if (position == 11) {

holder.rlDel.setVisibility(View.VISIBLE);

holder.tvKey.setVisibility(View.GONE);

} else {

holder.tvKey.setText(datas.get(position));

}

}

@Override

public int getItemCount() {

return datas == null ? 0 : datas.size();

}

class KeyboardHolder extends RecyclerView.ViewHolder {

public TextView tvKey;

public RelativeLayout rlDel;

private View convertView;

public KeyboardHolder(View itemView) {

super(itemView);

convertView = itemView;

tvKey = itemView.findViewById(R.id.tv_key);

rlDel = itemView.findViewById(R.id.rl_del);

}

public View getconvertView() {

return convertView;

}

public void setVisibility(boolean b) {

RecyclerView.LayoutParams param = (RecyclerView.LayoutParams) itemView.getLayoutParams();

if (b) {

param.height = LinearLayout.LayoutParams.WRAP_CONTENT;

param.width = LinearLayout.LayoutParams.MATCH_PARENT;

itemView.setVisibility(View.VISIBLE);

} else {

itemView.setVisibility(View.GONE);

param.height = 0;

param.width = 0;

}

itemView.setLayoutParams(param);

}

}

public interface OnKeyboardClickListener {

void onKeyClick(View view, RecyclerView.ViewHolder holder, int position);

void onDeleteClick(View view, RecyclerView.ViewHolder holder, int position);

}

public void setOnKeyboardClickListener(OnKeyboardClickListener listener) {

this.listener = listener;

}

}

3、在Activity中的运用。

关闭系统键盘

private void enableSystemKeyboard() {

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

try {

Class cls = EditText.class;

Method setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class);

setShowSoftInputOnFocus.setAccessible(true);

setShowSoftInputOnFocus.invoke(videoIdEt, false);

} catch (Exception e) {

e.printStackTrace();

}

}

键盘数字按钮的点击事件,并且获取输入当前光标位置

@SuppressLint("SetTextI18n")

@Override

public void onKeyClick(View view, RecyclerView.ViewHolder holder, int position) {

String editText = videoIdEt.getText().toString();

switch (position) {

case 9:

break;

default:

//获取当前光标位置

int index = videoIdEt.getSelectionStart();

if (index != videoIdEt.getText().length()) {

//在光标处插入数字

String inputEditText = keyboardNumbers.get(position);

Log.d(TAG, "inputEditText:" + inputEditText);

videoIdEt.getText().insert(index, inputEditText);

} else {

videoIdEt.setText(videoIdEt.getText().toString().trim() + keyboardNumbers.get(position));

videoIdEt.setSelection(videoIdEt.getText().length());

}

if (videoIdEt.getText().length() > 0) {

findViewById(R.id.ai_long_tv_confirm).setBackgroundResource(R.drawable.bg_determine_text);

Drawable drawable = getResources().getDrawable(R.drawable.ic_delete1);

//设置 ClickableEditText中只显示左边图标,删除图标可见

videoIdEt.setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, drawable,null);

}

break;

}

}

手指触摸事件:获取手指按下和滑动停止的位置。将结果传给手势监听。

private View.OnTouchListener onTouchListener = new View.OnTouchListener() {

@RequiresApi(api = Build.VERSION_CODES.ECLAIR)

@SuppressLint("ClickableViewAccessibility")

@Override

public boolean onTouch(View view, MotionEvent motionEvent) {

if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {

Log.d(TAG, "MotionEvent.ACTION_DOWN");

}

if (motionEvent.getAction() == MotionEvent.ACTION_UP) {

Log.d(TAG, "MotionEvent.ACTION_UP");

refreshStartPoint = true;

}

if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {

if (refreshStartPoint) {

startPointEvent = motionEvent.getX(0);

curatorIndex = videoIdEt.getSelectionStart();

}

refreshStartPoint = false;

stopPointEvent = motionEvent.getX();

Log.d(TAG, "MotionEvent.ACTION_MOVE");

}

return mDetector.onTouchEvent(motionEvent);

}

};

用户手势的监测。计算开始和停止滑动距离的大小(滑动前和滑动后的距离),通过计算滑动距离来计算光标移动多少,并通过距离的正负来判断滑动的方向,从而达到在键盘上滑动来控制光标移动的目的。

@RequiresApi(api = Build.VERSION_CODES.CUPCAKE)

private void initGestureDetector() {

mDetector = new GestureDetector(this, new GestureDetector.OnGestureListener() {

@Override

public boolean onDown(MotionEvent motionEvent) {

Log.d(TAG, "onDown");

return true;

}

@Override

public void onShowPress(MotionEvent motionEvent) {

Log.d(TAG, "onShowPress");

}

@Override

public boolean onSingleTapUp(MotionEvent motionEvent) {

Log.d(TAG, "onShowPress");

return false;

}

@Override

public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {

DisplayMetrics displayMetrics = getResources().getDisplayMetrics();

mScreenWidth = displayMetrics.widthPixels;

float x = stopPointEvent - startPointEvent;

float newPercent = Math.abs(x) / mScreenWidth;

int range = (int) ((videoIdEt.getText().length()) * newPercent);

int newIndex;

if (x > 0) {

newIndex = curatorIndex + range;

if (curatorIndex == videoIdEt.getText().length()) {

videoIdEt.setSelection(curatorIndex);

}

if (newIndex > videoIdEt.getText().length()) {

videoIdEt.setSelection(videoIdEt.getText().length());

} else {

videoIdEt.setSelection(newIndex);

}

} else {

newIndex = curatorIndex - range;

if (curatorIndex == 0) {

videoIdEt.setSelection(curatorIndex);

}

if (newIndex < 0) {

videoIdEt.setSelection(0);

} else {

videoIdEt.setSelection(newIndex);

}

}

return false;

}

@Override

public void onLongPress(MotionEvent motionEvent) {

Log.d(TAG, "onLongPress");

}

@Override

public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {

// startPointEvent=motionEvent1;

Log.d(TAG, "onFling: " + "motionEvent:" + motionEvent + "motionEvent1" + motionEvent1);

return false;

}

});

}

接着我们实现每三个数字输入一个空格。通过对ClickableEditText的监听来达到这一目的。

private TextWatcher watcher = new TextWatcher() {

@Override

public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

}

@Override

public void onTextChanged(CharSequence charSequence, int start, int before, int i2) {

if (isTextChanging) {

isTextChanging = false;

return;

}

isTextChanging = true;

String result = "";

String newStr = charSequence.toString();

newStr = newStr.replace(" ", "");

int index = 0;

while ((index + 3) < newStr.length()) {

result += (newStr.substring(index, index + 3) + " ");

index += 3;

}

result += (newStr.substring(index, newStr.length()));

int i = videoIdEt.getSelectionStart();

videoIdEt.setText(result);

try {

if (i % 4 == 0 && before == 0) {

if (i + 1 <= result.length()) {

videoIdEt.setSelection(i + 1);

} else {

videoIdEt.setSelection(result.length());

}

} else if (before == 1 && i < result.length()) {

videoIdEt.setSelection(i);

} else if (before == 0 && i < result.length()) {

videoIdEt.setSelection(i);

} else {

videoIdEt.setSelection(result.length());

}

} catch (Exception e) {

e.printStackTrace();

}

}

@Override

public void afterTextChanged(Editable editable) {

}

};

对右下角删除按钮的监听,只能删除一个一个删除,不能一下删除所输入的内容

@Override

public void onDeleteClick(View view, RecyclerView.ViewHolder holder, int position) {

int currentIndex = videoIdEt.getSelectionStart();

if (currentIndex > 0) {

videoIdEt.getText().delete(currentIndex - 1, currentIndex);

if (videoIdEt.getText().length() == 0) {

//设置 ClickableEditText中只显示左边图标,删除图标不可见

videoIdEt.setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null,null);

findViewById(R.id.ai_long_tv_confirm).setBackgroundResource(R.drawable.bg_determine_disable);

}

}

}

输入框右边删除按钮的点击事件,点击可消除所有输入的内容。

@Override

public void onDrawableRightClick(View view) {

switch (view.getId()) {

case R.id.ai_long_et_video_code:

videoIdEt.getText().clear();

//设置 ClickableEditText中只显示左边图标,删除图标不可见

videoIdEt.setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null,null);

findViewById(R.id.ai_long_tv_confirm).setBackgroundResource(R.drawable.bg_determine_disable);

break;

default:

break;

}

}

结论

以上就是整个项目的实现过程,注意一点,ClickableEditText是自定义的一个EditText,由于使用ConstraintLayout,在输入框的前后两端加按钮,是为了可以点击右边删除按钮能删除所有输入的内容所写。

android 自定义数字软键盘,(笔记)Android自定义数字键盘相关推荐

  1. android二级联动简书,笔记:Android二级联动---RecyclerView实现

    做笔记免得以后有重写一边或者忘了,仅供自己学习(只实现功能,无动画实现,左边没做可见处理,有空再做吧): 1.左右两个RecyclerView, 2.左边RecyclerView点击某个item同时i ...

  2. android拓展笔记本,有道笔记Android版上线,拓展移动终端产品布局

    在大家的翘首企盼下,有道笔记Android版今天终于上线了!广大G粉从今天起也可以在Android系统手机上使用有道笔记获得高效的个人知识管理体验. 有道笔记Android版拥有和桌面版.网页版一致的 ...

  3. python输入数字字符串_Python笔记—基本数据类型—数字字符串

    数字 int #整型 所有的功能,都在int里 a = 123v= a.bit_length() #软件自带函数联想 print(v)-int 将字符串转换成数字 a= '123' print(typ ...

  4. 最小值c语言编写自定义函数,C语言笔记55:自定义函数[老九学堂]

    函数定义 return_type function_name ([datetype1 arg1],[datype2 arg2,[...]){ //函数体 } 函数三要素返回值类型 函数名 参数列表 书 ...

  5. Android逆向文档阅读笔记-Android Application Fundamentals

    Fundamentals Review Android应用程序是在APK格式的文件中的,APK是基于ZIP文件的(可以将APK后缀改成ZIP后缀,然后可以使用unzip去解压). APK文件内容: A ...

  6. Android日常开发 - FlexboxLayout学习笔记

    Android日常开发 - FlexboxLayout学习笔记 Android日常开发使用FlexboxLayout实现流式布局的效果,FlexboxLayout与h5中的flex使用十分相似,都是将 ...

  7. Android开发笔记(一百四十八)自定义输入法软键盘

    手机上输入文字,都是通过系统自带的软键盘,这个软键盘可以是Android自带的,也可以是第三方软键盘如搜狗输入法.多数情况下面,系统自带的软键盘已经够用了,可是总有少数情况,系统软键盘无法满足开发者的 ...

  8. android 自定义数字软键盘,Android自定义键盘的实现(数字键盘和字母键盘)

    在项目中,产品对于输入方式会有特殊的要求,需要对输入方式增加特定的限制,这就需要采用自定义键盘.本文主要讲述数字键盘和字母键盘的自定义实现. 自定义键盘的实现步骤如下: 自定义CustomKeyboa ...

  9. Android自定义记账软键盘(仿鲨鱼记账的记账功能)

    鲨鱼记账App效果: 本文实现的效果图: 本文 不是什么原理分析,属于使用工具,不再具体分析.直接贴图贴代码了 自定义软键盘的XML模版 注:android:codes的值,请参考ASCII < ...

  10. Android 自定义安全软键盘 SafeKeyboard 开发详细说明 2.0

    Android 自定义安全软键盘 SafeKeyboard 开发详细说明 2.0 源码地址:GitHub:    https://github.com/SValence/SafeKeyboard 注意 ...

最新文章

  1. IIS 不能重新启动,并且使用RUN-iisreset 也不能运行,错误提示:No Such Interface Supported (已解决)...
  2. asp.net core web mvc之异常
  3. matlab写字,Matlab实现鼠标写字代码
  4. python生成器用法_理解python中生成器用法
  5. OK335x mksd.sh hacking
  6. 编程实战:如何管理代码里的常量
  7. tomcat运行模式(bio,aio,apr)
  8. OSGB 倾斜摄影数据生产完成后裁剪模型问题
  9. 密码模块安全技术要求
  10. 有容云:梁胜-如何让Docker容器在企业中投产(上)
  11. 恶意软件隐身术:把可执行文件隐藏在注册表里
  12. 头条小程序,小游戏头像授权报错
  13. kepware rest服务器_KEPServerEX
  14. RAP2 API管理工具部署手册
  15. vscode的下载速度会特别慢问题处理
  16. 天龙八部 找怪物ID
  17. 快手2018年招聘算法笔试
  18. 滑块验证码通杀方案--python破解代码
  19. 输入字符串按ASCⅡ大小排序
  20. pycharm笔记-动手学深度学习(李沐)微积分课后习题

热门文章

  1. raspberry pi系统配置
  2. Transmission搭建BT下载服务器
  3. 知乎上 40 个有趣回复,很精辟很提神
  4. 小兔 —— 「 洛谷 」P2768 珍珠项链
  5. apifox设置全局header
  6. ubantu20.04 ros的安装
  7. hrbust 1699 矩阵游戏【枚举找规律】
  8. 130 个相见恨晚的超实用网站
  9. java myeclipse下载_MyEclipse,myeclipse 8.5 下载,myeclipse官网下载_多特软件站
  10. 我的世界java版和基岩版是什么意思_我的世界基岩版是什么意思