编辑文本,清晰地聚焦于触摸外部

我的布局包括 ListViewSurfaceViewEditText。当我点击 EditText,它接收焦点和屏幕上的键盘弹出。当我点击 EditText之外的某个地方时,它仍然有焦点(它不应该有焦点)。 我想我可以设置 OnTouchListener的其他视图在布局和手动清除的 EditText的焦点。但是看起来太粗俗了。

我也有同样的情况在其他布局列表视图与不同类型的项目,其中一些有 EditText的内部。他们的行为就像我上面写的一样。

任务是使 EditText失去焦点时,用户触摸外面的东西。

我在这里看到过类似的问题,但没有找到任何解决办法..。

140507 次浏览

为了在触摸其他视图时失去焦点,两个视图都应该设置为 view.focus usableInTouchMode (true)。

但似乎不推荐在触摸模式下使用聚焦。 请看这里: Http://android-developers.blogspot.com/2008/12/touch-mode.html

对于 EditText 的父视图,将以下3个属性设置为“ 没错”:
可点击 集中注意力可聚焦触摸模式

如果一个视图想要获得焦点,它必须满足以下三个条件。

安卓,视图:

public boolean onTouchEvent(MotionEvent event) {
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
...
if (isFocusable() && isFocusableInTouchMode()
&& !isFocused()) {
focusTaken = requestFocus();
}
...
}
...
}

希望能有帮助。

你可能已经找到了这个问题的答案,但我一直在寻找如何解决这个问题,仍然不能真正找到我正在寻找的,所以我想我会张贴在这里。

我所做的如下(这是非常广义的,目的是给你一个如何进行的想法,复制和粘贴所有的代码将不会工作 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;
}
});

我希望这能帮助一些人,或者至少帮助他们开始解决自己的问题。

我尝试了所有这些解决方案。Edc598是最接近工作的,但是触摸事件不会触发布局中包含的其他 View。如果有人需要这种行为,我最后会这么做:

我创建了一个(不可见的) FrameLayout,称为 触摸拦截器,作为布局中的最后一个 View,以便覆盖所有内容(编辑:还必须使用 RelativeLayout作为父布局,并赋予 触摸拦截器 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;
}
});

还假让触摸操作失败。

虽然很俗气,但这是唯一对我有效的方法。

我有一个由 EditText视图组成的 ListView。该场景说,在编辑一行或多行中的文本后,我们应该点击一个名为“ Finish”的按钮。我在 listView内部的 EditText视图中使用了 onFocusChanged,但是点击完成后数据没有被保存。这个问题通过加法解决了

listView.clearFocus();

onClickListener的“完成”按钮和数据保存成功。

肯恩的回答很有效,但是很粗俗。正如 pcan 在答案的注释中所暗示的那样,也可以对 patchTouchEvent 进行同样的操作。这个解决方案更简洁,因为它避免了使用透明的、虚拟的 FrameLayout 破解 XML。下面是它看起来的样子:

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
EditText mEditText = findViewById(R.id.mEditText);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
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);
}

最好的方法是使用默认方法 clearFocus()

你知道怎么用 onTouchListener解密码吧?

只要拨打 EditText.clearFocus()。它将清除在 last EditText的焦点。

基于 Ken 的回答,下面是最模块化的复制粘贴解决方案。

不需要 XML。

把它放到你的活动中,它会应用到所有的 EditText,包括那些活动中的片段。

@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 );
}

正如@pcan 建议的那样,您可以在活动中重写 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);
}

此外,如果活动的布局在运行时没有改变,也没有必要检查视图的存在性和可见性(例如,你没有添加片段或者从布局中替换/删除视图)。但是如果你想关闭(或者做类似的事情)自定义上下文菜单(比如在 Google Play Store 中使用项目的溢出菜单) ,就需要检查视图是否存在。否则你会得到一个 NullPointerException

我真的认为使用 getLocationOnScreen比使用 getGlobalVisibleRect更健壮。因为我遇到了一个问题。有一个 Listview,它包含一些 Edittext 并在活动中设置 ajustpan。我发现 getGlobalVisibleRect返回一个类似于包含 scrollY 的值,但 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())));
}

只需将该 EditText的父级的两个属性定义为:

android:clickable="true"
android:focusableInTouchMode="true"

因此,当用户将触摸外面的 EditText区域,焦点将被删除,因为焦点将转移到父视图。

只要把这些属性放在最上面的父级。

android:focusableInTouchMode="true"
android:clickable="true"
android:focusable="true"

对我来说,下面的事情工作-

1. 在 ABC3. e. ABC4的 ABC2中加入 ABC0和 android:focusableInTouchMode="true"

<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusableInTouchMode="true">
<EditText
android:id="@+id/employeeID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:hint="Employee ID"
tools:layout_editor_absoluteX="-62dp"
tools:layout_editor_absoluteY="16dp"
android:layout_marginTop="42dp"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="36dp"
android:layout_marginEnd="36dp" />
</android.support.design.widget.TextInputLayout>

2. 在 Activity 类中重写 ABC0并插入 hideKeyboard()函数

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View view = getCurrentFocus();
if (view != null && view instanceof EditText) {
Rect r = new Rect();
view.getGlobalVisibleRect(r);
int rawX = (int)ev.getRawX();
int rawY = (int)ev.getRawY();
if (!r.contains(rawX, rawY)) {
view.clearFocus();
}
}
}
return super.dispatchTouchEvent(ev);
}


public void hideKeyboard(View view) {
InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

3. 为 EditText 添加 setOnFocusChangeListener

EmployeeId.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard(v);
}
}
});

这个简单的代码片段可以满足您的需要

GestureDetector gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
KeyboardUtil.hideKeyboard(getActivity());
return true;
}
});
mScrollView.setOnTouchListener((v, e) -> gestureDetector.onTouchEvent(e));

在 Kotlin

Hide 键盘() 是一个 Kotlin 扩展

fun Activity.hideKeyboard() {
hideKeyboard(currentFocus ?: View(this))
}

在活动中添加 事件

override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
val v: View? = currentFocus
if (v is EditText) {
val outRect = Rect()
v.getGlobalVisibleRect(outRect)
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
v.clearFocus()
hideKeyboard()
}
}
}
return super.dispatchTouchEvent(event)
}


将这些属性添加到最上面的父级中

android:focusableInTouchMode="true"
android:focusable="true"

这是我的版本基于 zMan 的代码。如果下一个视图也是编辑文本,则不会隐藏键盘。如果用户只是滚动屏幕,它也不会隐藏键盘。

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
downX = (int) event.getRawX();
}


if (event.getAction() == MotionEvent.ACTION_UP) {
View v = getCurrentFocus();
if (v instanceof EditText) {
int x = (int) event.getRawX();
int y = (int) event.getRawY();
//Was it a scroll - If skip all
if (Math.abs(downX - x) > 5) {
return super.dispatchTouchEvent(event);
}
final int reducePx = 25;
Rect outRect = new Rect();
v.getGlobalVisibleRect(outRect);
//Bounding box is to big, reduce it just a little bit
outRect.inset(reducePx, reducePx);
if (!outRect.contains(x, y)) {
v.clearFocus();
boolean touchTargetIsEditText = false;
//Check if another editText has been touched
for (View vi : v.getRootView().getTouchables()) {
if (vi instanceof EditText) {
Rect clickedViewRect = new Rect();
vi.getGlobalVisibleRect(clickedViewRect);
//Bounding box is to big, reduce it just a little bit
clickedViewRect.inset(reducePx, reducePx);
if (clickedViewRect.contains(x, y)) {
touchTargetIsEditText = true;
break;
}
}
}
if (!touchTargetIsEditText) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}
}
return super.dispatchTouchEvent(event);
}