前面做简单分析,后面附完整代码。
需求:
如标题,安卓app中点击非输入控件时,假若软键盘是打开的,则自动隐藏软键盘。
在activity中重写dispatchTouchEvent(MotionEvent ev)函数
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
if (ev.getAction() == MotionEvent.ACTION_DOWN)
{
View v = getCurrentFocus();
hideSoftInput(v, ev);
return super.dispatchTouchEvent(ev);
}
// 必不可少,否则所有的组件都不会有TouchEvent了
if (activity.getWindow().superDispatchTouchEvent(ev))
{
return true;
}
return activity.onTouchEvent(ev);
}
private void hideSoftInput(View v, MotionEvent ev)
{
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null && imm.isActive())
{
if (isShouldHideInput(v, ev))
{
if (v == null)
{
v = getWindow().getDecorView();
}
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}
/**
* 判断是否要隐藏软键盘。
* 除了EditText外,点击了其他地方,都要隐藏。
* @param v
* @param event
* @return
*/
public boolean isShouldHideInput(View v, MotionEvent event)
{
if (v != null && (v instanceof EditText))
{
return !isPointInView(v, (int)event.getX(), (int)event.getY());
}
return true;
}
/**
* 判断位置(x, y)是否在view的显示范围内
* @param view
* @param x
* @param y
* @return
*/
public boolean isPointInView(View view, int x, int y)
{
int[] location = new int[2];
// 点击事件获取的坐标为相对于window(如果activity是全屏的,Screen和Window一样)
// view.getLocationOnScreen(location);
view.getLocationInWindow(location);
int left = location[0];
int top = location[1];
int right = left + view.getMeasuredWidth();
int bottom = top + view.getMeasuredHeight();
if (
y>=top && y<=bottom
&& x>=left && x<=right
)
{
return true;
}
return false;
}
该代码正常可以使用。
但如果屏幕中有多个EditText,当软键盘已开启,此时点击另外EditText时,软键盘会先隐藏再出现。主要是如下代码
View v = getCurrentFocus();
hideSoftInput(v, ev);
获取的 View v 仍在上一个 EditText中。
----------------------------- 你爱管不管的分割线 -----------------------------
为了不出现以上现象,同时对代码做封装,添加两个类
类一: ViewGetter.java
import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
/**
* @desc 通过点击的坐标(x, y)获取相应的View
* @auth jsf
* @time 2018/10/24 16:39
*/
public class ViewGetter
{
private Activity activity;
public ViewGetter(Activity pActivity)
{
activity = pActivity;
}
/**
* 通过点击的坐标(x, y)获取相应的View
* @param x
* @param y
* @return
*/
public View getView(int x, int y)
{
if (activity == null)
{
return null;
}
View root = activity.getWindow().getDecorView();
return findViewByXY(root, x, y);
}
/**
* 遍历所有view,返回触摸位置(x, y)在某个view的显示范围内的view
* @param view
* @param x
* @param y
* @return
*/
private View findViewByXY(View view, int x, int y)
{
if (view instanceof ViewGroup)
{
ViewGroup v = (ViewGroup)view;
for (int i=0; i<v.getChildCount();i++)
{
View targetView = findViewByXY(v.getChildAt(i), x, y);
if (targetView != null)
{
return targetView;
}
}
}
else
{
if (isPointInView(view, x, y))
{
return view;
}
}
return null;
}
/**
* 判断位置(x, y)是否在view的显示范围内
* @param view
* @param x
* @param y
* @return
*/
public boolean isPointInView(View view, int x, int y)
{
int[] location = new int[2];
// 点击事件获取的坐标为相对于window(如果activity是全屏的,Screen和Window一样)
// view.getLocationOnScreen(location);
view.getLocationInWindow(location);
int left = location[0];
int top = location[1];
int right = left + view.getMeasuredWidth();
int bottom = top + view.getMeasuredHeight();
if (
y>=top && y<=bottom
&& x>=left && x<=right
)
{
return true;
}
return false;
}
}
类二: HideSoftInputByTouch.java
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
/**
* @desc
* @auth jsf
* @time 2018/10/24 17:10
*/
public class HideSoftInputByTouch
{
private final String TAG = HideSoftInputByTouch.class.getSimpleName();
private Activity activity;
IDispatchTouchEvent dispatchTouchEvent;
ViewGetter viewGetter;
/**
* 通过注入的方式,调用 Activity 父类的 dispatchTouchEvent 函数
*/
public interface IDispatchTouchEvent
{
boolean dispatchTouchEvent(MotionEvent ev);
}
public void setDispatchTouchEvent(IDispatchTouchEvent dispatchTouchEvent)
{
this.dispatchTouchEvent = dispatchTouchEvent;
}
public HideSoftInputByTouch(Activity pActivity)
{
activity = pActivity;
viewGetter = new ViewGetter(activity);
}
public boolean dispatchTouchEvent(MotionEvent ev)
{
if (ev.getAction() == MotionEvent.ACTION_DOWN)
{
// 如果通过 getCurrentFocus() 获取,假设原先焦点已在EditText上,再次点击时,会获取到原先焦点所在的EditText
// 因此会先隐藏软键盘,然后再显示,体验不好。
// 所以获取点击所在位置的view
// View v = getCurrentFocus();
View v = viewGetter.getView((int)ev.getX(), (int)ev.getY());
hideSoftInput(v, ev);
// 正常可以使用,
// 但用该函数,假如activity的主题为Theme.AppCompat.Light.NoActionBar,
// 并设置了Activity为对话框形式,且可通过 setFinishOnTouchOutside(true); 设置点击对话框外部关闭activity,
// 则可点击无效
// return activity.getWindow().superDispatchTouchEvent(ev);
if (dispatchTouchEvent != null)
{
return dispatchTouchEvent.dispatchTouchEvent(ev);
}
}
// 必不可少,否则所有的组件都不会有TouchEvent了
if (activity.getWindow().superDispatchTouchEvent(ev))
{
return true;
}
return activity.onTouchEvent(ev);
}
private void hideSoftInput(View v, MotionEvent ev)
{
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null && imm.isActive())
{
if (isShouldHideInput(v, ev))
{
if (v == null)
{
v = activity.getWindow().getDecorView();
}
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}
/**
* 判断是否要隐藏软键盘。
* 除了EditText外,点击了其他地方,都要隐藏。
* @param v
* @param event
* @return
*/
public boolean isShouldHideInput(View v, MotionEvent event)
{
if (v != null && (v instanceof EditText))
{
return !viewGetter.isPointInView(v, (int)event.getX(), (int)event.getY());
}
return true;
}
}
使用方法:
1、设置接口HideSoftInputByTouch.IDispatchTouchEvent();
2、重写 dispatchTouchEvent(MotionEvent ev) 函数。
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import com.gree.gongyi.washer.client.tools.HideSoftInputByTouch;
/**
* @desc 点击EditText外的区域,隐藏软键盘
* @auth jsf
* @time 2018/10/24 11:26
*/
public class TestActivity extends AppCompatActivity
{
HideSoftInputByTouch hideSoftInputByTouch;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
hideSoftInputByTouch = new HideSoftInputByTouch(this);
hideSoftInputByTouch.setDispatchTouchEvent(new HideSoftInputByTouch.IDispatchTouchEvent()
{
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
return HideSoftInputActivity.super.dispatchTouchEvent(ev);
}
});
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return hideSoftInputByTouch.dispatchTouchEvent(ev);
}
}
为了调用 Activity 父类 的 dispatchTouchEvent(ev),使用接口进行注入,不清楚是否有其他更便捷的方式,还请指出,感谢!
为了更好的了解点击安卓屏幕后发生了些什么事,建议看下下面的文章
图解 Android 事件分发机制
安卓事件分发学习之dispatchTouchEvent方法
结束。
网友评论