之前写过一遍6.0的导航栏屏蔽分析过程,可参考Android6.0 源码修改之屏蔽导航栏虚拟按键(Home和RecentAPP)/动态显示和隐藏NavigationBar
屏蔽状态栏下拉
6.0解决办法
源码位置 SystemUI\src\com\android\systemui\statusbar\phone\PhoneStatusBarView.java
@Override public boolean onTouchEvent(MotionEvent event) { boolean barConsumedEvent = mBar.interceptTouchEvent(event); if (DEBUG_GESTURES) { if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { EventLog.writeEvent(EventLogTags.SYSUI_PANELBAR_TOUCH, event.getActionMasked(), (int) event.getX(), (int) event.getY(), barConsumedEvent ? 1 : 0); } } // return barConsumedEvent || super.onTouchEvent(event); return false; }
直接返回false,action_move不会执行,代码看似简单,实则super.onTouchEvent(event)调用的是PanelBar.java中的onTouchEvent()
8.1解决办法(同上)
6.0和8.1的状态栏代码是有差异的,但是通过上面的方法都能达到需求效果
6.0的PhoneStatusBarView调用onTouchEvent(),最终调用PanelBar的onTouchEvent()
8.1的PhoneStatusBarView调用onTouchEvent(),接着调用PanelBar的onTouchEvent(),最终调用PanelView的onTouchEvent()
从根本上阻止事件的传递来达到屏蔽下拉的效果
补充
如果需要动态的控制是否允许下拉状态栏,可以通过广播通知,接受传递的参数,持久化保存(可通过SharedPreference或者Settings.Global.xxxx)当前状态栏的状态,一般建议采用Settings.Global保存。
具体步骤
- 在frameworks\base\core\java\android\provider\Settings.java中Globel内部类下添加变量名称,比如OPEN_PANEL_ENABLED
- 通过make update-api指令重新编译,因为在Settings中修改后需要编译才能对应到这两文件frameworks/base/api/system-current.txt、frameworks/base/api/current.txt
- 在广播接收的地方通过Settings.Global.putInt(context.getContentResolver(), Settings.Global.OPEN_PANEL_ENABLED, 1) 保存值
- (ps 可通过adb命令 adb shell settings put global open_panel_enabled 1 模拟写入 adb shell settings get global open_panel_enabled 模拟查看)
- 将刚刚的return false改成
boolean flag=Settings.Global.getInt(getContext().getContentResolver(), Settings.Global.OPEN_PANEL_ENABLED,1)==1; return flag ? barConsumedEvent || super.onTouchEvent(event) : flag;
屏蔽导航栏显示
6.0解决办法
源码位置SystemUI\src\com\android\systemui\statusbar\phone\PhoneStatusBar.java
@Override public void start() { mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); updateDisplaySize(); mScrimSrcModeEnabled = mContext.getResources().getBoolean( R.bool.config_status_bar_scrim_behind_use_src); super.start(); // calls createAndAddWindows() mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); // TODO: use MediaSessionManager.SessionListener to hook us up to future updates // in session state addNavigationBar(); ..... }
直接注释addNavigationBar()就能达到需求效果
8.1解决办法
源码位置SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java
protected void makeStatusBarView() { final Context context = mContext; updateDisplaySize(); // populates mDisplayMetrics updateResources(); updateTheme(); .... 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 } .... }
直接注释createNavigationBar()或者将showNav=false就能达到需求效果
代码流程分析
6.0和8.1的导航栏都是通过WindowManager的addView来添加的,通过WindowManager的removeViewImmediate来移除
1、6.0的addNavigationBar实现,通过addView将mNavigationBarView添加,prepareNavigationBarView方法设置了点击、触摸、长按事件,无需关心
private void addNavigationBar() { if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView); if (mNavigationBarView == null) return; prepareNavigationBarView(); mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams()); mNavigationShown=true; }
查找到在makeStatusBarView()中,mNavigationBarView初始化,加载navigation_bar布局文件
protected PhoneStatusBarView makeStatusBarView() { final Context context = mContext; Resources res = context.getResources(); updateDisplaySize(); // populates mDisplayMetrics updateResources(); ... mNavigationBarView = (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null); mNavigationBarView.setDisabledFlags(mDisabled1); mNavigationBarView.setBar(this); mNavigationBarView.setOnVerticalChangedListener( new NavigationBarView.OnVerticalChangedListener() { @Override public void onVerticalChanged(boolean isVertical) { if (mAssistManager != null) { mAssistManager.onConfigurationChanged(); } mNotificationPanel.setQsScrimEnabled(!isVertical); } }); mNavigationBarView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { checkUserAutohide(v, event); return false; }}); }
2、8.1的createNavigationBar实现,发现是通过NavigationBarFragment来实例化mNavigationBarView
protected void createNavigationBar() { mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> { mNavigationBar = (NavigationBarFragment) fragment; if (mLightBarController != null) { mNavigationBar.setLightBarController(mLightBarController); } mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility); }); }
NavigationBarFragment的crate方法通过addView将mNavigationBarView添加,加载navigation_bar_window布局
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.token = new Binder(); 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; context.getSystemService(WindowManager.class).addView(navigationBarView, lp); FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView); NavigationBarFragment fragment = new NavigationBarFragment(); fragmentHost.getFragmentManager().beginTransaction() .replace(R.id.navigation_bar_frame, fragment, TAG) .commit(); fragmentHost.addTagListener(TAG, listener); return navigationBarView; }
补充
依旧是通过广播来动态控制导航栏的显示和隐藏
6.0已在开头文章中写过,这里就只补充8.1的显示和隐藏
源码位置SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java
private static final String SHOW_NAVIGATION = "cc.intent.systemui.shownavigation"; private static final String HIDE_NAVIGATION = "cc.intent.systemui.hidenavigation"; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "onReceive: " + intent); String action = intent.getAction(); if (HIDE_NAVIGATION.equals(action)) { if (mNavigationBarView == null) return; mWindowManager.removeViewImmediate(mNavigationBarView); mNavigationBarView = null; }else if (SHOW_NAVIGATION.equals(action)) { if (mNavigationBarView != null) return; createNavigationBar(); } } };