Android TV 悬浮球模拟物理按键
来源:互联网 发布:搜房网网络摇号直播间 编辑:程序博客网 时间:2024/04/27 21:29
最近在体验实习的时候做了一个TV的内存管家,其中有个要求是实现一个悬浮球,模拟TV控制器的按键,实现上下左右,back,menu,home等效果,并且做一个火箭升空的效果。这时候才发现网上有关tv开发的资料十分少,不像手机端,一搜堆博客。一怒之下决定讲讲其实现
1、悬浮球实现
在Android中实现悬浮球效果比较简单,只要调用windowManager的addView方法即可。有这样一个需求:默认是一个小的view,只显示内存使用情况;当其被点击的时候会切换为大的view,即模拟遥控器的控制界面,点击外部又会切换为小的view,当移动悬浮球的时候会显示一个火箭,同时中下部会显示一个发射台,当移动火箭到发射台的时候放手,火箭升空,否则移动到最近的屏幕边缘。
显然需要三个view:
- 显示内存和小火箭的FloatBallSmallView
- 显示控制界面的FloatBallBigView
- 显示发射台的RocketLauncher
主要实现思路
(1)分别创建FloatBallBigView、FloatBallSmallView、RocketLauncher继承RelativeLayout,并重写View:
FloatBallBigView类主要是注册各个button的点击事件
RocketLauncher类主要是创建一个ImageView 加载发射台的图片资源
FloatBallSmallView类比较重要,这里需要着重讲解一下:分为两个组件textView (显示内存使用情况)和ImageView(加载小火箭,默认为GONE),FloatBallSmallView 实现OnClickListener , OnTouchListener接口。在onClick方法中主要做一个view的切换,调用windowManager的removeView方法将FloatBallSmallView remove掉,并将FloatBallBigView添加到WindowsManager中
—————– * 划重点!!!!! * ————————
在onTouch方法中我们监听view获得的触摸事件
@Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isPressed = true; mStartX = event.getRawX(); mStartY = event.getRawY(); mTempX = event.getRawX(); mTempY = event.getRawY(); updateViewStatus();//更新视图 break; case MotionEvent.ACTION_MOVE: float x = event.getRawX() - mStartX; float y = event.getRawY() - mStartY; //计算偏移量,刷新视图 mParams.x += x; mParams.y += y; // 手指移动的时候更新小悬浮窗的状态和位置 updateViewPosition(); mStartX = event.getRawX(); mStartY = event.getRawY(); break; case MotionEvent.ACTION_UP: float endX = event.getRawX(); float endY = event.getRawY(); isPressed = false; if (Math.abs(endX - mTempX) < 6 && Math.abs(endY - mTempY) < 6) { updateViewStatus();//更新视图 return false;//判断为点击事件,不做处理 } boolean b = mFloatBallManager.isReadyToLaunch();//判断小火箭是否到达发射台 if (b) { launchRocket();//发射火箭 } else { updateViewStatus();//更新视图 } return true; default: break; } return false; }
在ACTION_DOWN 的时候,需要记录下点击的位置,便于后面判断是否该拦截该事件。mTempX 和mTempY 是用来记录每次移动时上一个点的位置。注意,这里采用event.getRawX() 而不是event.getX()。查看getRawX方法可以知道,返回值是屏幕的绝对位置,而getX方法是获取view内部的相对值
(2) 创建一个FloatBallManager类来管理悬浮球的创建,删除工作。在创建悬浮球的时候有一个比较重要的对象LayoutParams,我们可以通过他来设置view的位置,在更新view的位置时候再调用windowManager类的updateViewLayout方法即可。在判断小火箭是否到达发送台时候,我们可以比较两者的LayoutParams的X和Y值。
2、模拟按键的实现
在这之前,需要简单介绍一下Android的按键代码,在KeyEvent对象中封存了Android所有物理按键对应的key code,其中用到的code 对应的key如下:
- menu按键: KEYCODE_MENU
- back按键:KEYCODE_BACK
- home按键:KEYCODE_HOME
- 上按键:KEYCODE_DPAD_UP
- 下按键:KEYCODE_DPAD_DOWN
- 左按键:KEYCODE_DPAD_LEFT
- 右按键:KEYCODE_DPAD_RIGHT
现在我们知道各个物理按键对应的code ,那么我们应该如何模拟物理按键呢? 我们可以通过以下代码实现:
public void simulateKeystroke(final int KeyCode) { new Thread(new Runnable() { public void run() { // TODO Auto-generated method stub try { Instrumentation inst = new Instrumentation(); inst.sendCharacterSync(KeyCode); } catch (Exception e) { // TODO: handle exception } } }).start(); }
是不是很简单呢?上面是一个比较简单的方法,还有一个方法如下,也能达到目的:
private void execAdbCode(int code) { Runtime runtime = Runtime.getRuntime(); try { runtime.exec("input keyevent " + code); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
其主要通过adb的方法去执行相应的代码,间接达到模拟物理按键的效果。到这里看似完美模拟物理按键了,获取对应的code,执行代码,系统自动反馈,一气呵成。然而,当你高兴地运行的时候,你会高兴不起来,为什么呢?你疯狂点击button 执行相应的代码,但是系统就是不买账,根本没有任何反馈,你开始怀疑解决方案,疯狂去找其他答案,发现找来找去,没有更好的办法。那咋办?需求依旧得按dateline完成,既然达不到效果,不妨换个思路想一下,物理按键的点击事件是不是由系统发出的?既然是系统层的,会不会处于安全问题有所限制?那我们应该如何才能使系统认可我们的应用,并调用系统级别的代码呢?先想想平常我们是如何打包应用,下发到手机(TV )的?是不是通过Android studio直接打包安装的?但这样安装的app处于应用级别,也就是说一些系统级别的权限它无法获取。那么怎么才能让我们开发的APP处于系统级别呢?
3、系统应用打包的实现
为了解决上述方案无效的问题,我们需要将自己的APP打包为系统应用,这样就可以愉快的玩耍了,具体过程我就不详细讲了,这里有一篇博客讲的比较详细,附上传送门 将APP打包到系统应用中
4、开机启动的实现
项目中有一个需求需要实现开机自启动,一般我们会注册一个静态广播接收器,监听开机广播,但有些手机在Framework层将广播开机事件给去掉了,这个方案不一定奏效。这时候需要同时监听网络状态的改变,代码如下:
- 先在Androidmanifest中配置
<receiver android:name="com.example.user.broadcast.InterNetBroadCast" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>
- 然后创建接收器,这里的intent需要setflag为FLAG_ACTIVITY_NEW_TASK,因为任务栈可能还没有创建,我们需要新创建一个任务栈
public class InterNetBroadCast extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent intent1; intent1 = new Intent(context,FloatBallService.class); intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startService(intent1); }}
- 然后在service的oncreate方法创建一个悬浮球
@Override public void onCreate() { super.onCreate(); mFloatBallManager = FloatBallManager. getFloatBallManager(this) ; mFloatBallManager.createBigFloatView(); }
5、关于一些坑
(1)在设置view的layoutParma的type参数设置为TYPE_PHONE与TYPE_TOAST的区别:
- TYPE_PHONE :需要获取android.permission.SYSTEM_ALERT_WINDOW权限,可以获取触摸事件,但是悬浮球跟随手指的效果不好
- TYPE_TOAST:不需要权限,在一些手机机型中无法获取触摸事件,但是在TV中设置为该值可以获取
(2)layoutParma的flags参数:
- 悬浮球外部获取触摸事件:可以设置为 LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCH_MODAL,让悬浮球外部获取触摸事件
附上 TV内存管家github传送门
- Android TV 悬浮球模拟物理按键
- Android 模拟物理按键
- Android 模拟物理按键
- android 模拟物理按键
- Android 通过串口模拟 模拟物理按键
- Android 通过串口模拟 模拟物理按键
- android tv 按键
- Android 模拟屏幕点击和物理按键方式
- android 物理按键
- android 物理按键
- android 添加物理按键
- android 物理按键
- android 物理按键
- android 物理按键
- android 物理按键
- Android 物理按键
- android 物理按键
- Android物理按键
- 橘子粑粑(java)基础之计算机
- 数组名与指针
- java语法学习笔记
- Makfile 应用进阶实例
- CF 803C Maximal GCD 枚举,构造
- Android TV 悬浮球模拟物理按键
- 运放带宽
- css效果实现一个物体的旋转小demo
- 记录——不平庸的十一月
- 服务亿级图片压缩那些事
- [RE]如何调整堆栈平衡
- 数据结构(非线性表)
- Tomcat Linux下启动不了(java.net.MalformedURLException: Local host name unknown: java)
- leetcode Regular Expression Matching