Android TV 悬浮球模拟物理按键

el/2024/5/23 0:48:25

  最近在体验实习的时候做了一个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获得的触摸事件

    @Overridepublic 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 stubtry {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 blocke.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 {@Overridepublic 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方法创建一个悬浮球
   @Overridepublic 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传送门

http://www.ngui.cc/el/5281481.html

相关文章

下载Android源码流程(完整版)

要在Linux环境下操作&#xff0c;要在Linux环境下操作&#xff0c;要在Linux环境下操作~~ 不要想在Windows环境下操作&#xff0c;因为会有各种问题。Windows环境的童鞋又不想装双系统的可以跟着下面的操作&#xff0c;Linux的童鞋可以直接跳过看。Mac的童鞋就略过~~~ &#x…

【Android】仿QQ可拖拽气泡

学习&#xff0c;从模仿开始。今天就学习一下QQ自定义的气泡&#xff0c;先上效果 这里涉及到一下知识点&#xff1a; Path&#xff1a;画贝塞尔曲线Canvas&#xff1a;画形状和文字以及图片view&#xff1a;触摸事件属性动画&#xff1a;处理爆炸效果 一、初始化 整个过…

【Android】换肤技术讲解

主题&#xff0c;是许多APP必备的一个功能&#xff0c;用户可以根据自己的喜好&#xff0c;来切换具有个性的主题&#xff0c;同时能让我们的APP更具把玩性。这篇博文就来聊聊皮肤切换的原理&#xff0c;效果图如下&#xff1a; 这里为了便于理解&#xff0c;在换肤的时候&a…

【android】插件化技术原理详解

作为移动端的黑科技&#xff0c;插件化技术一直受大厂的青睐。插件化技术有减少宿主Apk体积&#xff0c;可以独立更新&#xff0c;模块化开发等优点&#xff0c;让宿主APP极具扩展性。那么&#xff0c;现在就来聊聊其中的技术实现&#xff0c;国际惯例&#xff0c;先上效果图 这…

Android单元测试全解

自动化测试麻烦吗&#xff1f;说实在&#xff0c;麻烦&#xff01;有一定的学习成本。但是&#xff0c;自动化测试有以下优点&#xff1a; 节省时间&#xff1a;可以指定测试某一个activity&#xff0c;不需要一个个自己点单元测试&#xff1a;既然Java可以进行单元测试&#…

Android采用pm实现静默安装(降级安装)的解决方案

最近在做一个apk分析器&#xff0c;里面可以解析系统中所有安装app的信息&#xff0c;并提供组内开发的apk文件下载、静默安装&#xff08;包括降级安装&#xff09;&#xff0c;其中在降级安装中难度较大&#xff0c;在Android4.4与Android 8的解决方案不同&#xff0c;其他版…

cannot open line '/dev/tty.usbserial' for r/w resource busy

在进行串口调试的时候,需要在iterm上查看log信息只需要输入以下命令 screen /dev/tty.usbserial-gggggggg1 115200有时候会遇到以下问题 cannot open line /dev/tty.usbserial for r/w resource busy提示资源被占用了&#xff0c;感觉有点像进程被占用是一个问题。Google了一…

android适配右到左布局注意事项

呜呼&#xff0c;伊朗的项目终于做完了&#xff0c;大部分都是在整理右到左布局的需求。好在android sdk 从API17&#xff08;Android4.2&#xff09;开始支持右到左布局的需求&#xff0c;但是会有很多坑需要去填。   Android中的大部分组件是支持右到左布局的&#xff0c;只…

【flutter】把Google官方的历史时间demo跑起来

“其实我并不喜欢追求新技术。flutter是Google出的&#xff1f;真香&#xff5e;“ 引言 其实跨平台的痛&#xff0c;我真的没有体会到&#xff0c;毕竟我司不做ios平台。但是如果&#xff0c;flutter有可能成为新系统的开发框架&#xff0c;还是值得学习一下的&#xff0c;尤…

用python实现自动化翻译

“爬Google翻译还是需要点技巧的“ 引言 在做全球应用中&#xff0c;处理每个国家的翻译是必不可少的&#xff0c;也是最棘手的问题。为了保证翻译是正确的&#xff0c;这里需要借用Google翻译的接口&#xff0c;为此&#xff0c;特意写了一个python脚本来对接google翻译 _。 …