android虚拟按键源码流程分析
android 虚拟按键流程分析
今天来说说android 的虚拟按键的源码流程。⼤家都知道,android 系统的状态栏,虚拟按键,下拉菜单,以及通知显⽰,keyguard 锁屏都是在framework 下的SystemUI中的。
1. 要说起虚拟按键,⾸先得说下虚拟按键的开关
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
@Override
public void setInitialDisplaySize(Display display,int width,int height,int density){
...
// Allow the navigation bar to move on non-square small devices (phones).
mNavigationBarCanMove = width != height && shortSizeDp <600;
mHasNavigationBar = Boolean(fig_showNavigationBar);
//这⾥ mHasNavigationBar 变量决定android 系统是否有虚拟按键,想要android 系统默认显⽰或者关闭虚拟按键,则可以在framework 下的config ⽂件中将c onfig_showNavigationBar置为true或者false
// Allow a system property to override this. Used by the emulator.
// See also hasNavigationBar().
String navBarOverride = ("qemu.hw.mainkeys");
if("1".equals(navBarOverride)){
mHasNavigationBar =false;
}else if("0".equals(navBarOverride)){
mHasNavigationBar =true;
}
//这⾥⾕歌⼜给了⼀个开关,即 "qemu.hw,mainkeys"的值,⼀般来说,系统中是不对这个值处理的。这个是⾕歌预留的,在有需求的情况下,可以使⽤这个开关是设置prop,动态的显⽰或者隐藏虚拟按
键
// For demo purposes, allow the rotation of the HDMI display to be controlled.
// By default, HDMI locks rotation to landscape.
if("portrait".("persist.demo.hdmirotation"))){
mDemoHdmiRotation = mPortraitRotation;
}else{
mDemoHdmiRotation = mLandscapeRotation;
}
mDemoHdmiRotationLock = Boolean("persist.demo.hdmirotationlock",false);
// For demo purposes, allow the rotation of the remote display to be controlled.
// By default, remote display locks rotation to landscape.
if("portrait".("rotation"))){
mDemoRotation = mPortraitRotation;
}else{
mDemoRotation = mLandscapeRotation;
}
mDemoRotationLock = Boolean(
"ationlock",false);
// Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
// developer.android/guide/practices/screens_support.html#range
mForceDefaultOrientation = longSizeDp >=960&& shortSizeDp >=720&&
/
/ For debug purposes the next line turns this feature off with:
// $ adb shell setprop config.override_forced_orient true
// $ adb shell wm size reset
!"true".("config.override_forced_orient"));
}
2. SystemUI 中虚拟按键的创建
SystemUI\app\src\main\java\com\android\systemui\statusbar\phone\StatusBar.java
protected void makeStatusBarView(){
...
try{
boolean showNav = mWindowManagerService.hasNavigationBar();//获取上⾯虚拟按键的开关if(DEBUG) Log.v(TAG,"hasNavigationBar="+ showNav);
if(showNav){
createNavigationBar();// 创建虚拟按键
}
}catch(RemoteException ex){
// no window manager? good luck with that
}
...
}
protected void createNavigationBar(){
mNavigationBarView = ate(mContext,(tag, fragment)->{
mNavigationBar =(NavigationBarFragment) fragment;
if(mLightBarController !=null){
mNavigationBar.setLightBarController(mLightBarController);
}
mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
});
}
SystemUI\app\src\main\java\com\android\systemui\statusbar\phone\NavigationBarFragment.java
public static View create(Context context, FragmentListener listener){
WindowManager.LayoutParams lp =new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
lp.setTitle("NavigationBar");
lp.windowAnimations =0;
View navigationBarView = LayoutInflater.from(context).inflate(
R.layout.navigation_bar_window,null);
if(DEBUG) Log.v(TAG,"addNavigationBar: about to add "+ navigationBarView);
if(navigationBarView ==null)return null;
FragmentHostManager fragmentHost = (navigationBarView);
NavigationBarFragment fragment =new NavigationBarFragment();
.replace(R.id.navigation_bar_frame, fragment, TAG)
mit();
fragmentHost.addTagListener(TAG, listener);
return navigationBarView;
}
这⾥可以看到,其实虚拟按键的view 是⼀个window,是通过addView 添加的。
3. 接下来说说虚拟按键view的创建和显⽰
这⾥有三个重要的类,⼀个是上⾯提到的NavigationBarFragment,另外就是NavigationBarView和NavigationBarInflaterView 现在来看看NavigationBarView ,这个类主要是将虚拟按键的⼏个图标和view关联起来
public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture>{
这个类主要是将各个虚拟按键的button加⼊ButtonDispatcher中,另外这⾥要说⼀句,我们只知道⼀般虚拟按键只有三个(back,home,recent),其实看了下⾯,其实不⽌三个,其余⼏个都是隐藏的。另外,每⼀个虚拟按键的view都是⼀个layout。
public NavigationBarView(Context context, AttributeSet attrs){
super(context, attrs);
mDisplay =((WindowManager) SystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay();
mVertical =false;
mShowMenu =false;
mShowAccessibilityButton =false;
mLongClickableAccessibilityButton =false;
mConfiguration =new Configuration();
mConfiguration.Resources().getConfiguration());
updateIcons(context, Configuration.EMPTY, mConfiguration);
mBarTransitions =new NavigationBarTransitions(this);
mButtonDispatchers.put(R.id.back,new ButtonDispatcher(R.id.back));
mButtonDispatchers.put(R.id.home,new ButtonDispatcher(R.id.home));
mButtonDispatchers.put(_apps,new ButtonDispatcher(_apps));
mButtonDispatchers.put(u,new ButtonDispatcher(u));
mButtonDispatchers.put(R.id.ime_switcher,new ButtonDispatcher(R.id.ime_switcher));
mButtonDispatchers.put(R.id.accessibility_button,new ButtonDispatcher(R.id.accessibility_button));
}
//这个⽅法主要是将虚拟按键的图标和view,bind起来
private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig){
ientation != ientation
|| oldConfig.densityDpi != newConfig.densityDpi){
mDockedIcon =getDrawable(ctx,
R.drawable.ic_sysbar_docked, R.drawable.ic_sysbar_docked_dark);
}
if(oldConfig.densityDpi != newConfig.densityDpi
|| LayoutDirection()!= LayoutDirection()){
mBackIcon =getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
mBackLandIcon = mBackIcon;
mBackAltIcon =getDrawable(ctx,
R.drawable.ic_sysbar_back_ime, R.drawable.ic_sysbar_back_ime_dark);
mBackAltLandIcon = mBackAltIcon;
mHomeDefaultIcon =getDrawable(ctx,
R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
mRecentIcon =getDrawable(ctx,
R.drawable.ic_sysbar_recent, R.drawable.ic_sysbar_recent_dark);
mMenuIcon =getDrawable(ctx, R.drawable.ic_sysbar_menu, R.drawable.ic_sysbar_menu_dark);
mAccessibilityIcon =getDrawable(ctx, R.drawable.ic_sysbar_accessibility_button,
R.drawable.ic_sysbar_accessibility_button_dark);
int dualToneDarkTheme = ThemeAttr(ctx, R.attr.darkIconTheme);
int dualToneLightTheme = ThemeAttr(ctx, R.attr.lightIconTheme);
Context darkContext =new ContextThemeWrapper(ctx, dualToneDarkTheme);
Context lightContext =new ContextThemeWrapper(ctx, dualToneLightTheme);
mImeIcon =getDrawable(darkContext, lightContext,
R.drawable.ic_ime_switcher_default, R.drawable.ic_ime_switcher_default);
if(ALTERNATE_CAR_MODE_UI){
updateCarModeIcons(ctx);
updateCarModeIcons(ctx);
}
}
}
// 这个⽅法其实就是来隐藏其余的虚拟按键的。
public void setNavigationIconHints(int hints,boolean force){
if(!force && hints == mNavigationIconHints)return;
final boolean backAlt =(hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT)!=0;
if((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT)!=0&&!backAlt){ BackAltCleared();
}
if(DEBUG){
Toast.makeText(getContext(),
"Navigation icon hints = "+ hints,
500).show();
}
mNavigationIconHints = hints;
// We have to replace or restore the back and home button icons when exiting or entering
// carmode, respectively. Recents are not available in CarMode in nav bar so change
// to recent icon is not required.
KeyButtonDrawable backIcon =(backAlt)
?getBackIconWithAlt(mUseCarModeUi, mVertical)
:
getBackIcon(mUseCarModeUi, mVertical);
getBackButton().setImageDrawable(backIcon);
updateRecentsIcon();
if(mUseCarModeUi){
getHomeButton().setImageDrawable(mHomeCarModeIcon);
}else{
制作android软件流程getHomeButton().setImageDrawable(mHomeDefaultIcon);
}
// The Accessibility button always overrides the appearance of the IME switcher
final boolean showImeButton =
!mShowAccessibilityButton &&((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) !=0);
getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
getImeSwitchButton().setImageDrawable(mImeIcon);
// Update menu button in case the IME state has changed.
setMenuVisibility(mShowMenu,true);
getMenuButton().setImageDrawable(mMenuIcon);
setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton);
getAccessibilityButton().setImageDrawable(mAccessibilityIcon);
setDisabledFlags(mDisabledFlags,true);
}
}
说到这,不妨再来看看每⼀个虚拟按键的layout是怎么写的:
SystemUI\app\src\main\res\l
<com.android.systemui.statusbar.policy.KeyButtonView
android="schemas.android/apk/res/android"
systemui="schemas.android/apk/res-auto"
id="@+id/back"
layout_width="@dimen/navigation_key_width"
layout_height="match_parent"
layout_weight="0"
keyCode="4"
scaleType="fitCenter"
contentDescription="@string/accessibility_back"
paddingTop="15dp"
paddingBottom="15dp"
paddingStart="@dimen/navigation_key_padding"
paddingEnd="@dimen/navigation_key_padding"
/>
SystemUI\app\src\main\res\l
<com.android.systemui.statusbar.policy.KeyButtonView
android="schemas.android/apk/res/android"
systemui="schemas.android/apk/res-auto"
id="@+id/home"
layout_width="@dimen/navigation_key_width"
layout_height="match_parent"
layout_weight="0"
keyCode="3"
scaleType="fitCenter"
contentDescription="@string/accessibility_home"
paddingTop="@dimen/home_padding"
paddingBottom="@dimen/home_padding"
paddingStart="@dimen/navigation_key_padding"
paddingEnd="@dimen/navigation_key_padding"
/>
从上⾯我们可以知道,每⼀个虚拟按键都是⼀个单独的layout。细⼼的同学应该会注意到,这个⾥⾯有⼀个重要的元素,就是systemui:keyCode=“3”。从这⾥我们⼤概可以知道了,虚拟按键的点击实现,实际上是通过模拟发送keycode来实现的。
再来看看NavigationBarFragment 类
public class NavigationBarFragment extends Fragment implements Callbacks {
// 这个⽅法就是设置每⼀个虚拟按键的点击长按事件的监听的
private void prepareNavigationBarView(){
ButtonDispatcher recentsButton = RecentsButton();
recentsButton.setOnClickListener(this::onRecentsClick);
recentsButton.setOnTouchListener(this::onRecentsTouch);
recentsButton.setLongClickable(true);
recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
ButtonDispatcher backButton = BackButton();
backButton.setLongClickable(true);
backButton.setOnLongClickListener(this::onLongPressBackRecents);
ButtonDispatcher homeButton = HomeButton();
homeButton.setOnTouchListener(this::onHomeTouch);
homeButton.setOnLongClickListener(this::onHomeLongClick);
ButtonDispatcher accessibilityButton = AccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
updateAccessibilityServicesState(mAccessibilityManager);
}
// recent按键点击时会加载recentapp,
private boolean onRecentsTouch(View v, MotionEvent event){
int action = Action()& MotionEvent.ACTION_MASK;
if(action == MotionEvent.ACTION_DOWN){
mCommandQueue.preloadRecentApps();
}else if(action == MotionEvent.ACTION_CANCEL){
mCommandQueue.cancelPreloadRecentApps();
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论