Android-部分可编辑的EditText
2014-02-20 14:20
302 查看
有一个需求是这样的,页面上有一个输入框,供用户输入手机号码,如果通讯录里面存在这个号码,会自动把名字追加到号码后面。这个需求变态的地方在于,假如用一个EditText+TextView,那么不好控制二者之间的距离,就算是做了各种适配,但是用户可以设置系统的字体,仍然显示很难看!没办法,之好在一个EditText里面来做,让号码是可编辑的,名字是自动追加上的。
MainActivity.java:
activity_main.xml:
FrontPartEditableText.java:
front_part_editable_text.xml:
BackDetectableEditText.java
Util.java:
后来又试出来另一种做法。用两个view。
CombinedEditTextView.java
此外,为了防止在进入页面的时候自动弹出软键盘,可以在manifest的activity元素添加<activity android:windowSoftInputMode="stateAlwaysHidden|adjustPan">
源码:http://download.csdn.net/download/goldenfish1919/6968309
ps:后来做横竖屏装换,切换的时候,页面上的view会被删除然后重新添加上,导致getLayout()出了空指针!
MainActivity.java:
public class MainActivity extends Activity { private FrontPartEditableText edittext; private CombinedEditTextView edittext2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //方法一:很挫,效率很低 final View rootView = this.findViewById(R.id.rootview); edittext = (FrontPartEditableText) this.findViewById(R.id.edittext1); edittext.setMaxFrontPartLength(11); edittext.setBuildTextContentListener(new FrontPartEditableText.BuildTextContentListener(){ @Override public String buildTextContent(String text) { if(text == null){ return ""; } String arr[] = text.split("\\s+"); String mobile = arr[0]; String name = getName(mobile); return mobile + (name == null ? "" : " " + name); } @Override public void afterTextChanged(String text) { Log.e("test",text); } }); // 空白处点击,隐藏软键盘 rootView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { edittext.hideSoftInput(); } }); //方法二:推荐 edittext2 = (CombinedEditTextView) this.findViewById(R.id.edittext2); edittext2.setMaxLength(11); edittext2.setListener(new CombinedEditTextView.TextContentListener() { @Override public String getBackTextContent(String frontText) { return getName(frontText); } @Override public void afterFrontTextChanged(String text1, String text2) { Log.e("test", "text1:"+text1+",text2:"+text2); } }); } // 业务方法,联系人数据 private Map<String, String> data; private String getName(String mobile) { if (data == null) { data = contactData(); } return data.get(mobile); } private Map<String, String> contactData() { Map<String, String> data = new HashMap<String, String>(); data.put("15012341234", "张三"); data.put("15112341234", "李四"); data.put("15212341234", "王五"); return data; } }
activity_main.xml:
<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="wrap_content" android:orientation="vertical" android:id="@+id/rootview"> <com.example.edittextdemo.FrontPartEditableText android:id="@+id/edittext1" android:layout_width="match_parent" android:layout_height="wrap_content"/> <com.example.edittextdemo.CombinedEditTextView android:id="@+id/edittext2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="50dp"/> </LinearLayout>
FrontPartEditableText.java:
public class FrontPartEditableText extends LinearLayout { private Button button; private int maxFrontPartLength; private BackDetectableEditText edittext; private BuildTextContentListener listener; public FrontPartEditableText(Context context) { super(context); init(); } public FrontPartEditableText(Context context, AttributeSet attrs) { super(context, attrs); init(); } public FrontPartEditableText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { final LayoutInflater mLayoutInflater = LayoutInflater.from(getContext()); View v = mLayoutInflater.inflate(R.layout.front_part_editable_text, null,false); addView(v); edittext = (BackDetectableEditText) v.findViewById(R.id.part_editable_text); // 限定只能输入数字 edittext.setInputType(EditorInfo.TYPE_CLASS_NUMBER); // 可以获取焦点 button = (Button) v.findViewById(R.id.part_editable_text_dummy); button.setFocusable(true); button.setFocusableInTouchMode(true); // http://stackoverflow.com/questions/3425932/detecting-when-user-has-dismissed-the-soft-keyboard // 拦截返回事件,可以去掉的 edittext.setOnEditTextImeBackListener(new BackDetectableEditText.EditTextImeBackListener() { @Override public void onImeBack(BackDetectableEditText ctrl, String text) { } }); // 一旦获取焦点,设置光标位置 edittext.setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { String mobile = getFrontPart(edittext.getText().toString()); setCursorPosition(mobile.length()); } } }); // 返回true,手动处理touch事件,即使edittext获取了焦点,也不会自动弹出软键盘,要手动弹出 // http://stackoverflow.com/questions/10263384/android-how-to-get-text-position-from-touch-event edittext.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { Layout layout = ((EditText) v).getLayout(); float x = event.getX() + edittext.getScrollX(); int offset = layout.getOffsetForHorizontal(0, x); if (offset >= 0 && offset < maxFrontPartLength) { edittext.setSelection(offset); } else if (offset >= maxFrontPartLength) { edittext.setSelection(maxFrontPartLength); } showSoftInput(); } return true; } }); edittext.addTextChangedListener(new TextWatcher() { private String preText; @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if(listener == null){ throw new RuntimeException("BuildTextContentListener can not be empty"); } String nowtext = listener.buildTextContent(s.toString()); if (nowtext.equals(preText)) { return; } String frontPart = getFrontPart(nowtext); if(frontPart.length() < maxFrontPartLength -1){ return; } // 计算当前的光标位置 int offset = calCursorOffset(preText, nowtext); // 一定要在setTest之前设置preText,否则会StackOverflow preText = nowtext; edittext.setText(nowtext); // 文字发生变化,重新设置光标,否则会跑到最前面 setCursorPosition(offset); if (frontPart.length() == maxFrontPartLength) { hideSoftInput(); } listener.afterTextChanged(nowtext); } }); } public void setBuildTextContentListener(BuildTextContentListener listener){ this.listener = listener; } public interface BuildTextContentListener{ public String buildTextContent(String text); public void afterTextChanged(String text); } public void setMaxFrontPartLength(int frontPartLength) { this.maxFrontPartLength = frontPartLength; } public void hideSoftInput() { edittext.requestFocus(); Util.hideKeyboard(edittext); button.requestFocus(); } public void showSoftInput() { edittext.requestFocus(); Util.showKeyboard(edittext); } private String getFrontPart(String text) { if (text == null || text.length() <= 0) { return ""; } String arr[] = text.split("\\s"); String mobile = arr[0]; return mobile; } private void setCursorPosition(int offset) { edittext.setSelection(offset); } private int calCursorOffset(String pre, String now) { if (Util.isBlank(pre) && Util.isBlank(now)) { return 0; } else if (!Util.isBlank(pre) && !Util.isBlank(now)) { for (int i = 0; i < pre.length() && i < now.length(); i++) { int prechar = pre.charAt(i); int nowchar = now.charAt(i); if (prechar != nowchar) { return i; } } } return now.length() > maxFrontPartLength ? maxFrontPartLength : now.length(); } }
front_part_editable_text.xml:
<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="wrap_content" android:orientation="vertical" android:id="@+id/part_editable_layout"> <com.example.edittextdemo.BackDetectableEditText android:id="@+id/part_editable_text" android:layout_width="600dp" android:layout_height="wrap_content"/> <Button android:id="@+id/part_editable_text_dummy" android:layout_width="0dp" android:layout_height="0dp"/> </LinearLayout>
BackDetectableEditText.java
public class BackDetectableEditText extends EditText { public BackDetectableEditText(Context context) { super(context); } public BackDetectableEditText(Context context, AttributeSet attrs) { super(context, attrs); } public BackDetectableEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } private EditTextImeBackListener mOnImeBack; @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { if (mOnImeBack != null){ mOnImeBack.onImeBack(this, this.getText().toString()); return true; } } return super.dispatchKeyEvent(event); } public void setOnEditTextImeBackListener(EditTextImeBackListener listener) { mOnImeBack = listener; } public interface EditTextImeBackListener { public abstract void onImeBack(BackDetectableEditText ctrl, String text); } }
Util.java:
public class Util { /** * 多次调用不会报错 * * */ public static void showKeyboard(EditText edittext) { if(edittext == null){ return; } try { InputMethodManager imm = (InputMethodManager) edittext.getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(edittext, 0); } catch (Exception e) { Log.e("SoftInput:Showing had a wrong.", e.toString()); } } public static void hideKeyboard(EditText edittext) { if (edittext == null) { return; } try { InputMethodManager imm = ((InputMethodManager) edittext .getContext().getSystemService( Activity.INPUT_METHOD_SERVICE)); imm.hideSoftInputFromWindow(edittext.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } catch (Exception e) { Log.e("SoftInput:Hiding had a wrong.", e.toString()); } } public static boolean isBlank(String str) { if (str == null || str.length() <= 0) { return true; } return false; } }
后来又试出来另一种做法。用两个view。
CombinedEditTextView.java
public class CombinedEditTextView extends LinearLayout { private TextView backTextView; private EditText frontEdittextView; private int frontMaxLength; private TextContentListener listener; public CombinedEditTextView(Context context) { super(context); init(); } public CombinedEditTextView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CombinedEditTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { final LayoutInflater mLayoutInflater = LayoutInflater.from(getContext()); View v = mLayoutInflater.inflate(R.layout.combined_edit_text_view, null,false); addView(v); frontEdittextView = (EditText) v.findViewById(R.id.combined_edit_text); backTextView = (TextView) v.findViewById(R.id.combined_text_view); backTextView.setFocusable(true); backTextView.setFocusableInTouchMode(true); frontEdittextView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { String frontText = s.toString(); int frontTextLength = frontText.length(); if(frontTextLength < frontMaxLength - 1){ return; } String edittext = frontText.trim(); if(edittext.length() >= frontMaxLength){ setBackTextView(edittext); hideSoftInput(); }else{ clearBackTextView(); } listener.afterFrontTextChanged(edittext, backTextView.getText().toString()); } }); } //http://stackoverflow.com/questions/5044342/how-to-get-cursor-position-x-y-in-edittext-android private void setBackTextView(String frontText){ String backTextContent = listener.getBackTextContent(frontText); if(backTextContent != null && backTextContent.length() > 0){ RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)backTextView.getLayoutParams(); params.leftMargin = (int)getCursorX() + 20; backTextView.setText(backTextContent); backTextView.setVisibility(View.VISIBLE); }else{ clearBackTextView(); } } private float getCursorX(){ String frontTextContent = frontEdittextView.getText().toString(); frontEdittextView.setSelection(frontTextContent.length()); int pos = frontEdittextView.getSelectionStart(); Layout layout = frontEdittextView.getLayout(); float x = layout.getPrimaryHorizontal(pos); //int line = layout.getLineForOffset(pos); //int baseline = layout.getLineBaseline(line); //int ascent = layout.getLineAscent(line); //float y = baseline + ascent; return x; } private void clearBackTextView(){ backTextView.setText(""); backTextView.setVisibility(View.GONE); } public int getMaxLength() { return frontMaxLength; } public void setMaxLength(int maxLength) { this.frontMaxLength = maxLength; } public TextContentListener getListener() { return listener; } public void setListener(TextContentListener listener) { this.listener = listener; } public interface TextContentListener{ public String getBackTextContent(String frontText); public void afterFrontTextChanged(String frontText,String backText); } public void hideSoftInput() { frontEdittextView.requestFocus(); Util.hideKeyboard(frontEdittextView); frontEdittextView.clearFocus(); backTextView.requestFocus(); } }combined_edit_text_view.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/combined_edit_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="number" android:singleLine="true" android:layout_centerVertical="true" android:layout_alignParentLeft="true"/> <TextView android:id="@+id/combined_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:visibility="gone"/> </RelativeLayout>
此外,为了防止在进入页面的时候自动弹出软键盘,可以在manifest的activity元素添加<activity android:windowSoftInputMode="stateAlwaysHidden|adjustPan">
源码:http://download.csdn.net/download/goldenfish1919/6968309
ps:后来做横竖屏装换,切换的时候,页面上的view会被删除然后重新添加上,导致getLayout()出了空指针!
/** * @return the Layout that is currently being used to display the text. * This can be null if the text or width has recently changes. */ public final Layout getLayout() { return mLayout; }getLayout可能会返回null,因此更靠谱的做法是测量输入文字的长度。
Paint paint = new Paint(); int size = res.getDimensionPixelSize(R.dimen.fontsize_18); paint.setTextSize(size); float textWidth = paint.measureText(mobile);走的各种弯路啊!!
相关文章推荐
- Android四大组件之service
- Android Service 要点总结
- 基于 Android NDK
- Android开发规范
- Android性能优化总结
- android 页面停几秒后跳转
- ubuntu12.04下调试Android真机连接ADB的方法
- Android之drawable state各个属性详解
- Android开发规范
- Android动态加载jar、apk的实现
- SearchView的应用,为自定义adapter设计搜索TextFilter
- Android使用DexClassLoader动态加载未安装Apk
- android解决socket接收时候的中文名乱码
- android webview js交互, 响应webview中的图片点击事件
- Android国际化---Java国际化
- Android开发规范详解
- Android中的DDMS进行调试
- android开发文件转载
- Android中的Button控件
- 我的chromium for Android编译环境搭建