Android进阶之路 - 自定义数字键盘

在项目开发中自定义控件使用场景相对比较频繁,基本每一个app都会有一些自定义控件,尤其金融产品中涉及的可能更多一些,例如普通app一般使用的都是原生的系统键盘,只需设定初始展示类型为数字键盘、英文键盘基本就可以满足诉求;但是金融方面的键盘一般都需要自定义,故此该篇先从简单的数字键盘开始…

一直以来自定义控件应该是我的短板之一,所以决定今年好好再学习、巩固一下这方面的不足

在项目中也经常可以见到有些数字键盘是随机显示数字的,不过采用当前所写方式尚不支持随机数键盘,如果要用随机数键盘的话,需要改一些代码,等有机会我补充一下随机数键盘 + + ~

基础了解

这款 自定义数字键盘 采用了 xml(视图) + 自定义类(逻辑)的方式,优点是入门很简单,实现起来也挺快;缺点也很明显就是相对死板、扩展有限 (os:这种方式严格意义上讲不能算是全自定义控件,因为不需要重写View的绘制流程,也不需要进行View的汇算操作,也没有用自定义属性等; )

当前Demo - 实现效果

请添加图片描述

关于数字键盘的调用场景,在我目前能想到的话有以下俩种

  • 静态页面,当前页面显示数字键盘 + 输入内容(输入内容不可点击)

好多年前跟着别人学习过 自定义密码输入框、数字键盘这种效果就是我讲的静态页面效果; 不过,当前我们只讲数字键盘

这里写图片描述

  • 动态页面,用户点击特定控件时弹出数字键盘(相对使用场景更灵活),等后续有机会我再补一篇

自定义 数字键盘

NumKeyboard

内部逻辑实现简单,数据源固定不可变,故通过构造参数传入;同时针对不同按钮进行监听回调即可

package com.example.numkeyboard;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class NumKeyboard extends FrameLayout implements View.OnClickListener {
    
    
    public NumKeyboard(@NonNull Context context) {
    
    
        super(context);
        initView();
    }

    public NumKeyboard(@NonNull Context context, @Nullable AttributeSet attrs) {
    
    
        super(context, attrs);
        initView();
    }

    public NumKeyboard(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    
    
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
    
    
        View numView = LayoutInflater.from(this.getContext()).inflate(R.layout.num_key_board, null);
        numView.findViewById(R.id.tv_0).setOnClickListener(this);
        numView.findViewById(R.id.tv_1).setOnClickListener(this);
        numView.findViewById(R.id.tv_2).setOnClickListener(this);
        numView.findViewById(R.id.tv_3).setOnClickListener(this);
        numView.findViewById(R.id.tv_4).setOnClickListener(this);
        numView.findViewById(R.id.tv_5).setOnClickListener(this);
        numView.findViewById(R.id.tv_6).setOnClickListener(this);
        numView.findViewById(R.id.tv_7).setOnClickListener(this);
        numView.findViewById(R.id.tv_8).setOnClickListener(this);
        numView.findViewById(R.id.tv_9).setOnClickListener(this);
        numView.findViewById(R.id.tv_del).setOnClickListener(this);
        numView.findViewById(R.id.tv_done).setOnClickListener(this);
        LayoutParams params = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
        this.addView(numView, params);
    }


    @SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View view) {
    
    
        if (onNumKeyBoardLister == null) {
    
    
            return;
        }
        switch (view.getId()) {
    
    
            case R.id.tv_0:
                onNumKeyBoardLister.onNumLister(0);
                break;
            case R.id.tv_1:
                onNumKeyBoardLister.onNumLister(1);
                break;
            case R.id.tv_2:
                onNumKeyBoardLister.onNumLister(2);
                break;
            case R.id.tv_3:
                onNumKeyBoardLister.onNumLister(3);
                break;
            case R.id.tv_4:
                onNumKeyBoardLister.onNumLister(4);
                break;
            case R.id.tv_5:
                onNumKeyBoardLister.onNumLister(5);
                break;
            case R.id.tv_6:
                onNumKeyBoardLister.onNumLister(6);
                break;
            case R.id.tv_7:
                onNumKeyBoardLister.onNumLister(7);
                break;
            case R.id.tv_8:
                onNumKeyBoardLister.onNumLister(8);
                break;
            case R.id.tv_9:
                onNumKeyBoardLister.onNumLister(9);
                break;
            case R.id.tv_del:
                onNumKeyBoardLister.onDelLister();
                break;
            case R.id.tv_done:
                onNumKeyBoardLister.onDownLister();
                break;
            default:
                break;
        }
    }

    public void setOnNumKeyBoardLister(NumKeyBoardLister onNumKeyBoardLister) {
    
    
        this.onNumKeyBoardLister = onNumKeyBoardLister;
    }

    public NumKeyBoardLister onNumKeyBoardLister;
	
	//事件监听-接口
    public interface NumKeyBoardLister {
    
    
    	//点击数字按钮时的监听回调
        void onNumLister(int num);
    	//点击删除按钮时的监听回调
        void onDelLister();
    	//点击完成按钮时的监听回调
        void onDownLister();
    }
}

xml 画一个粗糙数字键盘页面

在这里插入图片描述

num_key_board

关于键盘方面的布局方式

  • 有的人用的GridView、有的人用的RecyclerView,用这种方式主要体现在有规则的列表或者网格数据;
  • 如果要定义性更强一点,那么就需要自行绘制了,我此处为图方便直接用 LinearLayoutCompat 权重画的
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/black" />

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="1" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <TextView
            android:id="@+id/tv_2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="2" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <TextView
            android:id="@+id/tv_3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="3" />
    </androidx.appcompat.widget.LinearLayoutCompat>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/black" />

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_4"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="4" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <TextView
            android:id="@+id/tv_5"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="5" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <TextView
            android:id="@+id/tv_6"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="6" />
    </androidx.appcompat.widget.LinearLayoutCompat>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/black" />

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_7"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="7" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <TextView
            android:id="@+id/tv_8"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="8" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <TextView
            android:id="@+id/tv_9"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="9" />
    </androidx.appcompat.widget.LinearLayoutCompat>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/black" />

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_del"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="删除" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <TextView
            android:id="@+id/tv_0"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="0" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <TextView
            android:id="@+id/tv_done"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="完成" />
    </androidx.appcompat.widget.LinearLayoutCompat>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/black" />
</androidx.appcompat.widget.LinearLayoutCompat>

场景使用

逻辑很简单,简要说一下

  • 获取键盘回调,含数字、删除、确定
  • 当获取数字回调时光标置于尾部 + String拼接(因禁用事件,可不管光标问题
  • 当获取删除回调时光标置于尾部 + 尾部逐位删除(因禁用事件,可不管光标问题
  • 当获取完成回调时获取输入内容

MainActivity

package com.example.numkeyboard

import android.os.Bundle
import android.util.Log
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.numkeyboard.NumKeyboard.NumKeyBoardLister
import org.apache.commons.lang3.StringUtils

class MainActivity : AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var mEditNum = findViewById<EditText>(R.id.et_num)
        var mDisplay = findViewById<TextView>(R.id.tv_display)

        findViewById<NumKeyboard>(R.id.numKeyboard).setOnNumKeyBoardLister(object : NumKeyBoardLister {
    
    
            override fun onNumLister(num: Int) {
    
    
                Toast.makeText(this@MainActivity, num.toString(), Toast.LENGTH_SHORT).show()
                //将输入的数字填写输入框中
                mEditNum.text = mEditNum.text.append(num.toString())
                //将光标设置到尾部
                mEditNum.setSelection(mEditNum.text.length)
            }

            override fun onDelLister() {
    
    
                Toast.makeText(this@MainActivity, "删除", Toast.LENGTH_SHORT).show()
                if (mEditNum.text.isNotEmpty()) {
    
    
                    var length = mEditNum.text.length
                    //从后往前逐个删除
                    mEditNum.text = mEditNum.text.delete(length - 1, length)
                    //将光标设置到尾部
                    mEditNum.setSelection(mEditNum.text.length)
                }
            }

            override fun onDownLister() {
    
    
                Toast.makeText(this@MainActivity, "完成", Toast.LENGTH_SHORT).show()
                if (mEditNum.text.isNotEmpty()) {
    
    
                    mDisplay.text = mEditNum.text
                }
            }
        })
    }
}

当前页呈现效果

在这里插入图片描述

activity_main

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

    <EditText
        android:id="@+id/et_num"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_marginHorizontal="50dp"
        android:layout_marginTop="40dp"
        android:background="@null"
        android:clickable="false"
        android:enabled="false"
        android:focusable="false"
        android:gravity="center"
        android:hint="输入内容"
        android:textColor="@color/black" />

    <TextView
        android:id="@+id/tv_display"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_marginHorizontal="50dp"
        android:layout_marginTop="15dp"
        android:gravity="center"
        android:hint="完成 - 显示区域" />

    <View
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <com.example.numkeyboard.NumKeyboard
        android:id="@+id/numKeyboard"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

猜你喜欢

转载自blog.csdn.net/qq_20451879/article/details/128974617
今日推荐