Android-部分可编辑的EditText

有一个需求是这样的,页面上有一个输入框,供用户输入手机号码,如果通讯录里面存在这个号码,会自动把名字追加到号码后面。这个需求变态的地方在于,假如用一个EditText+TextView,那么不好控制二者之间的距离,就算是做了各种适配,但是用户可以设置系统的字体,仍然显示很难看!没办法,之好在一个EditText里面来做,让号码是可编辑的,名字是自动追加上的。

MainActivity.java:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.   
  3.     private FrontPartEditableText edittext;  
  4.     private CombinedEditTextView edittext2;  
  5.   
  6.     @Override  
  7.     protected void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.         setContentView(R.layout.activity_main);  
  10.         //方法一:很挫,效率很低  
  11.         final View rootView = this.findViewById(R.id.rootview);  
  12.         edittext = (FrontPartEditableText) this.findViewById(R.id.edittext1);  
  13.         edittext.setMaxFrontPartLength(11);  
  14.         edittext.setBuildTextContentListener(new FrontPartEditableText.BuildTextContentListener(){  
  15.             @Override  
  16.             public String buildTextContent(String text) {  
  17.                 if(text == null){  
  18.                     return "";  
  19.                 }  
  20.                 String arr[] = text.split("\\s+");  
  21.                 String mobile = arr[0];  
  22.                 String name = getName(mobile);  
  23.                 return mobile + (name == null ? "" : " " + name);  
  24.             }  
  25.             @Override  
  26.             public void afterTextChanged(String text) {  
  27.                 Log.e("test",text);  
  28.             }  
  29.         });  
  30.         // 空白处点击,隐藏软键盘  
  31.         rootView.setOnClickListener(new View.OnClickListener() {  
  32.             @Override  
  33.             public void onClick(View v) {  
  34.                 edittext.hideSoftInput();  
  35.             }  
  36.         });  
  37.           
  38.         //方法二:推荐  
  39.         edittext2 = (CombinedEditTextView) this.findViewById(R.id.edittext2);  
  40.         edittext2.setMaxLength(11);  
  41.         edittext2.setListener(new CombinedEditTextView.TextContentListener() {  
  42.             @Override  
  43.             public String getBackTextContent(String frontText) {  
  44.                 return getName(frontText);  
  45.             }  
  46.             @Override  
  47.             public void afterFrontTextChanged(String text1, String text2) {  
  48.                 Log.e("test""text1:"+text1+",text2:"+text2);  
  49.             }  
  50.         });  
  51.     }  
  52.   
  53.     // 业务方法,联系人数据  
  54.     private Map<String, String> data;  
  55.   
  56.     private String getName(String mobile) {  
  57.         if (data == null) {  
  58.             data = contactData();  
  59.         }  
  60.         return data.get(mobile);  
  61.     }  
  62.   
  63.     private Map<String, String> contactData() {  
  64.         Map<String, String> data = new HashMap<String, String>();  
  65.         data.put("15012341234""张三");  
  66.         data.put("15112341234""李四");  
  67.         data.put("15212341234""王五");  
  68.         return data;  
  69.     }  
  70. }  

activity_main.xml:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="wrap_content"  
  5.     android:orientation="vertical"  
  6.     android:id="@+id/rootview">  
  7.        
  8.      <com.example.edittextdemo.FrontPartEditableText  
  9.         android:id="@+id/edittext1"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="wrap_content"/>  
  12.        
  13.      <com.example.edittextdemo.CombinedEditTextView  
  14.         android:id="@+id/edittext2"  
  15.         android:layout_width="match_parent"  
  16.         android:layout_height="wrap_content"  
  17.         android:layout_marginTop="50dp"/>  
  18.        
  19. </LinearLayout>  

FrontPartEditableText.java:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class FrontPartEditableText extends LinearLayout {  
  2.   
  3.     private Button button;  
  4.     private int maxFrontPartLength;  
  5.     private BackDetectableEditText edittext;  
  6.     private BuildTextContentListener listener;  
  7.   
  8.     public FrontPartEditableText(Context context) {  
  9.         super(context);  
  10.         init();  
  11.     }  
  12.   
  13.     public FrontPartEditableText(Context context, AttributeSet attrs) {  
  14.         super(context, attrs);  
  15.         init();  
  16.     }  
  17.   
  18.     public FrontPartEditableText(Context context, AttributeSet attrs, int defStyle) {  
  19.         super(context, attrs, defStyle);  
  20.         init();  
  21.     }  
  22.   
  23.     private void init() {  
  24.         final LayoutInflater mLayoutInflater = LayoutInflater.from(getContext());  
  25.         View v = mLayoutInflater.inflate(R.layout.front_part_editable_text, null,false);  
  26.         addView(v);  
  27.           
  28.         edittext = (BackDetectableEditText) v.findViewById(R.id.part_editable_text);  
  29.         // 限定只能输入数字  
  30.         edittext.setInputType(EditorInfo.TYPE_CLASS_NUMBER);  
  31.         // 可以获取焦点  
  32.         button = (Button) v.findViewById(R.id.part_editable_text_dummy);  
  33.         button.setFocusable(true);  
  34.         button.setFocusableInTouchMode(true);  
  35.         // http://stackoverflow.com/questions/3425932/detecting-when-user-has-dismissed-the-soft-keyboard  
  36.         // 拦截返回事件,可以去掉的  
  37.         edittext.setOnEditTextImeBackListener(new BackDetectableEditText.EditTextImeBackListener() {  
  38.             @Override  
  39.             public void onImeBack(BackDetectableEditText ctrl, String text) {  
  40.             }  
  41.         });  
  42.   
  43.         // 一旦获取焦点,设置光标位置  
  44.         edittext.setOnFocusChangeListener(new OnFocusChangeListener() {  
  45.             @Override  
  46.             public void onFocusChange(View v, boolean hasFocus) {  
  47.                 if (hasFocus) {  
  48.                     String mobile = getFrontPart(edittext.getText().toString());  
  49.                     setCursorPosition(mobile.length());  
  50.                 }  
  51.             }  
  52.         });  
  53.   
  54.         // 返回true,手动处理touch事件,即使edittext获取了焦点,也不会自动弹出软键盘,要手动弹出  
  55.         // http://stackoverflow.com/questions/10263384/android-how-to-get-text-position-from-touch-event  
  56.         edittext.setOnTouchListener(new View.OnTouchListener() {  
  57.             @Override  
  58.             public boolean onTouch(View v, MotionEvent event) {  
  59.                 if (event.getAction() == MotionEvent.ACTION_DOWN) {  
  60.                     Layout layout = ((EditText) v).getLayout();  
  61.                     float x = event.getX() + edittext.getScrollX();  
  62.                     int offset = layout.getOffsetForHorizontal(0, x);  
  63.                     if (offset >= 0 && offset < maxFrontPartLength) {  
  64.                         edittext.setSelection(offset);  
  65.                     } else if (offset >= maxFrontPartLength) {  
  66.                         edittext.setSelection(maxFrontPartLength);  
  67.                     }  
  68.                     showSoftInput();  
  69.                 }  
  70.                 return true;  
  71.             }  
  72.         });  
  73.   
  74.         edittext.addTextChangedListener(new TextWatcher() {  
  75.             private String preText;  
  76.   
  77.             @Override  
  78.             public void beforeTextChanged(CharSequence s, int start, int count,  
  79.                     int after) {  
  80.             }  
  81.   
  82.             @Override  
  83.             public void onTextChanged(CharSequence s, int start, int before,  
  84.                     int count) {  
  85.   
  86.             }  
  87.   
  88.             @Override  
  89.             public void afterTextChanged(Editable s) {  
  90.                 if(listener == null){  
  91.                     throw new RuntimeException("BuildTextContentListener can not be empty");  
  92.                 }  
  93.                 String nowtext = listener.buildTextContent(s.toString());  
  94.                 if (nowtext.equals(preText)) {  
  95.                     return;  
  96.                 }  
  97.                 String frontPart = getFrontPart(nowtext);  
  98.                 if(frontPart.length() < maxFrontPartLength -1){  
  99.                     return;  
  100.                 }  
  101.                 // 计算当前的光标位置  
  102.                 int offset = calCursorOffset(preText, nowtext);  
  103.                 // 一定要在setTest之前设置preText,否则会StackOverflow  
  104.                 preText = nowtext;  
  105.                 edittext.setText(nowtext);  
  106.                 // 文字发生变化,重新设置光标,否则会跑到最前面  
  107.                 setCursorPosition(offset);  
  108.                 if (frontPart.length() == maxFrontPartLength) {  
  109.                     hideSoftInput();  
  110.                 }  
  111.                 listener.afterTextChanged(nowtext);  
  112.             }  
  113.         });  
  114.     }  
  115.       
  116.     public void setBuildTextContentListener(BuildTextContentListener listener){  
  117.         this.listener = listener;  
  118.     }  
  119.       
  120.     public interface BuildTextContentListener{  
  121.         public String buildTextContent(String text);  
  122.         public void afterTextChanged(String text);  
  123.     }  
  124.       
  125.     public void setMaxFrontPartLength(int frontPartLength) {  
  126.         this.maxFrontPartLength = frontPartLength;  
  127.     }  
  128.   
  129.     public void hideSoftInput() {  
  130.         edittext.requestFocus();  
  131.         Util.hideKeyboard(edittext);  
  132.         button.requestFocus();  
  133.     }  
  134.   
  135.     public void showSoftInput() {  
  136.         edittext.requestFocus();  
  137.         Util.showKeyboard(edittext);  
  138.     }  
  139.       
  140.     private String getFrontPart(String text) {  
  141.         if (text == null || text.length() <= 0) {  
  142.             return "";  
  143.         }  
  144.         String arr[] = text.split("\\s");  
  145.         String mobile = arr[0];  
  146.         return mobile;  
  147.     }  
  148.   
  149.     private void setCursorPosition(int offset) {  
  150.         edittext.setSelection(offset);  
  151.     }  
  152.       
  153.     private int calCursorOffset(String pre, String now) {  
  154.         if (Util.isBlank(pre) && Util.isBlank(now)) {  
  155.             return 0;  
  156.         } else if (!Util.isBlank(pre) && !Util.isBlank(now)) {  
  157.             for (int i = 0; i < pre.length() && i < now.length(); i++) {  
  158.                 int prechar = pre.charAt(i);  
  159.                 int nowchar = now.charAt(i);  
  160.                 if (prechar != nowchar) {  
  161.                     return i;  
  162.                 }  
  163.             }  
  164.         }  
  165.         return now.length() > maxFrontPartLength ? maxFrontPartLength : now.length();  
  166.     }  
  167. }  

front_part_editable_text.xml:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="wrap_content"  
  5.     android:orientation="vertical"  
  6.     android:id="@+id/part_editable_layout">  
  7.   
  8.     <com.example.edittextdemo.BackDetectableEditText   
  9.         android:id="@+id/part_editable_text"  
  10.         android:layout_width="600dp"  
  11.         android:layout_height="wrap_content"/>  
  12.       
  13.      <Button   
  14.         android:id="@+id/part_editable_text_dummy"  
  15.         android:layout_width="0dp"  
  16.         android:layout_height="0dp"/>  
  17.   
  18. </LinearLayout>  

BackDetectableEditText.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class BackDetectableEditText extends EditText {  
  2.   
  3.     public BackDetectableEditText(Context context) {  
  4.         super(context);  
  5.     }  
  6.   
  7.     public BackDetectableEditText(Context context, AttributeSet attrs) {  
  8.         super(context, attrs);  
  9.     }  
  10.   
  11.     public BackDetectableEditText(Context context, AttributeSet attrs, int defStyle) {  
  12.         super(context, attrs, defStyle);  
  13.     }  
  14.       
  15.     private EditTextImeBackListener mOnImeBack;  
  16.       
  17.     @Override  
  18.     public boolean onKeyPreIme(int keyCode, KeyEvent event) {  
  19.         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {  
  20.             if (mOnImeBack != null){  
  21.                 mOnImeBack.onImeBack(thisthis.getText().toString());  
  22.                 return true;  
  23.             }  
  24.         }  
  25.         return super.dispatchKeyEvent(event);  
  26.     }  
  27.   
  28.     public void setOnEditTextImeBackListener(EditTextImeBackListener listener) {  
  29.         mOnImeBack = listener;  
  30.     }  
  31.       
  32.     public interface EditTextImeBackListener {  
  33.         public abstract void onImeBack(BackDetectableEditText ctrl, String text);  
  34.     }  
  35. }  

Util.java:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class Util {  
  2.     /** 
  3.      * 多次调用不会报错 
  4.      *  
  5.      * */  
  6.     public static void showKeyboard(EditText edittext) {  
  7.         if(edittext == null){  
  8.             return;  
  9.         }  
  10.         try {  
  11.             InputMethodManager imm = (InputMethodManager) edittext.getContext()  
  12.                     .getSystemService(Context.INPUT_METHOD_SERVICE);  
  13.             imm.showSoftInput(edittext, 0);  
  14.         } catch (Exception e) {  
  15.             Log.e("SoftInput:Showing had a wrong.", e.toString());  
  16.         }  
  17.     }  
  18.   
  19.     public static void hideKeyboard(EditText edittext) {  
  20.         if (edittext == null) {  
  21.             return;  
  22.         }  
  23.         try {  
  24.             InputMethodManager imm = ((InputMethodManager) edittext  
  25.                     .getContext().getSystemService(  
  26.                             Activity.INPUT_METHOD_SERVICE));  
  27.             imm.hideSoftInputFromWindow(edittext.getWindowToken(),  
  28.                     InputMethodManager.HIDE_NOT_ALWAYS);  
  29.         } catch (Exception e) {  
  30.             Log.e("SoftInput:Hiding had a wrong.", e.toString());  
  31.         }  
  32.     }  
  33.   
  34.     public static boolean isBlank(String str) {  
  35.         if (str == null || str.length() <= 0) {  
  36.             return true;  
  37.         }  
  38.         return false;  
  39.     }  
  40.   
  41. }  

后来又试出来另一种做法。用两个view。

CombinedEditTextView.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class CombinedEditTextView extends LinearLayout {  
  2.   
  3.     private TextView backTextView;  
  4.     private EditText frontEdittextView;  
  5.     private int frontMaxLength;  
  6.     private TextContentListener listener;  
  7.   
  8.     public CombinedEditTextView(Context context) {  
  9.         super(context);  
  10.         init();  
  11.     }  
  12.   
  13.     public CombinedEditTextView(Context context, AttributeSet attrs) {  
  14.         super(context, attrs);  
  15.         init();  
  16.     }  
  17.   
  18.     public CombinedEditTextView(Context context, AttributeSet attrs, int defStyle) {  
  19.         super(context, attrs, defStyle);  
  20.         init();  
  21.     }  
  22.   
  23.     private void init() {  
  24.         final LayoutInflater mLayoutInflater = LayoutInflater.from(getContext());  
  25.         View v = mLayoutInflater.inflate(R.layout.combined_edit_text_view, null,false);  
  26.         addView(v);  
  27.           
  28.         frontEdittextView = (EditText) v.findViewById(R.id.combined_edit_text);  
  29.         backTextView = (TextView) v.findViewById(R.id.combined_text_view);  
  30.         backTextView.setFocusable(true);  
  31.         backTextView.setFocusableInTouchMode(true);  
  32.           
  33.         frontEdittextView.addTextChangedListener(new TextWatcher() {  
  34.             @Override  
  35.             public void beforeTextChanged(CharSequence s, int start, int count,  
  36.                     int after) {  
  37.             }  
  38.             @Override  
  39.             public void onTextChanged(CharSequence s, int start, int before,  
  40.                     int count) {  
  41.             }  
  42.             @Override  
  43.             public void afterTextChanged(Editable s) {  
  44.                 String frontText = s.toString();  
  45.                 int frontTextLength = frontText.length();  
  46.                 if(frontTextLength < frontMaxLength - 1){  
  47.                     return;  
  48.                 }  
  49.                 String edittext = frontText.trim();  
  50.                 if(edittext.length() >= frontMaxLength){  
  51.                     setBackTextView(edittext);  
  52.                     hideSoftInput();  
  53.                 }else{  
  54.                     clearBackTextView();  
  55.                 }  
  56.                 listener.afterFrontTextChanged(edittext, backTextView.getText().toString());  
  57.             }  
  58.         });  
  59.     }  
  60.       
  61.     //http://stackoverflow.com/questions/5044342/how-to-get-cursor-position-x-y-in-edittext-android  
  62.     private void setBackTextView(String frontText){  
  63.         String backTextContent = listener.getBackTextContent(frontText);  
  64.         if(backTextContent != null && backTextContent.length() > 0){  
  65.             RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)backTextView.getLayoutParams();  
  66.             params.leftMargin = (int)getCursorX() + 20;  
  67.             backTextView.setText(backTextContent);  
  68.             backTextView.setVisibility(View.VISIBLE);  
  69.         }else{  
  70.             clearBackTextView();  
  71.         }  
  72.     }  
  73.       
  74.     private float getCursorX(){  
  75.         String frontTextContent = frontEdittextView.getText().toString();  
  76.         frontEdittextView.setSelection(frontTextContent.length());  
  77.         int pos = frontEdittextView.getSelectionStart();  
  78.         Layout layout = frontEdittextView.getLayout();  
  79.         float x = layout.getPrimaryHorizontal(pos);  
  80.         //int line = layout.getLineForOffset(pos);  
  81.         //int baseline = layout.getLineBaseline(line);  
  82.         //int ascent = layout.getLineAscent(line);  
  83.         //float y = baseline + ascent;  
  84.         return x;  
  85.     }  
  86.       
  87.     private void clearBackTextView(){  
  88.         backTextView.setText("");  
  89.         backTextView.setVisibility(View.GONE);  
  90.     }  
  91.       
  92.     public int getMaxLength() {  
  93.         return frontMaxLength;  
  94.     }  
  95.   
  96.     public void setMaxLength(int maxLength) {  
  97.         this.frontMaxLength = maxLength;  
  98.     }  
  99.   
  100.     public TextContentListener getListener() {  
  101.         return listener;  
  102.     }  
  103.   
  104.     public void setListener(TextContentListener listener) {  
  105.         this.listener = listener;  
  106.     }  
  107.   
  108.   
  109.     public interface TextContentListener{  
  110.         public String getBackTextContent(String frontText);  
  111.         public void afterFrontTextChanged(String frontText,String backText);  
  112.     }  
  113.       
  114.     public void hideSoftInput() {  
  115.         frontEdittextView.requestFocus();  
  116.         Util.hideKeyboard(frontEdittextView);  
  117.         frontEdittextView.clearFocus();  
  118.         backTextView.requestFocus();  
  119.     }  
  120. }  
combined_edit_text_view.xml

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="wrap_content">  
  5.   
  6.     <EditText   
  7.         android:id="@+id/combined_edit_text"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:inputType="number"  
  11.         android:singleLine="true"  
  12.         android:layout_centerVertical="true"  
  13.         android:layout_alignParentLeft="true"/>  
  14.       
  15.      <TextView   
  16.         android:id="@+id/combined_text_view"  
  17.         android:layout_width="wrap_content"  
  18.         android:layout_height="wrap_content"  
  19.         android:layout_centerVertical="true"  
  20.         android:visibility="gone"/>  
  21.   
  22. </RelativeLayout>  

此外,为了防止在进入页面的时候自动弹出软键盘,可以在manifest的activity元素添加<activity  android:windowSoftInputMode="stateAlwaysHidden|adjustPan">

源码:http://download.csdn.net/download/goldenfish1919/6968309


ps:后来做横竖屏装换,切换的时候,页面上的view会被删除然后重新添加上,导致getLayout()出了空指针!

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * @return the Layout that is currently being used to display the text. 
  3.      * This can be null if the text or width has recently changes. 
  4.      */  
  5.     public final Layout getLayout() {  
  6.         return mLayout;  
  7.     }  
getLayout可能会返回null,因此更靠谱的做法是测量输入文字的长度。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint = new Paint();  
  2. int size = res.getDimensionPixelSize(R.dimen.fontsize_18);  
  3. paint.setTextSize(size);  
  4. float textWidth = paint.measureText(mobile);  
走的各种弯路啊!!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序邦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值