Android中通过按键旋转屏幕

本文介绍如何通过长按手机上的Menu键,实现屏幕旋转功能,包括修改按键处理程序、实现选择旋转方向的函数以及创建新旋转键的代码实现。
 

1 修改按键处理程序
frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java

函数
    public boolean interceptKeyTi(WindowState win, int code, int metaKeys, boolean down, 
            int repeatCount, int flags)
在处理菜单键的地方
if (code == KeyEvent.KEYCODE_MENU) {
            final int chordBug = KeyEvent.META_SHIFT_ON;

            if (down && repeatCount == 0) {
                if (mEnableShiftMenuBugReports && (metaKeys & chordBug) == chordBug) {
                    Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
                    mContext.sendOrderedBroadcast(intent, null);
                    return true;
                } else if (SHOW_PROCESSES_ON_ALT_MENU &&
                        (metaKeys & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
                    Intent service = new Intent();
                    service.setClassName(mContext, "com.android.server.LoadAverageService");
                    ContentResolver res = mContext.getContentResolver();
                    boolean shown = Settings.System.getInt(
                            res, Settings.System.SHOW_PROCESSES, 0) != 0;
                    if (!shown) {
                        mContext.startService(service);
                    } else {
                        mContext.stopService(service);
                    }
                    Settings.System.putInt(
                            res, Settings.System.SHOW_PROCESSES, shown ? 0 : 1);
                    return true;
                }

}
//上面是原来的内容,下面是添加的新内容
else if (down && repeatCount == 20 && MenuKeyUp && (!keyguardOn)) { 
//如果按下Menu键一定时间,抬起时执行此段函数
                MenuKeyUp = false;
   try {
                int ro = mWindowManager.getRotation(); //获取当前方向
                
                    if( ro == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ) {
                        ro = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                    } else {
                        ro = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                    }
                } 
   catch (RemoteException e) {
                    Log.v(TAG, "!!! getRotation fail !!!");
                }

                try {
                    //旋转屏幕
                    mWindowManager.setRotation(ro, true, Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);
//最后可跟不同的参数,可实现一些旋转效果
                } 
   catch (RemoteException e) {
                    Log.v(TAG, "!!! mWindowManager.setRotation fail !!!");
                }
                return true;
            }
            if(!down) {
                MenuKeyUp = true;            
            }
}


2 修改实现选择的函数
/frameworks/base/services/java/com/android/server/WindowManagerService.java

找到该函数
    public boolean setRotationUncheckedLocked(int rotation, int animFlags)

将以下妨碍选择的内容注释掉
        //rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation,
        //        mRotation, mDisplayEnabled);


3、当然也可以新作一个rotate键来选择屏幕,以下是引用代码

+ } else if (code == KeyEvent.KEYCODE_ROTATE) { 
+ // ROTATE KEY pressed 
+ if (down) { 
+ mButtonPushFlg = true; 

+ try { 
+ int ro = mWindowManager.getRotation(); // Orientation vertical 
+ if (ro == 3 ) { 
+ mWindowManager.setRotation (Surface.ROTATION_0,true,mFancyRotationAnimation); //Orientation 
landscape 
+ } else { 
+ mWindowManager.setRotation 
(Surface.ROTATION_270,true,mFancyRotationAnimation); //Orientation 
portlate 
+ } 
+ } catch (RemoteException e) { 
+ // Igbore 
+ Log.i("info", "Rotation failed "); 
+ } 
+ } 
+ return true; 
}

 

OK,重新编译后,长按Menu键即可实现屏幕旋转。

<think> 我们正在讨论如何通过adb shell获取Android屏幕上虚拟按键的坐标。根据之前的对话,我们已经知道: 1. 开启开发者选项中的“指针位置”功能可以在屏幕上显示触摸点的坐标,但这需要手动操作。 2. 使用`adb shell dumpsys input`可以获取输入系统的信息,但需要分析其中的内容才能得到虚拟按键的坐标。 现在,用户再次询问使用adb shell获取虚拟按键坐标的方法。根据引用内容,我们注意到: 引用[2]中提到,在MonkeyScript中可以使用DispatchTrackball等事件,并且需要坐标。同时,引用[2]中也提到了开启指针位置功能。 但是,我们仍然需要一个直接通过adb命令获取坐标的方法。然而,虚拟按键(如返回键、主页键、最近任务键)是系统导航栏的一部分,它们的坐标信息通常由系统UI管理。 因此,我们可以尝试通过dumpsys系统UI服务来获取导航栏的信息,从而推断虚拟按键的位置。 具体步骤如下: 1. 使用命令:`adb shell dumpsys window displays` 这个命令会输出屏幕的显示信息,包括屏幕的分辨率。虚拟按键区域(导航栏)通常位于屏幕底部(或侧边),我们可以得到屏幕的宽高。 2. 使用命令:`adb shell dumpsys window` 在输出中搜索“mStable”或“mUnrestricted”等字样,找到内容区域(Content区域)和整个显示区域(Display区域)。导航栏的高度(或宽度)就是整个显示区域减去内容区域。 3. 使用命令:`adb shell dumpsys activity` 或者更具体地,`adb shell dumpsys activity containers`,这里可能会显示当前窗口的边界。 4. 使用命令:`adb shell dumpsys SystemUI` 系统UI服务管理导航栏。在输出中搜索“NavigationBar”相关信息,可能会包含导航栏视图的边界(例如,一个矩形区域)。 但是,请注意,不同Android版本和不同厂商的定制系统输出可能不同。 例如,在Android原生系统中,我们可以尝试: ```bash adb shell dumpsys window | grep -E 'Display|mCurrentFocus|mStable' ``` 或者,更具体地查找导航栏: ```bash adb shell dumpsys window | grep mNavigationBar ``` 如果找到类似这样的行: mNavigationBar=com.android.systemui.navigationbar.NavigationBarView{... ... left=0 top=1184 right=1080 bottom=1200} 那么,这个矩形区域(left, top, right, bottom)就是导航栏的位置。虚拟按键就在这个区域内。 但是,虚拟按键可能有多个(返回、主页、最近任务),它们通常是等间距排列在导航栏中。我们可以通过计算得到每个按键的中心坐标。 例如,假设导航栏的矩形是 (0, 1184, 1080, 1200),那么高度是16像素(bottom-top)。虚拟按键通常是水平排列,所以我们可以将导航栏水平分成三个部分(三个按键),每个部分的中心坐标计算如下: - 返回键:x = 1080/6 = 180, y = (1184+1200)/2 = 1192 - 主页键:x = 1080/2 = 540, y = 1192 - 最近任务键:x = 1080*5/6 = 900, y = 1192 但是,这是理想情况,实际布局可能有所不同(比如有的系统只有两个按键,或者有额外的按键)。因此,我们需要具体分析导航栏的布局。 另外,在Android 10及更高版本中,手势导航可能取代了虚拟按键,此时导航栏区域可能很小甚至没有(只有手势条)。 因此,步骤总结如下: 1. 通过`adb shell dumpsys window`获取导航栏的边界矩形。 2. 解析出矩形的坐标(left, top, right, bottom)。 3. 根据导航栏内按键的布局(通常从左到右等间距排列),计算每个按键的中心坐标。 但是,需要注意的是,虚拟按键的坐标是相对于屏幕的,且原点在屏幕左上角。 如果无法直接获取导航栏的矩形,还可以尝试获取内容区域和整个显示区域,然后计算导航栏的位置。例如: 整个显示区域高度为1920,内容区域高度为1904(即状态栏高度24+导航栏高度?),那么导航栏高度为1920-1904=16?但实际上,状态栏和导航栏是分开的。 更准确的做法是: - 整个显示区域(Display):通过`adb shell dumpsys window displays`得到。 - 内容区域(Content区域):在`dumpsys window`的输出中查找mStableInsets或mUnrestrictedScreen等。 例如,输出可能包含: mStableInsets=Rect(0, 50, 0, 100) // 上、左、下、右的稳定内边距(非内容区域) mUnrestrictedScreen=Rect(0,0,1080,1920) 那么,底部的稳定内边距(100)很可能就是导航栏的高度。此时,导航栏的矩形区域为: left=0, top=1920-100, right=1080, bottom=1920 然后,同样按照等分的方法计算按键坐标。 然而,以上方法需要根据具体输出进行解析。由于不同设备差异较大,可能无法保证通用性。 因此,回答用户的问题: 目前,没有直接获取虚拟按键坐标的adb命令。但可以通过解析`adb shell dumpsys window`或`adb shell dumpsys SystemUI`的输出,间接计算出虚拟按键的坐标。 具体操作步骤(以获取底部导航栏为例): 1. 执行`adb shell dumpsys window`,查找导航栏的矩形区域(如搜索NavigationBar)或屏幕的稳定内边距(mStableInsets)。 2. 如果找到导航栏的矩形,则根据矩形计算。 3. 如果找到稳定内边距,则根据整个显示区域和稳定内边距计算出导航栏的矩形。 4. 假设导航栏内按键水平均匀分布,计算每个按键的中心坐标。 示例命令解析: 步骤1:获取整个显示区域 adb shell dumpsys window displays 输出示例: Display: mDisplayId=0 init=1080x1920 420dpi cur=1080x1920 app=1080x1794 ... 这里,app区域高度为1794,那么状态栏和导航栏一共占1920-1794=126像素。但不知道导航栏具体多少。 步骤2:获取稳定内边距 adb shell dumpsys window | grep mStableInsets 输出示例: mStableInsets=Rect(0, 50, 0, 100) 这里,底部内边距为100,所以导航栏高度为100。那么导航栏的矩形为:left=0, top=1920-100=1820, right=1080, bottom=1920。 步骤3:计算按键坐标。假设有三个按键(返回、主页、最近任务),则: 每个按键区域宽度 = 1080 / 3 = 360 返回键中心:x=180, y=1820+50=1870(这里50是导航栏高度的一半?实际上,中心y坐标应该是(1820+1920)/2=1870) 主页键中心:x=540, y=1870 最近任务键中心:x=900, y=1870 注意:这个计算是假设的,实际按键可能不是均匀分布,或者有额外的区域(如手势区域)。而且,不同设备可能不同。 因此,如果用户需要精确获取,可能需要查看系统UI的布局文件,但这需要root权限。 鉴于以上复杂性,如果只是为了自动化测试,可以考虑使用其他工具,如uiautomator,它可以获取界面元素(包括导航栏)的坐标。 使用uiautomator的方法: 1. 确保设备API级别>=16 2. 执行命令:`adb shell uiautomator dump`(将当前界面布局保存到/sdcard/window_dump.xml) 3. 然后使用`adb pull /sdcard/window_dump.xml`将文件拉取到本地 4. 在xml文件中,搜索导航栏的控件,获取其边界(bounds)属性。例如: <node ... resource-id="com.android.systemui:id/navigation_bar" bounds="[0,1870][1080,1920]">...</node> 然后,再在这个节点下查找具体的按键,如返回键: <node ... resource-id="com.android.systemui:id/back" bounds="[30,1870][150,1920]">...</node> 这样,我们就可以计算出返回键的中心坐标:x=(30+150)/2=90, y=(1870+1920)/2=1895 这种方法更为准确,但需要额外的步骤(拉取文件并解析xml)。 总结: 如果没有root权限,且设备支持uiautomator,则推荐使用uiautomator dump的方法获取虚拟按键的坐标。 如果用户坚持使用dumpsys window,则需要根据输出进行解析,但这种方法可能因设备而异。 因此,我们给出两种方法: 方法一:通过dumpsys window获取(系统级,但可能不准确) 方法二:通过uiautomator dump获取(更准确,但需要文件操作) 下面给出方法二的详细步骤: 1. 执行命令: adb shell uiautomator dump --compressed /sdcard/window_dump.xml 2. 将文件从设备拉取到本地: adb pull /sdcard/window_dump.xml 3. 解析window_dump.xml文件,查找导航栏中的按键节点(根据resource-id),然后解析bounds属性。 在Android系统中,常见的虚拟按键资源ID如下(可能因系统版本和厂商定制而不同): 返回键:com.android.systemui:id/back 或 com.android.systemui:id/nav_btn_back 主页键:com.android.systemui:id/home 或 com.android.systemui:id/nav_btn_home 最近任务键:com.android.systemui:id/recent_apps 或 com.android.systemui:id/nav_btn_recent 注意:有些设备可能使用手势导航,没有可见的虚拟按键。 最后,如果用户需要动态获取(比如在脚本中),可以使用: adb shell uiautomator dump /sdcard/window_dump.xml && adb shell cat /sdcard/window_dump.xml 然后通过grep等工具解析xml(但xml在设备上是单行文本,建议拉取到本地解析)。 相关问题: 1. 如何在没有root权限的情况下,通过adb命令获取虚拟按键的坐标? 2. 使用uiautomator dump获取布局信息时,如何快速解析出特定虚拟按键的坐标? 3. 不同设备(如华为、小米等)的虚拟按键资源ID有何不同? 4. 如果设备使用了手势导航,如何获取手势区域的坐标? </think> 要通过 `adb shell` 获取 Android 屏幕上虚拟按键(导航栏)的坐标,可采用以下两种方法: --- ### **方法一:通过系统 UI 服务解析导航栏边界** 1. **获取导航栏边界信息**: ```bash adb shell dumpsys window | grep -i 'mNavigationBar' ``` - 输出示例:`mNavigationBar=com.android.systemui.navigationbar.NavigationBarView{... left=0 top=1872 right=1080 bottom=1920}` - **关键参数**: - `top`:导航栏顶部 Y 坐标 - `bottom`:导航栏底部 Y 坐标 - `right`:屏幕宽度 X 坐标 2. **计算虚拟按键坐标**: - 虚拟按键通常在导航栏内**水平均匀分布**(如返回键、主页键、多任务键)。 - 以三键导航栏为例: - **返回键中心坐标**: $$(x_1, y) = \left( \frac{\text{right}}{6}, \frac{\text{top} + \text{bottom}}{2} \right)$$ - **主页键中心坐标**: $$(x_2, y) = \left( \frac{\text{right}}{2}, \frac{\text{top} + \text{bottom}}{2} \right)$$ - **多任务键中心坐标**: $$(x_3, y) = \left( \frac{5 \times \text{right}}{6}, \frac{\text{top} + \text{bottom}}{2} \right)$$ > 示例:若输出为 `top=1872, bottom=1920, right=1080`,则主页键坐标为 `(540, 1896)`。 --- ### **方法二:通过 UI Automator 获取精确坐标** 1. **导出当前界面布局**: ```bash adb shell uiautomator dump /sdcard/window_dump.xml adb pull /sdcard/window_dump.xml ``` 2. **解析导航栏按键坐标**: - 打开 `window_dump.xml`,搜索导航栏控件: - **资源标识符**(常见系统 UI): - 返回键:`com.android.systemui:id/back` - 主页键:`com.android.systemui:id/home` - 多任务键:`com.android.systemui:id/recent_apps` - 输出示例: ```xml <node ... resource-id="com.android.systemui:id/home" bounds="[480,1872][600,1920]"/> ``` - **坐标解析**: - `bounds="[left,top][right,bottom]"` - 中心坐标:$$ \left( \frac{\text{left} + \text{right}}{2}, \frac{\text{top} + \text{bottom}}{2} \right) $$ --- ### **注意事项** 1. **设备差异**: - 导航栏位置可能位于屏幕**底部**(常见)或**侧边**(如三星设备)。 - 部分厂商定制 ROM 会修改资源 ID(需检查实际 XML 中的标识符)[^1]。 2. **手势导航**: - 若启用手势操作(无虚拟按键),导航栏高度可能为 0,需通过 `adb shell dumpsys window | grep mGestureHint` 获取手势条位置。 3. **动态分辨率**: - 屏幕旋转或分辨率变更后需重新执行命令获取最新坐标。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值