EditText,清除重点在外面的触摸
我的布局包含ListView
, SurfaceView
和EditText
。 当我点击EditText
,它会收到焦点,并popup屏幕上的键盘。 当我点击EditText
之外的地方时,它仍然有焦点(不应该)。 我想我可以在布局中的其他视图上设置OnTouchListener
,并手动清除EditText
的焦点。 但似乎太ha
在其他布局列表视图中,也有不同types的项目,其中一些内部有EditText
。 他们的行为就像我上面写的。
当用户触摸外面的东西时,任务就是让EditText
失去焦点。
我在这里看到类似的问题,但还没有find任何解决scheme…
我试过所有这些解决scheme。 edc598's是最接近工作,但触摸事件没有触发其他View
包含在布局。 如果任何人需要这种行为,这就是我最终做的事情:
我创build了一个名为touchInterceptor的(不可见的) FrameLayout
作为布局中的最后一个View
,以便覆盖所有内容( 编辑:您还必须使用RelativeLayout
作为父布局,并赋予touchInterceptor的 fill_parent
属性)。 然后我用它来拦截触摸,并确定触摸是否在EditText
之上:
FrameLayout touchInterceptor = (FrameLayout)findViewById(R.id.touchInterceptor); touchInterceptor.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (mEditText.isFocused()) { Rect outRect = new Rect(); mEditText.getGlobalVisibleRect(outRect); if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) { mEditText.clearFocus(); InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); } } } return false; } });
返回false让触摸处理通过。
这是hacky,但它是唯一的工作对我来说。
Ken的答案是,这是最模块化的复制和粘贴解决scheme。
不需要XML。
把它放在你的活动中,它将应用于所有的EditTexts,包括那个Activity内的片段。
@Override public boolean dispatchTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { View v = getCurrentFocus(); if ( v instanceof EditText) { Rect outRect = new Rect(); v.getGlobalVisibleRect(outRect); if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) { v.clearFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); } } } return super.dispatchTouchEvent( event ); }
对于EditText的父视图,让以下3个属性为“ true ”:
可点击 , 可调焦 , focusableInTouchMode 。
如果一个观点要获得重点,就必须满足这三个条件。
请参阅android.view :
public boolean onTouchEvent(MotionEvent event) { ... if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { ... if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } ... } ... }
希望它有帮助。
肯的答案是有效的,但是这很不好。 正如pcans暗示的答案的评论,同样的事情可以用dispatchTouchEvent完成。 这个解决scheme比较干净,因为它避免了用透明的虚拟FrameLayout来破解XML。 这是这样的:
@Override public boolean dispatchTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (mEditText.isFocused()) { Rect outRect = new Rect(); mEditText.getGlobalVisibleRect(outRect); if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) { mEditText.clearFocus(); // // Hide keyboard // InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); } } } return super.dispatchTouchEvent(event); }
你可能已经find了这个问题的答案,但我一直在寻找如何解决这个问题,仍然不能真正find我正在寻找什么,所以我想我会在这里发布。
我做的是以下(这是非常普遍的,目的是给你一个如何进行的想法,复制和粘贴所有的代码将不会工作O:D):
首先在您的程序中包含EditText和任何其他视图,并将其包含在单个视图中。 在我的情况下,我用LinearLayout来包装一切。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mainLinearLayout"> <EditText android:id="@+id/editText"/> <ImageView android:id="@+id/imageView"/> <TextView android:id="@+id/textView"/> </LinearLayout>
然后在你的代码中,你必须设置一个Touch Listener到你的主LinearLayout。
final EditText searchEditText = (EditText) findViewById(R.id.editText); mainLinearLayout.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub if(searchEditText.isFocused()){ if(event.getY() >= 72){ //Will only enter this if the EditText already has focus //And if a touch event happens outside of the EditText //Which in my case is at the top of my layout //and 72 pixels long searchEditText.clearFocus(); InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); } } Toast.makeText(getBaseContext(), "Clicked", Toast.LENGTH_SHORT).show(); return false; } });
我希望这有助于一些人。 或者至less帮助他们开始解决他们的问题。
当触及其他视图时失去焦点,应将这两个视图设置为view.focusableInTouchMode(true)。
但是,似乎不build议在触摸模式下使用焦点。 请看看这里: http : //android-developers.blogspot.com/2008/12/touch-mode.html
我有一个由EditText
视图组成的ListView
。 该场景说,在编辑一行或多行文本后,我们应该点击一个名为“完成”的button。 我在onFocusChanged
的EditText
视图中使用onFocusChanged
,但点击完成后,数据没有被保存。 问题是通过添加解决的
listView.clearFocus();
在“完成”button的onClickListener
,数据已成功保存。
我真的认为使用getLocationOnScreen
比getGlobalVisibleRect
更强大。 因为我遇到了一个问题。 有一个ListView包含一些Edittext,并在活动中设置一个ajustpan
。 我发现getGlobalVisibleRect
返回一个看起来像包含getGlobalVisibleRect
的值,但event.getRawY总是在屏幕上。 下面的代码运行良好。
public boolean dispatchTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { View v = getCurrentFocus(); if ( v instanceof EditText) { if (!isPointInsideView(event.getRawX(), event.getRawY(), v)) { Log.i(TAG, "!isPointInsideView"); Log.i(TAG, "dispatchTouchEvent clearFocus"); v.clearFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); } } } return super.dispatchTouchEvent( event ); } /** * Determines if given points are inside view * @param x - x coordinate of point * @param y - y coordinate of point * @param view - view object to compare * @return true if the points are within view bounds, false otherwise */ private boolean isPointInsideView(float x, float y, View view) { int location[] = new int[2]; view.getLocationOnScreen(location); int viewX = location[0]; int viewY = location[1]; Log.i(TAG, "location x: " + location[0] + ", y: " + location[1]); Log.i(TAG, "location xWidth: " + (viewX + view.getWidth()) + ", yHeight: " + (viewY + view.getHeight())); // point is inside view bounds return ((x > viewX && x < (viewX + view.getWidth())) && (y > viewY && y < (viewY + view.getHeight()))); }
只要将这些属性放在最顶层的父项中。
android:focusableInTouchMode="true" android:clickable="true" android:focusable="true"
最好的方法是使用默认方法clearFocus()
你知道如何解决onTouchListener
代码吗?
只需调用EditText.clearFocus()
。 它将清除last EditText
焦点。
由于@pcansbuild议您可以在您的活动中重写dispatchTouchEvent(MotionEvent event)
。
在这里,我们得到触摸坐标,并将它们比较来查看边界。 如果在视图之外进行触摸,请执行一些操作。
@Override public boolean dispatchTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { View yourView = (View) findViewById(R.id.view_id); if (yourView != null && yourView.getVisibility() == View.VISIBLE) { // touch coordinates int touchX = (int) event.getX(); int touchY = (int) event.getY(); // get your view coordinates final int[] viewLocation = new int[2]; yourView.getLocationOnScreen(viewLocation); // The left coordinate of the view int viewX1 = viewLocation[0]; // The right coordinate of the view int viewX2 = viewLocation[0] + yourView.getWidth(); // The top coordinate of the view int viewY1 = viewLocation[1]; // The bottom coordinate of the view int viewY2 = viewLocation[1] + yourView.getHeight(); if (!((touchX >= viewX1 && touchX <= viewX2) && (touchY >= viewY1 && touchY <= viewY2))) { Do what you want... // If you don't want allow touch outside (for example, only hide keyboard or dismiss popup) return false; } } } return super.dispatchTouchEvent(event); }
另外,如果活动的布局在运行时不发生变化(例如,您不添加碎片或从布局中replace/删除视图),则不需要检查视图的存在和可见性。 但是,如果您想要closures(或者做类似的事情)自定义的上下文菜单(如在谷歌播放商店使用项目的溢出菜单时),有必要检查视图存在。 否则,你会得到一个NullPointerException
。
只需将该EditText
的父项的两个属性定义为:
android:clickable="true" android:focusableInTouchMode="true"
因此,当用户将触摸EditText
区域之外时,焦点将被移除,因为焦点将被转移到父视图。