走进股神境界之股票键盘设计与实现

前言

之前一段时间,公司开始研发一款炒股软件。

如今,项目已经上线有段时间了。现在就将其中的一个功能—“股票键盘”拿出来给大家献献丑。

不说了,先上效果图:

股票键盘

技术点

说到股票软件,股票界的泰山北斗自然是“同花顺”了。小编设计研发的股票键盘也是向“同花顺”靠齐了。

该股票键盘的技术点主要有如下几点:

  1. 屏蔽系统键盘
  2. 自定义键盘
  3. 键盘显示方式
  4. 系统底部功能按钮适配

只要理清楚了这几点,那对功能的实现是起到事半功倍的效果。

接下来就从代码实现来给大家讲解一下。

股票键盘的实现

股票键盘的功能实现最主要的按钮是使用了系统提供的控件:KeyboardViewKeyboard

KeyboardView:keyboard的容器,KeyboardView.setKeyboard()切换显示的键盘

Keyboard:键盘的具体显示

设计键盘布局

键盘的布局通过xml文件方式定义。

KeyboardRowKey组成。

  • Keyboard:Keyboard控件的根布局,用以控制按钮行、列间距,按钮Key的宽高。
属性 介绍
android:horizontalGap Default horizontal gap between keys.
android:keyHeight Default height of a key, in pixels or percentage of display width.
android:keyWidth Default width of a key, in pixels or percentage of display width.
android:verticalGap Default vertical gap between rows of keys.
  • Row:行,Key的容器
属性 介绍
android:horizontalGap Default horizontal gap between keys.
android:keyHeight Default height of a key, in pixels or percentage of display width.
android:keyWidth Default width of a key, in pixels or percentage of display width.
android:keyboardMode Mode of the keyboard.
android:rowEdgeFlags Row edge flags.
android:verticalGap Default vertical gap between rows of keys.
  • Key:按钮,设置按钮的属性,最常用的有
    • codes:OnKeyboardActionListener.onKey()中第一个参数为该值,用以区分按钮
    • keyLabel:按钮显示的内容
    • isRepeatable:按钮长按是否可重复发起回调
    • keyIcon:按钮显示的图标
    • keyEdgeFlags:按钮边的标志,leftright
属性 介绍
android:codes The unicode value or comma-separated values that this key outputs.
android:horizontalGap Default horizontal gap between keys.
android:iconPreview The icon to show in the popup preview.
android:isModifier Whether this is a modifier key such as Alt or Shift.
android:isRepeatable Whether long-pressing on this key will make it repeat.
android:isSticky Whether this is a toggle key.
android:keyEdgeFlags Key edge flags.
android:keyHeight Default height of a key, in pixels or percentage of display width.
android:keyIcon The icon to display on the key instead of the label.
android:keyLabel The label to display on the key.
android:keyOutputText The string of characters to output when this key is pressed.
android:keyWidth Default width of a key, in pixels or percentage of display width.
android:popupCharacters The characters to display in the popup keyboard.
android:popupKeyboard The XML keyboard layout of any popup keyboard.

数字键盘

keyboard_stock_num.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:horizontalGap="1dp"
android:keyHeight="@dimen/key_height"
android:keyWidth="26.67%p"
android:verticalGap="1dp">
<Row>
<Key
android:codes="8600"
android:keyEdgeFlags="left"
android:keyLabel="600"
android:keyWidth="20%p" />
<Key
android:codes="49"
android:keyLabel="1" />
<Key
android:codes="50"
android:keyLabel="2" />
<Key
android:codes="51"
android:keyLabel="3" />
</Row>
<Row>
<Key
android:codes="8300"
android:keyEdgeFlags="left"
android:keyLabel="300"
android:keyWidth="20%p" />
<Key
android:codes="52"
android:keyLabel="4" />
<Key
android:codes="53"
android:keyLabel="5" />
<Key
android:codes="54"
android:keyLabel="6" />
</Row>
<Row>
<Key
android:codes="8000"
android:keyEdgeFlags="left"
android:keyLabel="00"
android:keyWidth="20%p" />
<Key
android:codes="55"
android:keyLabel="7" />
<Key
android:codes="56"
android:keyLabel="8" />
<Key
android:codes="57"
android:keyLabel="9" />
</Row>
<Row>
<Key
android:codes="8065"
android:keyEdgeFlags="left"
android:keyLabel="ABC"
android:keyWidth="20%p" />
<Key
android:codes="48"
android:keyLabel="0" />
<Key
android:codes="-5"
android:isRepeatable="true"
android:keyIcon="@drawable/icon_keyboard_del" />
<Key
android:codes="8079"
android:isRepeatable="true"
android:keyEdgeFlags="right"
android:keyLabel="确定" />
</Row>
</Keyboard>

字母键盘

keyboard_stock_letter.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<?xml version="1.0" encoding="UTF-8"?><!-- 全键盘带数字小键盘 -->
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:horizontalGap="0.3%p"
android:keyHeight="@dimen/key_height"
android:keyWidth="9.7%p"
android:verticalGap="0.12%p">

<Row>
<Key
android:codes="81"
android:keyEdgeFlags="left"
android:keyLabel="Q" />
<Key
android:codes="87"
android:keyLabel="W" />
<Key
android:codes="69"
android:keyLabel="E" />
<Key
android:codes="82"
android:keyLabel="R" />
<Key
android:codes="84"
android:keyLabel="T" />
<Key
android:codes="89"
android:keyLabel="Y" />
<Key
android:codes="85"
android:keyLabel="U" />
<Key
android:codes="73"
android:keyLabel="I" />
<Key
android:codes="79"
android:keyLabel="O" />
<Key
android:codes="80"
android:keyEdgeFlags="right"
android:keyLabel="P" />

</Row>
<Row>
<Key
android:codes="0"
android:keyEdgeFlags="left"
android:keyLabel=""
android:keyWidth="5%p" />
<Key
android:codes="65"
android:keyLabel="A" />
<Key
android:codes="83"
android:keyLabel="S" />
<Key
android:codes="68"
android:keyLabel="D" />
<Key
android:codes="70"
android:keyLabel="F" />
<Key
android:codes="71"
android:keyLabel="G" />
<Key
android:codes="72"
android:keyLabel="H" />
<Key
android:codes="74"
android:keyLabel="J" />
<Key
android:codes="75"
android:keyLabel="K" />
<Key
android:codes="76"
android:keyLabel="L" />
<Key
android:codes="0"
android:keyEdgeFlags="right"
android:keyLabel=""
android:keyWidth="5%p" />
</Row>
<Row>
<Key
android:codes="-1"
android:isModifier="true"
android:isSticky="true"
android:keyEdgeFlags="left"
android:keyIcon="@drawable/icon_keyboard_shift"
android:keyWidth="15%p" />
<Key
android:codes="90"
android:keyLabel="Z" />
<Key
android:codes="88"
android:keyLabel="X" />
<Key
android:codes="67"
android:keyLabel="C" />
<Key
android:codes="86"
android:keyLabel="V" />
<Key
android:codes="66"
android:keyLabel="B" />
<Key
android:codes="78"
android:keyLabel="N" />
<Key
android:codes="77"
android:keyLabel="M" />
<Key
android:codes="-5"
android:isRepeatable="true"
android:keyEdgeFlags="right"
android:keyIcon="@drawable/icon_keyboard_del"
android:keyWidth="15%p" />
</Row>

<Row android:rowEdgeFlags="bottom">
<Key
android:codes="8049"
android:keyEdgeFlags="left"
android:keyLabel="123"
android:keyWidth="25%p" />
<Key
android:codes="32"
android:keyLabel=""
android:keyWidth="50%p" />
<Key
android:codes="8079"
android:keyEdgeFlags="right"
android:keyLabel="确定"
android:keyWidth="25%p" />
</Row>
</Keyboard>

隐藏系统键盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 隐藏系统键盘
*
* @param editText
*/
public static void hideSystemSofeKeyboard(EditText editText) {
//Android L之后可直接设置
if (BuildUtils.isBiggerL()) {
editText.setShowSoftInputOnFocus(false);
} else if (BuildUtils.isBiggerH()) {//通过反射设置
try {
Class<EditText> cls = EditText.class;
Method setShowSoftInputOnFocus;
setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
setShowSoftInputOnFocus.setAccessible(true);
setShowSoftInputOnFocus.invoke(editText, false);

} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
} else {
editText.setInputType(InputType.TYPE_NULL);
}
}
  • Android H(11)之前设置EditText.setInputType(InputType.TYPE_NULL);屏蔽系统键盘
  • Android H(11)~Android L(21)通过反射设置setShowSoftInputOnFocusfalse
  • Android L(21)以及之后 直接设置EditText.setShowSoftInputOnFocus(true)

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public StockKeyboardUtil(Activity mActivity) {
this.mActivity = mActivity;
//初始化数字、字母键盘
this.keyboardMum = new Keyboard(mActivity, R.xml.keyboard_stock_num);
this.keyboardLetter = new Keyboard(mActivity, R.xml.keyboard_stock_letter);
wordClolrGray = mActivity.getResources().getColor(R.color.def_word_gray);
wordClolrRed = mActivity.getResources().getColor(R.color.red_title);
curKeyboardType = KEYBOARD_NUM;
//判断并获取系统底部虚拟功能按钮高度,避免本键盘覆盖系统底部虚拟功能按钮
hasNav = NavigationBarUtils.checkDeviceHasNavigationBar(mActivity);
if (hasNav) {
navHight = NavigationBarUtils.getNavigationBarHeight(mActivity);
}
}

NavigationBarUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* <pre>
* author : Haitao
* blog : http://www.blog.com
* time : 2017/12/4
* desc : NavigationBar,系统底部虚拟按钮工具类
* version: 2.0
* </pre>
*/
public class NavigationBarUtils {

/**
* 判断是否有NavigationBar,系统底部虚拟按钮
* @param context
* @return
*/
public static boolean checkDeviceHasNavigationBar(Context context) {
boolean hasNavigationBar = false;
Resources rs = context.getResources();
int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0) {
hasNavigationBar = rs.getBoolean(id);
}
try {
Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
Method m = systemPropertiesClass.getMethod("get", String.class);
String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");
if ("1".equals(navBarOverride)) {
hasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
hasNavigationBar = true;
}
} catch (Exception e) {
}

return hasNavigationBar;

}

/**
* 获取NavigationBar的高度
* @param context
* @return
*/
public static int getNavigationBarHeight(Context context) {
int navigationBarHeight = 0;
Resources rs = context.getResources();
int id = rs.getIdentifier("navigation_bar_height", "dimen", "android");
if (id > 0 && checkDeviceHasNavigationBar(context)) {
navigationBarHeight = rs.getDimensionPixelSize(id);
}
return navigationBarHeight;
}
}

初始化

两个Keyboard:keyboardMum、keyboardLetter;

色值:灰、红;

系统底部虚拟按钮:hasNav-是否有,navHight-高度

NavigationBarUtils为NavigationBar的工具类,用以判断是否有NavigationBar(系统底部虚拟按钮),获取NavigationBar的高度。

绑定EditText

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* 绑定EditText
*
* @param editText
* @param isAuto 当前是否显示股票键盘
*/
public void attachTo(EditText editText, boolean isAuto) {
this.editText = editText;
//隐藏系统键盘
hideSystemSofeKeyboard(this.editText);
setAutoShowOnFocs(isAuto);
}
/**
* editText 是否设置焦点改变(OnFocusChangeListener)后隐藏/显示 本股票键盘
*
* @param enable
*/
public void setAutoShowOnFocs(boolean enable) {
if (editText == null)
return;
if (enable)
editText.setOnFocusChangeListener(onFocusChangeListener1);
else
editText.setOnFocusChangeListener(null);
}

//焦点改变回调,用以监听本股票键盘绑定的EditText的焦点,以控制该键盘的隐藏/显示
View.OnFocusChangeListener onFocusChangeListener1 = new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
showSoftKeyboard();
else
hideSoftKeyboard();
}
};

初始化 完成后调用attachTo(),绑定传入的EditText
内部hideSystemSofeKeyboard()隐藏系统键盘,setAutoShowOnFocs()设置焦点改变监听,用以控制本股票键盘的隐藏/显示。
股票键盘的隐藏/显示见下章节。

股票键盘显示/隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
* 显示股票键盘
*/
public void showSoftKeyboard() {
//处理系统键盘问题
if (curKeyboardType == KEYBOARD_CH) {
curKeyboardType = KEYBOARD_NUM;
}

isShown = true;

//根据viewContainer,避免重复初始化
if (viewContainer == null) {
viewContainer = mActivity.getLayoutInflater().inflate(R.layout.view_keyboardview, null);
tvNum = (TextView) viewContainer.findViewById(R.id.tv_num);
tvLetter = (TextView) viewContainer.findViewById(R.id.tv_letter);
tvCh = (TextView) viewContainer.findViewById(R.id.tv_chinese);
tvSlash = (TextView) viewContainer.findViewById(R.id.tv_slash);
flHide = (FrameLayout) viewContainer.findViewById(R.id.fl_hide);
vHolder = viewContainer.findViewById(R.id.v_holder);

//keyboardView是keyboard的容器,KeyboardView.setKeyboard()切换键盘
keyboardView = (KeyboardView) viewContainer.findViewById(R.id.keyboard_view);
keyboardView.setEnabled(true);
keyboardView.setPreviewEnabled(false);
//设置键盘按钮回调
keyboardView.setOnKeyboardActionListener(new OnKeyboardActionListener() {
@Override
public void swipeUp() {
}

@Override
public void swipeRight() {
}

@Override
public void swipeLeft() {
}

@Override
public void swipeDown() {
}

@Override
public void onText(CharSequence text) {
}

@Override
public void onRelease(int primaryCode) {
}

@Override
public void onPress(int primaryCode) {
}

@Override
public void onKey(int primaryCode, int[] keyCodes) {
if (editText != null) {
//处理点击的按钮,
// primaryCode即:键盘布局文件中Key中的android:codes值,
// 该值为ASCII表中的十进制列,
// 比如:48->0,97->a,
// 这样表示方便将int型转换成字符
keyCode_delect(primaryCode, editText);
}
keyboardView.postInvalidate();
}
});


} else {
if (viewContainer.getParent() != null)
return;
}
tvNum.setOnClickListener(this);
tvLetter.setOnClickListener(this);
tvCh.setOnClickListener(this);
tvSlash.setOnClickListener(this);
flHide.setOnClickListener(this);
vHolder.setOnClickListener(this);

changeKeyboard(curKeyboardType);

//直接操作Activity的DecorView,将本股票布局填上去
FrameLayout frameLayout = (FrameLayout) mActivity.getWindow().getDecorView();
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.BOTTOM;
if (hasNav) {
lp.setMargins(0, 0, 0, navHight);
}
frameLayout.addView(viewContainer, lp);
viewContainer.setAnimation(AnimationUtils.loadAnimation(mActivity, R.anim.down_to_up));
}

down_to_up.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="600"
android:fromYDelta="100%p" />
</set>

如果是首次显示,则初始化各种按钮和keyboardView,并给keyboardView注册监听,用以获取股票键盘的按钮回调。

frameLayout为当前ActivityDecorView,直接将本股票布局填到该frameLayout上进行显示。并添加了一个从底部向上移动的View动画。

按钮数据处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* 处理按钮,
* 删除、数字字母切换、确定
*
* @param primaryCode
* @param edText
*/
private void keyCode_delect(int primaryCode, EditText edText) {

Editable editable = edText.getText();
int start = edText.getSelectionStart();
if (primaryCode == Keyboard.KEYCODE_DELETE) {// 回退
if (edText.hasFocus()) {
if (!TextUtils.isEmpty(editable)) {
if (start > 0) {
editable.delete(start - 1, start);
}
}
}

} else if (primaryCode == 8600) {// 600
editable.insert(start, "600");
} else if (primaryCode == 8300) {// 300
editable.insert(start, "300");
} else if (primaryCode == 8000) {// 00
editable.insert(start, "00");
} else if (primaryCode == 8079) {// ok
if (mListener != null) {
mListener.onOkClick();
}
} else if (primaryCode == 8065) {// ABC
changeKeyboard(KEYBOARD_LETTER);
} else if (primaryCode == 8049) {// 123
changeKeyboard(KEYBOARD_NUM);
} else if (primaryCode == -1 || primaryCode == 32) {// shift、空格,不处理
} else {
if (edText.hasFocus()) {
//int和char是可以互转的,这里将int转成char
editable.insert(start, Character.toString((char) primaryCode));
}
}
}

根据传入的primaryCode进行判断处理。

primaryCode设计键盘布局数字键盘字母键盘Key中的android:codes属性值。通过该值来判断当前点击的是哪个键盘按钮。
该值必须唯一。

editable.insert()primaryCode转成char类型后再调用Character.toString()转成字符串插入editable中。因为intchar是可以互转的。两者间的关系可查询ASCII表
ASCII表

changeKeyboard()用以切换当前显示的键盘,总共有3种,这在下章节中介绍。

切换键盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
* 修改键盘类型
*
* @param keyboardType,3种键盘类型:KEYBOARD_NUM、KEYBOARD_LETTER、KEYBOARD_CH
*/
private void changeKeyboard(@KeyboardType int keyboardType) {
curKeyboardType = keyboardType;
switch (keyboardType) {
case KEYBOARD_NUM:
change2Num();//切换为数字键盘
break;
case KEYBOARD_LETTER:
change2Letter();//切换为字母键盘
break;
case KEYBOARD_CH:
change2Ch();//切换为系统键盘
break;
}
}

/**
* 切换为字母键盘
*/
private void change2Letter() {
curKeyboardType = KEYBOARD_LETTER;
//修改股票键盘头部标志及样式
tvNum.setTextColor(wordClolrGray);
tvNum.setTextSize(12);//12表示12sp
tvLetter.setTextColor(wordClolrRed);
tvLetter.setTextSize(18);
//切换为字母键盘
keyboardView.setKeyboard(keyboardLetter);
}

/**
* 切换为数字键盘
*/
private void change2Num() {
curKeyboardType = KEYBOARD_NUM;
tvLetter.setTextColor(wordClolrGray);
tvLetter.setTextSize(12);
tvNum.setTextColor(wordClolrRed);
tvNum.setTextSize(18);
keyboardView.setKeyboard(keyboardMum);
}

/**
* 切换为系统键盘
*/
private void change2Ch() {
hideSoftKeyboard();
showSystemSofeKeyboard(editText);
SoftKeyboardUtil.showSoftKeyboard(mActivity, editText);
editText.postDelayed(new Runnable() {
@Override
public void run() {
hideSystemSofeKeyboard(editText);

}
}, 250);
}

public void hideSoftKeyboard() {
isShown = false;
if (viewContainer != null && viewContainer.getParent() != null) {
((ViewGroup) viewContainer.getParent()).removeView(viewContainer);
}
}

/**
* 显示系统键盘
* @param editText
*/
public static void showSystemSofeKeyboard(EditText editText) {
if (BuildUtils.isBiggerL()) {
editText.setShowSoftInputOnFocus(true);
} else if (BuildUtils.isBiggerH()) {
try {
Class<EditText> cls = EditText.class;
Method setShowSoftInputOnFocus;
setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
setShowSoftInputOnFocus.setAccessible(true);
setShowSoftInputOnFocus.invoke(editText, true);

} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
} else {
editText.setInputType(InputType.TYPE_CLASS_TEXT);
}
}

SoftKeyboardUtil

1
2
3
4
5
6
7
8
9
    public static void showSoftKeyboard(Context context, View view) {
if (view.requestFocus()) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
// imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_NOT_ALWAYS);
}
}
}

数字键盘和字母键盘的切换主要是调用 KeyboardView.setKeyboard()方法进行切换。

中文键盘设置比较复杂:

  1. 先隐藏当前股票键盘
  2. 再将之前屏蔽掉的系统键盘属性还原
  3. 显示系统键盘
  4. 延迟250毫秒后重新将系统键盘屏蔽掉,是为了下次显示股票键盘做准备

完整代码

所涉及到的其他代码这里就不再陈述了,上面的介绍中都有涉及到。

演示示例之后抽空补上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/**
* <pre>
* author : Haitao
* blog : http://blog.nhtzj.com
* time : 2017/12/1
* desc : 股票键盘
* version: 2.0
* </pre>
*/
public class StockKeyboardUtil implements View.OnClickListener {
//键盘类型,3种
public static final int KEYBOARD_NUM = 0; //数字键盘
public static final int KEYBOARD_LETTER = 1; //字母键盘
public static final int KEYBOARD_CH = 2; // 系统键盘

@IntDef({KEYBOARD_NUM, KEYBOARD_LETTER, KEYBOARD_CH})
@Retention(RetentionPolicy.SOURCE)
public @interface KeyboardType {

}

private int curKeyboardType; //当前显示的键盘类型

private Activity mActivity; //当前持有该键盘的Activity

private View viewContainer; //自定义键盘根View
private KeyboardView keyboardView; //keyboardView是keyboard的容器,KeyboardView.setKeyboard()切换键盘
private Keyboard keyboardMum;// 全键盘包括数字和字母
private Keyboard keyboardLetter;// 全键盘包括数字和字母

private TextView tvNum, tvLetter, tvCh, tvSlash;
private FrameLayout flHide;
private View vHolder;
private EditText editText; //键盘绑定的EditText,接收本键盘的输入

private boolean isShown; //本键盘是否显示中

private int wordClolrGray, wordClolrRed; //键盘标题颜色:灰、红

private OnViewClick mListener; //键盘"确定"按钮点击回调

private boolean hasNav; //系统是否有底部虚拟功能按钮
private int navHight; //系统底部虚拟功能按钮的高度

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_num:
case R.id.tv_letter:
case R.id.tv_slash:
switch (curKeyboardType) {
case KEYBOARD_NUM:
changeKeyboard(KEYBOARD_LETTER);//修改为字母键盘
break;
case KEYBOARD_LETTER:
changeKeyboard(KEYBOARD_NUM);//修改为数字键盘
break;
}

break;
case R.id.tv_chinese:
changeKeyboard(KEYBOARD_CH);//修改为中文键盘
break;
case R.id.fl_hide:
case R.id.v_holder:
hideSoftKeyboard();//隐藏键盘
break;
}
}


public StockKeyboardUtil(Activity mActivity) {
this.mActivity = mActivity;
//初始化数字、字母键盘
this.keyboardMum = new Keyboard(mActivity, R.xml.keyboard_stock_num);
this.keyboardLetter = new Keyboard(mActivity, R.xml.keyboard_stock_letter);
wordClolrGray = mActivity.getResources().getColor(R.color.def_word_gray);
wordClolrRed = mActivity.getResources().getColor(R.color.red_title);
curKeyboardType = KEYBOARD_NUM;
//判断并获取系统底部虚拟功能按钮高度,避免本键盘覆盖系统底部虚拟功能按钮
hasNav = NavigationBarUtils.checkDeviceHasNavigationBar(mActivity);
if (hasNav) {
navHight = NavigationBarUtils.getNavigationBarHeight(mActivity);
}
}

/**
* 绑定EditText
*
* @param editText
* @param isAuto 当前是否显示股票键盘
*/
public void attachTo(EditText editText, boolean isAuto) {
this.editText = editText;
//隐藏系统键盘
hideSystemSofeKeyboard(this.editText);
setAutoShowOnFocs(isAuto);
}

/**
* editText 是否设置焦点改变(OnFocusChangeListener)后隐藏/显示 本股票键盘的监听
*
* @param enable
*/
public void setAutoShowOnFocs(boolean enable) {
if (editText == null)
return;
if (enable)
editText.setOnFocusChangeListener(onFocusChangeListener1);
else
editText.setOnFocusChangeListener(null);
}

//焦点改变回调,用以监听本股票键盘绑定的EditText的焦点,以控制该键盘的隐藏/显示
View.OnFocusChangeListener onFocusChangeListener1 = new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
showSoftKeyboard();
else
hideSoftKeyboard();
}
};

/**
* 显示股票键盘
*/
public void showSoftKeyboard() {
//处理系统键盘问题
if (curKeyboardType == KEYBOARD_CH) {
curKeyboardType = KEYBOARD_NUM;
}

isShown = true;

//根据viewContainer,避免重复初始化
if (viewContainer == null) {
viewContainer = mActivity.getLayoutInflater().inflate(R.layout.view_keyboardview, null);
tvNum = (TextView) viewContainer.findViewById(R.id.tv_num);
tvLetter = (TextView) viewContainer.findViewById(R.id.tv_letter);
tvCh = (TextView) viewContainer.findViewById(R.id.tv_chinese);
tvSlash = (TextView) viewContainer.findViewById(R.id.tv_slash);
flHide = (FrameLayout) viewContainer.findViewById(R.id.fl_hide);
vHolder = viewContainer.findViewById(R.id.v_holder);

//keyboardView是keyboard的容器,KeyboardView.setKeyboard()切换键盘
keyboardView = (KeyboardView) viewContainer.findViewById(R.id.keyboard_view);
keyboardView.setEnabled(true);
keyboardView.setPreviewEnabled(false);
//设置键盘按钮回调
keyboardView.setOnKeyboardActionListener(new OnKeyboardActionListener() {
@Override
public void swipeUp() {
}

@Override
public void swipeRight() {
}

@Override
public void swipeLeft() {
}

@Override
public void swipeDown() {
}

@Override
public void onText(CharSequence text) {
}

@Override
public void onRelease(int primaryCode) {
}

@Override
public void onPress(int primaryCode) {
}

@Override
public void onKey(int primaryCode, int[] keyCodes) {
if (editText != null) {
//处理点击的按钮,
// primaryCode即:键盘布局文件中Key中的android:codes值,
// 该值为ASCII表中的十进制列,
// 比如:48->0,97->a,
// 这样表示方便将int型转换成字符
keyCode_delect(primaryCode, editText);
}
keyboardView.postInvalidate();
}
});


} else {
if (viewContainer.getParent() != null)
return;
}
tvNum.setOnClickListener(this);
tvLetter.setOnClickListener(this);
tvCh.setOnClickListener(this);
tvSlash.setOnClickListener(this);
flHide.setOnClickListener(this);
vHolder.setOnClickListener(this);

changeKeyboard(curKeyboardType);

//直接操作Activity的DecorView,将本股票布局填上去
FrameLayout frameLayout = (FrameLayout) mActivity.getWindow().getDecorView();
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.BOTTOM;
if (hasNav) {
lp.setMargins(0, 0, 0, navHight);
}
frameLayout.addView(viewContainer, lp);
viewContainer.setAnimation(AnimationUtils.loadAnimation(mActivity, R.anim.down_to_up));
}


public void hideSoftKeyboard() {
isShown = false;
if (viewContainer != null && viewContainer.getParent() != null) {
((ViewGroup) viewContainer.getParent()).removeView(viewContainer);
}
}

public boolean isShowing() {
return isShown;
}

/**
* 隐藏系统键盘
*
* @param editText
*/
public static void hideSystemSofeKeyboard(EditText editText) {
//Android L以及之后可直接设置
if (BuildUtils.isBiggerL()) {
editText.setShowSoftInputOnFocus(false);
} else if (BuildUtils.isBiggerH()) {//通过反射设置
try {
Class<EditText> cls = EditText.class;
Method setShowSoftInputOnFocus;
setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
setShowSoftInputOnFocus.setAccessible(true);
setShowSoftInputOnFocus.invoke(editText, false);

} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
} else {
editText.setInputType(InputType.TYPE_NULL);
}
}

/**
* 显示系统键盘
* @param editText
*/
public static void showSystemSofeKeyboard(EditText editText) {
if (BuildUtils.isBiggerL()) {
editText.setShowSoftInputOnFocus(true);
} else if (BuildUtils.isBiggerH()) {
try {
Class<EditText> cls = EditText.class;
Method setShowSoftInputOnFocus;
setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
setShowSoftInputOnFocus.setAccessible(true);
setShowSoftInputOnFocus.invoke(editText, true);

} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
} else {
editText.setInputType(InputType.TYPE_CLASS_TEXT);
}
}


/**
* 处理按钮,
* 删除、数字字母切换、确定
*
* @param primaryCode
* @param edText
*/
private void keyCode_delect(int primaryCode, EditText edText) {

Editable editable = edText.getText();
int start = edText.getSelectionStart();
if (primaryCode == Keyboard.KEYCODE_DELETE) {// 回退
if (edText.hasFocus()) {
if (!TextUtils.isEmpty(editable)) {
if (start > 0) {
editable.delete(start - 1, start);
}
}
}

} else if (primaryCode == 8600) {// 600
editable.insert(start, "600");
} else if (primaryCode == 8300) {// 300
editable.insert(start, "300");
} else if (primaryCode == 8000) {// 00
editable.insert(start, "00");
} else if (primaryCode == 8079) {// ok
if (mListener != null) {
mListener.onOkClick();
}
} else if (primaryCode == 8065) {// ABC
changeKeyboard(KEYBOARD_LETTER);
} else if (primaryCode == 8049) {// 123
changeKeyboard(KEYBOARD_NUM);
} else if (primaryCode == -1 || primaryCode == 32) {// shift、空格,不处理
} else {
if (edText.hasFocus()) {
//int和char是可以互转的,这里将int转成char
editable.insert(start, Character.toString((char) primaryCode));
}
}
}

/**
* 修改键盘类型
*
* @param keyboardType,3种键盘类型:KEYBOARD_NUM、KEYBOARD_LETTER、KEYBOARD_CH
*/
private void changeKeyboard(@KeyboardType int keyboardType) {
curKeyboardType = keyboardType;
switch (keyboardType) {
case KEYBOARD_NUM:
change2Num();//切换为数字键盘
break;
case KEYBOARD_LETTER:
change2Letter();//切换为字母键盘
break;
case KEYBOARD_CH:
change2Ch();//切换为系统键盘
break;
}
}

/**
* 切换为字母键盘
*/
private void change2Letter() {
curKeyboardType = KEYBOARD_LETTER;
//修改股票键盘头部标志及样式
tvNum.setTextColor(wordClolrGray);
tvNum.setTextSize(12);//12表示12sp
tvLetter.setTextColor(wordClolrRed);
tvLetter.setTextSize(18);
//切换为字母键盘
keyboardView.setKeyboard(keyboardLetter);
}

/**
* 切换为数字键盘
*/
private void change2Num() {
curKeyboardType = KEYBOARD_NUM;
tvLetter.setTextColor(wordClolrGray);
tvLetter.setTextSize(12);
tvNum.setTextColor(wordClolrRed);
tvNum.setTextSize(18);
keyboardView.setKeyboard(keyboardMum);
}

/**
* 切换为系统键盘
*/
private void change2Ch() {
hideSoftKeyboard();
showSystemSofeKeyboard(editText);
SoftKeyboardUtil.showSoftKeyboard(mActivity, editText);
editText.postDelayed(new Runnable() {
@Override
public void run() {
hideSystemSofeKeyboard(editText);

}
}, 250);
}

/**
* 设置 确定 按钮回调
*
* @param listener
*/
public void setListener(OnViewClick listener) {
this.mListener = listener;
}

public interface OnViewClick {
void onOkClick();
}

}
坚持原创技术分享,您的支持是对我最大的鼓励!