开发
基本方法
-
View# setFocusable()/android:focusable
设置View是否可以聚焦(注意:设置了View不一定可以拿到焦点 具体看这篇深入理解:View和ViewGroup如何才能获取焦点) -
View# boolean hasFocus()
View是否有焦点。如果是ViewGroup:自身有焦点或者其子View有焦点返回true,其他返回false -
View# boolean hasFocusable()
view是否可以聚焦。如果是ViewGroup:自身可以聚焦或者其子View有可以聚焦的返回true,其他返回false -
ViewGroup# View getFocusedChild()
ViewGroup方法。获取当前有焦点或者包含焦点的子View。获取到的View不一定是当前获取焦点的View,无法准确获取真正有焦点的View。
比如:以下xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/fl_container" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".activity.tv.FocusTestActivity">
<FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content">
<Button android:id="@+id/test_btn" android:focusable="true" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</FrameLayout>
</FrameLayout>
焦点实际上在Button上,如果我们调用跟布局的FrameLayout的getFocusedChild方法获取到的是第二层的FrameLayout。
-
View# View findFocus() 寻找实际获取焦点的View
View方法,查找当前View和其子View中实际获取焦点的View。如果我们要查找当前页面获取焦点的View,我们最好使用DecorView的findFocus方法 -
Window# View getCurrentFocus()
Window方法获取当前Window聚焦的方法。内部实际上就是调用DecorView的findCocus方法在视图层级去查找。
监听
-
View# void setOnFocusChangeListener(OnFocusChangeListener listener)
设置View聚焦监听。 -
View# protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
@Nullable Rect previouslyFocusedRect)
View可以重写监听焦点变化的方法。
第一个参数:是否获取焦点了(只有真正获取焦点了会回调,viewGroup的子View获取焦点ViewGroup此方法不回调)
第二个参数:焦点聚焦的方向 上、下、左、右用于监控焦点来源方向。一般是系统自动找焦点的时候可以看一下这个值,这个值不一定是真实的,因为方向值我可以乱传
第三个参数:上一个焦点的位置信息,不一定有。 -
ViewTreeObserver# void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener)
监听当前页面View树焦点的变化。如果你找不到焦点的变化情况可以通过这个方法很好用。回调结果:上一个聚焦的View和当前聚焦的View
使用:
view.getViewTreeObserver().addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
@Override
public void onGlobalFocusChanged(View oldFocus, View newFocus) {
}
});
请求焦点
- boolean requestFocus()
请求焦点,方向默认往下 - boolean requestFocus(int direction)
指定请求方法,请求焦点。取值View.FOCUS_UP、View.FOCUS_DOWN、View.FOCUS_LEFT、View.FOCUS_RIGHT、View.FOCUS_BACKWARD、VIew.FOCUS_FORWARD - boolean requestFocus(int direction, Rect previouslyFocusedRect)
指定请求方法,并指定上一个焦点所在的View的区域信息(是一个标记功能,可以不用填),请求焦点
通常情况下我们调用第一个就可以了,如果明确指定请求焦点的方向后面有处理使用第二个方法。
焦点查找
-
View# View focusSearch(@FocusRealDirection int direction)
在指定方向去查找下一个最近的可以聚焦的View。找不到就返回null。通过这个方法我们可以知道一个View的指定方向上是否还有可聚焦的View。用于监听是否到了页面边缘。 -
FocusFinder类
Android系统提供的查找可聚焦View的方法,系统自动查找焦点也是通过这个类来实现的。开放的方法不多,我们主要用这个方法:
View findNextFocus(ViewGroup root, View focused, int direction)
第一个参数:指定焦点查找范围,比如我们如果传递DecorView,就是在整个View树中去查找。如果我们传递一个指定的ViewGroup,就在这个ViewGroup内去查找。通过这个方法我们可以监听一个View或者ViewGroup的是否到达可聚焦的边缘,做一些边缘抖动的动画等等
第二个参数:当前聚焦的View
第三个参数:查找方向上、下、左、下、前一个、后一个
基本使用:
View nextFocus = FocusFinder.getInstance()
.findNextFocus((ViewGroup) getWindow().getDecorView(),
getWindow().getCurrentFocus(),
View.FOCUS_DOWN);
- View# 指定下一个可聚焦View的id,对应xml属性就是foucsxxxId
- void setNextFocusDownId(int nextFocusDownId)
- void setNextFocusForwardId(int nextFocusForwardId)
- void setNextFocusUpId(int nextFocusUpId)
- void setNextFocusLeftId(int nextFocusLeftId)
- void setNextFocusRightId(int nextFocusRightId)
当这个View聚焦之后,如果这个View指定了这些id,那么按上、下、左、右按键的时候,就会用你指定的可聚焦的View来聚焦。如果指定的不可聚焦,系统就会自动就近去找。
调试
adb
-
按键模拟
- adb shell input keyevent xxxkeyCode
模拟发送指定的keyCode对应的按键。具体的KeyCode在KeyEvent这个类可以看到 - adb shell input text a
模拟输入字符a
- adb shell input keyevent xxxkeyCode
-
查看盒子分辨率和dpi信息
adb shell dumpsys window displays
第一个是盒子初始分辨率
第二个是盒子dpi
第三个是盒子当前分辨率 -
修改盒子分辨率
adb shell wm size 1920x1080 -
修改盒子dpi
adb shell wm density 320 -
adb 连接
adb connect ip:port -
查看所有已安装应用包名
adb shell pm list packages -
查看所有已安装应用包名和对应Apk文件的路径
adb shell pm list packages -f -
查看指定apk设备安装路径
adb shell pm path 应用包名 -
adb启动开发者选项Activity
adb shell am start com.android.settings/.DevelopmentSettings -
布局边界(重启生效)
启动布局边界:adb shell setprop debug.layout true
关闭布局边界:adb shell setprop debug.layout false -
过度绘制检查(重启生效)
启动:adb shell setprop debug.hwui.overdraw show
关闭:adb shell setprop debug.hwui.overdraw false -
查看当前栈顶的Activity
adb shell dumpsys activity | grep “mFocus”
apk安装常见错误
- Failure [INSTALL_FAILED_TEST_ONLY]
解决方法:
adb install -t xxxxx.apk
Android Stuido3.0版本打release包在某些设备上可能导致无法安装
在
gradle.properties中添加以下属性可杜绝release包无法升级的问题
android.injected.testOnly=false
- Failure [INSTALL_FAILED_ALREADY_EXISTS]
apk包存在导致的问题
解决方法:
adb install -r apk包名
或者
adb uninstall 包名
adb install apk
- Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE]
安装包版本兼容问题
解决办法:
adb uninstall 包名
adb install apk
- [INSTALL_FAILED_CONFLICTING_PROVIDER]
手机上存在provider的authorities 相同
应用内换authorities的值,和其他应用冲突只能换authorities值了
其他问题
- 如何替换系统应用
1、使系统可以挂载
adb root
adb remount //使系统可挂载
2、查找系统引用
adb shell
cd system/app 或者 system/priv-app下查找apk
3、删除系统应用和应用数据
rm -rf xxxx
rm -rf data/data/应用包名
4、推入我们新的应用
adb push xxxx.apk /system/app 或者 system/priv-app
5、重启设备生效
adb reboot
不重启直接安装会报 Failure [INSTALL_FAILED_ALREADY_EXISTS] 错误
- 如何测试APK是否设置android:debuggable="false"
-
aapt list -v -a apk文件名 | grep debuggable
得到如下输出:
A: android:debuggable(0x0101000f)=(type 0x12)0x0
这表示 android:debuggable=”false”aapt命令在sdk/build-tools/版本/xxx
-
Android Studio反编译打好的apk直接看manifest文件
-
开发中遇到的坑
- VideoView播放的时候会抢焦点
默认不调用requestFocus方法的时候,如果有VideoView,系统会给VideoView焦点,VideoView设置setFocusable(false)是没有的。解决方法就是给页面其他可聚焦元素主动调用requestFocus方法就可以了。
- 对ViewGroup手动调用dispatchKeyEvent方法其中的子View的dispatchKeyEvent方法不触发
如果ViewGroup不包含焦点,调用dispatchKeyEvent不会往子View分发的,解决办法就是调用ViewGroup的requestFocus方法或者调用某个子View的requestFocus方法,让ViewGroup获得或者包含焦点