项目中有个需求要求在EditView内只能进行度分秒的控制输入,看到参考其他软件的实现效果,自己实现了一下。
输入的时候会自动限定在经纬度数值范围之内,当光标在引号位置时输入无效并自动跳过。
这个效果需要使用TextWatcher。
TextWatcher接口有三个方法,在输入框内容修改的时候会依次执行
beforeTextChanged(CharSequences, int start, int count, int after);
s是修改之前输入框的内容
start是新内容插入的位置
count是对影响原有内容的长度,插入操作的时候通常为0
after是插入新内容的长度
onTextChanged(CharSequences, int start, int before, int count);
start是改变内容的开始位置
before是改变前的内容数量
count是改变内容的长度
afterTextChanged(Editables);
s是改变后的内容
根据这三个方法我们可以在内容插入的时候对内容进行修改,然后再次调用setText来实现输入框的限制输入,但是需要注意在调用setText后会重新执行这三个方法,可能导致死循环,因此在满足条件的时候就不要再调用setText了。
我们的要求是限制输入为度分秒,不多说,直接上代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
/**
* 度分秒输入控制器,可以将输入控制在经度、纬度、角度范围之内。
* @author Wkkyo
* @date 2016-4-19
* @version 1.0.0
* @code
* <code>
* EditView editView = findViewById(R.id.editView);
* editView.addTextChangedListener(new DMSTextWatcher(editView,0));
* </code>
*
*/
public final class DMSTextWatcher implements TextWatcher{
private String oldText;
private boolean backspace = true;
private int watchType = 0;
private final String MAX_VALUE_N = "90:00:00.00000";
private final String MAX_VALUE_E = "180:00:00.00000";
private final String MAX_VALUE_A = "360:00:00.00000";
private final String DEFAULT_VALUE_N = "00:00:00.00000N";
private final String DEFAULT_VALUE_E = "000:00:00.00000E";
private final String DEFAULT_VALUE_A = "000:00:00.00000";
private EditText mEditText;
private Pattern mPattern;
//纬度正则
private final static String regExNE = "(^([0-8][0-9]):([0-5][0-9]):([0-5][0-9])(\\.{1})([0-9]{5})" +
"(N|S)$)|(^([0-8][0-9]):([6][0]):([0-5][0-9])(\\.{1})([0-9]{5})(N|S)$)|(^([0-8]" +
"[0-9]):([0-5][0-9]):(60)(\\.{1})([0]{5})(N|S)$)|(^([0-8][0-9]):([6][0]):([6]" +
"[0])(\\.{1})([0]{5})(N|S)$)|(^90:00:00.00000(N|S)$)";
//经度正则
private final static String regExEW = "(^([0][0-9][0-9]):([0-5][0-9]):([0-5][0-9])(\\.{1})([0-9]{5})(E|W)$)|(^(0[0-9]" +
"[0-9]):([6][0]):([0-5][0-9])(\\.{1})([0-9]{5})(E|W)$)|(^(0[0-9][0-9]):([0-5][0-9]):(60)(\\.{1})" +
"([0]{5})(E|W)$)|(^(0[0-9][0-9]):([6][0]):([6][0])(\\.{1})([0]{5})(E|W)$)|(^([1][0-7][0-9]):" +
"([0-5][0-9]|60):([0-5][0-9])(\\.{1})([0-9]{5})(E|W)$)|(^([1][0-7][0-9]):([0-5][0-9]|60):" +
"(60)(\\.{1})(0{5})(E|W)$)|(^180:00:00.00000(E|W)$)";
//角度正则
private final static String regEx = "(^(([0-2][0-9][0-9])|([0-3][0-5][0-9])):([0-5][0-9]|60):([0-5][0-9])(\\.{1})" +
"([0-9]{5})$)|(^(([0-2][0-9][0-9])|([0-3][0-5][0-9])):([0-5][0-9]|60):(60)(\\.{1})(0{5})$)" +
"|(^([0-3][0-5][0-9]):([6][0]):([0-5][0-9])(\\.{1})([0-9]{5})$)|(^([0-3][0-5][0-9]):(60):(60)" +
"(\\.{1})([0]{5})$)|(^([0-3][0-5][0-9]):([0-5][0-9]):(60)(\\.{1})([0]{5})$)|(^([0-3][0-5][0-9])" +
":([0-5][0-9]):([0-5][0-9])(\\.{1})([0-9]{5})$)|(^360:00:00.00000$)";
/**
* 构造方法。
* @param editText 需要控制的输入框。默认采用纬度控制,纬度最大值90:00:00.00000。
*/
public DMSTextWatcher(EditText editText) {
this(editText,0,null);
}
/**
* 构造方法。
* @param editText 需要控制的输入框。
* @param type 控制方式,经度-1、纬度-0、角度-2。
* <li>0-纬度控制,纬度最大值90:00:00.00000</li>
* <li>1-经度控制,经度最大值180:00:00.00000</li>
* <li>2-角度控制,经度最大值360:00:00.00000</li>
*/
public DMSTextWatcher(EditText editText,int type) {
this(editText,type,null);
}
/**
* 构造方法。
* @param editText 需要控制的输入框。默认采用纬度控制,纬度最大值90:00:00.00000。
* @param defaultValue 默认值。
*/
public DMSTextWatcher(EditText editText,String defaultValue) {
this(editText,0,defaultValue);
}
/**
* 构造方法。
* @param editText 需要控制的输入框。
* @param type 控制方式,经度-1、纬度-0、角度-2。
* <li>0-纬度控制,纬度最大值90:00:00.00000</li>
* <li>1-经度控制,经度最大值180:00:00.00000</li>
* <li>2-角度控制,经度最大值360:00:00.00000</li>
* @param defaultValue 默认值。
*/
public DMSTextWatcher(EditText editText,int type,String defaultValue) {
this.mEditText = editText;
watchType = type;
if(watchType == 0){
this.mPattern = Pattern.compile(regExNE);
if(defaultValue != null){
Matcher matcher = mPattern.matcher(defaultValue);
if(matcher.find()){
this.mEditText.setText(defaultValue);
}else{
this.mEditText.setText(DEFAULT_VALUE_N);
}
}else{
this.mEditText.setText(DEFAULT_VALUE_N);
}
}else if(watchType == 1){
this.mPattern = Pattern.compile(regExEW);
if(defaultValue != null){
Matcher matcher = mPattern.matcher(defaultValue);
if(matcher.find()){
this.mEditText.setText(defaultValue);
}else{
this.mEditText.setText(DEFAULT_VALUE_E);
}
}else{
this.mEditText.setText(DEFAULT_VALUE_E);
}
}else if(watchType == 2){
this.mPattern = Pattern.compile(regEx);
if(defaultValue != null){
Matcher matcher = mPattern.matcher(defaultValue);
if(matcher.find()){
this.mEditText.setText(defaultValue);
}else{
this.mEditText.setText(DEFAULT_VALUE_A);
}
}else{
this.mEditText.setText(DEFAULT_VALUE_A);
}
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// KLog.d("onTextChanged :"+s+" "+start+"---"+before+"---"+count);
if(watchType == 0){
onNSTextChanged(s,start,before,count);
}else if(watchType == 1){
onEWTextChanged(s,start,before,count);
}else if(watchType == 2){
onAngleTextChanged(s,start,before,count);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,int after) {
// KLog.d("beforeTextChanged :"+s+" "+start+"---"+after+"---"+count);
if(backspace){
oldText = new String(s.toString());
}else{
backspace = true;
}
}
@Override
public void afterTextChanged(Editable s) {
// KLog.d("afterTextChanged :"+s);
}
private void onNSTextChanged(CharSequence s, int start, int before, int count){
StringBuilder str = new StringBuilder(s.toString());
if(oldText != null && str.length() < oldText.length()){
if(start == 2 || start == 5 || start == 8 || start == 14){
str = new StringBuilder(oldText);
}else{
str.insert(start, "0");
}
mEditText.setTextKeepState(str);
oldText = null;
}else{
Matcher m = mPattern.matcher(str);
if(!m.find()){
StringBuilder oldStr = new StringBuilder(str.toString().substring(0,start)+str.substring(start+1));
if(count > 1){
str = new StringBuilder(MAX_VALUE_N);
str.append(oldStr.charAt(14) == 'N'?"N":"S");
}else{
if(start == 2 || start == 5 || start == 8){
str = oldStr;
}else if(start == 14 || start == 15){
start = 14;
if(oldStr.charAt(14) == 'N'){
oldStr.replace(start, start+1, "S");
}else{
oldStr.replace(start, start+1, "N");
}
str = oldStr;
mEditText.setSelection(start);
}else{
String ch = str.substring(start, start+1);
str = new StringBuilder(oldStr);
str = str.replace(start, start+1, ch);
if(start == 0){
if(ch.equals("9")){
str = new StringBuilder(MAX_VALUE_N);
str.append(oldStr.charAt(14) == 'N'?"N":"S");
}
}else if(start == 3 || start == 6){
if("6789".indexOf(ch) > -1){
str = str.replace(start, start+1, "6");
str = str.replace(start+1, start+2, "0");
if(start == 6){
str = str.replace(start+3, start+8, "00000");
}
}
}
m = mPattern.matcher(str);
m.reset();
if(!m.find()){
str = oldStr;
}
}
}
backspace = false;
oldText = null;
mEditText.setTextKeepState(str);
}
}
}
private void onEWTextChanged(CharSequence s, int start, int before, int count){
StringBuilder str = new StringBuilder(s.toString());
if(oldText != null && str.length() < oldText.length()){
if(start == 3 || start == 6 || start == 9 || start == 15){
str = new StringBuilder(oldText);
}else{
str.insert(start, "0");
}
mEditText.setTextKeepState(str);
oldText = null;
}else{
Matcher m = mPattern.matcher(str);
if(!m.find()){
StringBuilder oldStr = new StringBuilder(str.toString().substring(0,start)+str.substring(start+1));
if(count > 1){
str = new StringBuilder(MAX_VALUE_E);
str.append(oldStr.charAt(15) == 'W'?"W":"E");
}else{
if(start == 3 || start == 6 || start == 9){
str = oldStr;
}else if(start == 15 || start == 16){
start = 15;
if(oldStr.charAt(15) == 'E'){
oldStr.replace(start, start+1, "W");
}else{
oldStr.replace(start, start+1, "E");
}
str = oldStr;
mEditText.setSelection(start);
}else{
String ch = str.substring(start, start+1);
str = new StringBuilder(oldStr);
str = str.replace(start, start+1, ch);
if(start == 0){
if("23456789".indexOf(ch) > -1){
str = new StringBuilder(MAX_VALUE_E);
str.append(oldStr.charAt(15) == 'W'?"W":"E");
}
if("1".indexOf(ch) > -1 && (str.charAt(1) == '9' || str.charAt(1) == '8')){
str = new StringBuilder(MAX_VALUE_E);
str.append(oldStr.charAt(15) == 'W'?"W":"E");
}
}else if(start == 1){
if(("89").indexOf(ch) > -1 && str.charAt(0) == '1'){
str = new StringBuilder(MAX_VALUE_E);
str.append(oldStr.charAt(15) == 'W'?"W":"E");
}
}else if(start == 4 || start == 7){
if("6789".indexOf(ch) > -1){
str = str.replace(start, start+1, "6");
str = str.replace(start+1, start+2, "0");
if(start == 7){
str = str.replace(start+3, start+8, "00000");
}
}
}
m = mPattern.matcher(str);
m.reset();
if(!m.find()){
str = oldStr;
}
}
}
backspace = false;
oldText = null;
mEditText.setTextKeepState(str);
}
}
}
private void onAngleTextChanged(CharSequence s, int start, int before, int count){
StringBuilder str = new StringBuilder(s.toString());
if(oldText != null && str.length() < oldText.length()){
if(start == 3 || start == 6 || start == 9 || start == 15){
str = new StringBuilder(oldText);
}else{
str.insert(start, "0");
}
mEditText.setTextKeepState(str);
oldText = null;
}else{
Matcher m = mPattern.matcher(str);
if(!m.find()){
StringBuilder oldStr = new StringBuilder(str.toString().substring(0,start)+str.substring(start+1));
if(count > 1){
str = new StringBuilder(MAX_VALUE_A);
}else{
if(start == 3 || start == 6 || start == 9){
str = oldStr;
}else if(start == 15){
start = 15;
str = oldStr;
mEditText.setSelection(start);
}else{
String ch = str.substring(start, start+1);
str = new StringBuilder(oldStr);
str = str.replace(start, start+1, ch);
if(start == 0){
if("456789".indexOf(ch) > -1){
str = new StringBuilder(MAX_VALUE_A);
}
if("3".indexOf(ch) > -1 && ("6789".indexOf(str.charAt(1)) > -1)){
str = new StringBuilder(MAX_VALUE_A);
}
}else if(start == 1){
if(("6789").indexOf(ch) > -1 && str.charAt(0) == '3'){
str = new StringBuilder(MAX_VALUE_A);
}
}else if(start == 4 || start == 7){
if("6789".indexOf(ch) > -1){
str = str.replace(start, start+1, "6");
str = str.replace(start+1, start+2, "0");
if(start == 7){
str = str.replace(start+3, start+8, "00000");
}
}
}
m = mPattern.matcher(str);
m.reset();
if(!m.find()){
str = oldStr;
}
}
}
backspace = false;
oldText = null;
mEditText.setTextKeepState(str);
}
}
}
}
我实现了三种类型的现在,分别对应纬度、经度和角度,在构造方法里通过参数判断,使用也很简单
EditView editView = findViewById(R.id.editView);
editView.addTextChangedListener(new DMSTextWatcher(editView,0));
后来发现有个InputFilter接口似乎也能实现这个效果,时间有限,以后有空再研究。