Android系统插上鼠标后会在屏幕显示系统光标,这套光标是无法改变的,因此在开发的过程中可能互隐藏这个光标,并且在光标碰触到手机的最上边或者最下边会触发显示状态栏,这样我们的app全屏的时候就无法获得我们app的焦点,从而我们的开发无法获取边缘地带,下面会一一的分析出来:直接上解决方法,然后在分析其他的接口的使用情况,上述情况如下图所示:
一、解决方案
采用setOnCapturedPointerListener的方案代码如下:
public class MainActivity extends Activity
{// implements View.OnGenericMotionListener, View.OnTouchListener,View.OnSystemUiVisibilityChangeListener
View controllerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);//设置无抬头标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置全屏
setContentView(R.layout.activity_main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
controllerView = findViewById(R.id.controller_view);
// controllerView.setOnTouchListener(this);
// controllerView.setOnGenericMotionListener(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//只有版本大于O的才能使用这样一接口
// The view must be focusable for pointer capture to work.
controllerView.setFocusable(true);
controllerView.setDefaultFocusHighlightEnabled(false);
Log.i("MyTouchTest", "SDK_INT: ");
controllerView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
@Override
public boolean onCapturedPointer(View view, MotionEvent event) {
Log.d("MyTouchTest", "onCapturedPointer: " + event.getAction() + " " + event.getSource());
return true;
//return handleMotionEvent(motionEvent);
}
});
}
Handler h = new Handler();
h.postDelayed(new Runnable() {
@TargetApi(Build.VERSION_CODES.O)
@Override
public void run() {
controllerView.requestPointerCapture();
}
}, 500);//这个线程延时很重要,不做延时或者延时时间短是无法实现view的onCapturedPointer事件监听的
}
}
此处也可一使用监听重写的方式来实现如下所示:
public class MainActivity extends Activity implements View.OnCapturedPointerListener{
..............
//在onCreate内使用监听
controllerView.setOnCapturedPointerListener(this);
................
}
然后重写方法如下:
@Override
public boolean onCapturedPointer(View view, MotionEvent event) {
Log.d("MyTouchTest", "onCapturedPointer: " + event.getAction() + " " + event.getSource());
return true;
//return handleMotionEvent(motionEvent);
}
上面的问题解决完了后还有一个问题,就是当我们的app最小化或者处于后台运行的时候,然后在切回我们的app,我们的app就会失去焦点,继而系统鼠标光标就会再次出现,因此需要override一个方法onWindowFocusChanged,重新获取焦点,代码如下:
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Capture is lost when focus is lost, so it must be requested again
// when focus is regained.
// Recapture the pointer if focus was regained. On Android Q,
// we have to delay a bit before requesting capture because otherwise
// we'll hit the "requestPointerCapture called for a window that has no focus"
// error and it will not actually capture the cursor.
Handler h = new Handler();
h.postDelayed(new Runnable() {
@TargetApi(Build.VERSION_CODES.O)
@Override
public void run() {
controllerView.requestPointerCapture();
}
}, 500);
}
}
其实有了这个方法上面的onCreate内的线程延时触发可是不需要使用,但安全保障还是要使用;
此处给一个简单的xml
这个方法不仅隐藏系统鼠标光标,还不会触发状态栏的显示,从鼠标的角度看彻底隐藏了状态栏,但是通过触摸屏的下拉还是可以显示状态栏的。
关于状态栏的隐藏的方法可以参考地址:https://blog.csdn.net/bzlj2912009596/article/details/78598810
1、在AndroidManifest.xml文件中修改theme为android:theme=”@android:style/Theme.NoTitleBar.Fullscreen”
2、在setContentView方法前执行如下代码:
requestWindowFeature(Window.FEATURE_NO_TITLE)
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
3、通过View的setSystemUiVisibility方法
4、通过如下代码实现状态栏的隐藏和显示:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) //隐藏状态栏
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) //显示状态栏
推荐尝试使用第三种方法setSystemUiVisibility,其他的方法属于假隐藏;
二、其他采集鼠标的位置的接口
其他接口都无法隐藏系统光标,继而也会触发状态栏的显示,在此做个记录
//1、dispatch方法是最底层的,使用后其他方法是无法使用的
//用来采集鼠标的motion,但左键按下拖动时是无法采集到motion的,只能采集到左键的
//MotionEvent.ACTION_BUTTON_PRESS和MotionEvent.ACTION_BUTTON_RELEASE,这种状况motion只能到
//dispatchTouchEvent或者onTouch去采集,而右击和中键事件也不在这个方法内检测而是在键盘的检测方法
//内,即onKeyDown和onKeyUp内的KEYCODE_BACK和KEYCODE_MENU,但是右击滑动的motion和中键滚轮的滑动
//是在此方法内
@Override
public boolean dispatchGenericMotionEvent (MotionEvent event)/
{
Log.i(" MyTouchTest", "dispatchGenericMotionEvent: ");
.......................
return false;
}
//需要注意的是方法的返回值,是false和true的区别很大,牵涉导一些事件是否能检测的到,
//如果有的事件检测不到,可以修改返回值试一试;
@Override
public boolean dispatchTouchEvent(final MotionEvent event) //触摸屏的motion采集
{
Log.i(" MyTouchMouse", "MyTouchMouse: action point " + event.getAction() + " " + event.getPointerCount());
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
............
}
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
............
}
............
return true;//不返回true,无法检测导双指按下
}
//2、继承监听方法
//需要view的监听,必须在activity引用
public class MainActivity extends Activity implements View.OnGenericMotionListener, View.OnTouchListener
//然后在onCreate内启用监听
controllerView.setOnTouchListener(this);
controllerView.setOnGenericMotionListener(this);
//然后override方法
@Override
public boolean onGenericMotionEvent(MotionEvent event) {//mouse event
........
return true;
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return false;
}
//3、还有个类似第一种方案的方法,用法和1一样
@Override
public boolean onGenericMotion(View view, MotionEvent motionEvent) {
return false;
}