DigitsKeyListener.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.text.method; 18 19import android.text.InputType; 20import android.text.Spanned; 21import android.text.SpannableStringBuilder; 22import android.view.KeyEvent; 23 24 25/** 26 * For digits-only text entry 27 */ 28public class DigitsKeyListener extends NumberKeyListener 29{ 30 private char[] mAccepted; 31 private boolean mSign; 32 private boolean mDecimal; 33 34 private static final int SIGN = 1; 35 private static final int DECIMAL = 2; 36 37 @Override 38 protected char[] getAcceptedChars() { 39 return mAccepted; 40 } 41 42 /** 43 * The characters that are used. 44 * 45 * @see KeyEvent#getMatch 46 * @see #getAcceptedChars 47 */ 48 private static final char[][] CHARACTERS = new char[][] { 49 new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }, 50 new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' }, 51 new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' }, 52 new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.' }, 53 }; 54 55 /** 56 * Allocates a DigitsKeyListener that accepts the digits 0 through 9. 57 */ 58 public DigitsKeyListener() { 59 this(false, false); 60 } 61 62 /** 63 * Allocates a DigitsKeyListener that accepts the digits 0 through 9, 64 * plus the minus sign (only at the beginning) and/or decimal point 65 * (only one per field) if specified. 66 */ 67 public DigitsKeyListener(boolean sign, boolean decimal) { 68 mSign = sign; 69 mDecimal = decimal; 70 71 int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0); 72 mAccepted = CHARACTERS[kind]; 73 } 74 75 /** 76 * Returns a DigitsKeyListener that accepts the digits 0 through 9. 77 */ 78 public static DigitsKeyListener getInstance() { 79 return getInstance(false, false); 80 } 81 82 /** 83 * Returns a DigitsKeyListener that accepts the digits 0 through 9, 84 * plus the minus sign (only at the beginning) and/or decimal point 85 * (only one per field) if specified. 86 */ 87 public static DigitsKeyListener getInstance(boolean sign, boolean decimal) { 88 int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0); 89 90 if (sInstance[kind] != null) 91 return sInstance[kind]; 92 93 sInstance[kind] = new DigitsKeyListener(sign, decimal); 94 return sInstance[kind]; 95 } 96 97 /** 98 * Returns a DigitsKeyListener that accepts only the characters 99 * that appear in the specified String. Note that not all characters 100 * may be available on every keyboard. 101 */ 102 public static DigitsKeyListener getInstance(String accepted) { 103 // TODO: do we need a cache of these to avoid allocating? 104 105 DigitsKeyListener dim = new DigitsKeyListener(); 106 107 dim.mAccepted = new char[accepted.length()]; 108 accepted.getChars(0, accepted.length(), dim.mAccepted, 0); 109 110 return dim; 111 } 112 113 public int getInputType() { 114 int contentType = InputType.TYPE_CLASS_NUMBER; 115 if (mSign) { 116 contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED; 117 } 118 if (mDecimal) { 119 contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; 120 } 121 return contentType; 122 } 123 124 @Override 125 public CharSequence filter(CharSequence source, int start, int end, 126 Spanned dest, int dstart, int dend) { 127 CharSequence out = super.filter(source, start, end, dest, dstart, dend); 128 129 if (mSign == false && mDecimal == false) { 130 return out; 131 } 132 133 if (out != null) { 134 source = out; 135 start = 0; 136 end = out.length(); 137 } 138 139 int sign = -1; 140 int decimal = -1; 141 int dlen = dest.length(); 142 143 /* 144 * Find out if the existing text has '-' or '.' characters. 145 */ 146 147 for (int i = 0; i < dstart; i++) { 148 char c = dest.charAt(i); 149 150 if (c == '-') { 151 sign = i; 152 } else if (c == '.') { 153 decimal = i; 154 } 155 } 156 for (int i = dend; i < dlen; i++) { 157 char c = dest.charAt(i); 158 159 if (c == '-') { 160 return ""; // Nothing can be inserted in front of a '-'. 161 } else if (c == '.') { 162 decimal = i; 163 } 164 } 165 166 /* 167 * If it does, we must strip them out from the source. 168 * In addition, '-' must be the very first character, 169 * and nothing can be inserted before an existing '-'. 170 * Go in reverse order so the offsets are stable. 171 */ 172 173 SpannableStringBuilder stripped = null; 174 175 for (int i = end - 1; i >= start; i--) { 176 char c = source.charAt(i); 177 boolean strip = false; 178 179 if (c == '-') { 180 if (i != start || dstart != 0) { 181 strip = true; 182 } else if (sign >= 0) { 183 strip = true; 184 } else { 185 sign = i; 186 } 187 } else if (c == '.') { 188 if (decimal >= 0) { 189 strip = true; 190 } else { 191 decimal = i; 192 } 193 } 194 195 if (strip) { 196 if (end == start + 1) { 197 return ""; // Only one character, and it was stripped. 198 } 199 200 if (stripped == null) { 201 stripped = new SpannableStringBuilder(source, start, end); 202 } 203 204 stripped.delete(i - start, i + 1 - start); 205 } 206 } 207 208 if (stripped != null) { 209 return stripped; 210 } else if (out != null) { 211 return out; 212 } else { 213 return null; 214 } 215 } 216 217 private static DigitsKeyListener[] sInstance = new DigitsKeyListener[4]; 218} 219