安卓系统默认支持一个导航栏和一个状态栏,有时客户需求双边按键或者三屏分开显示,例如左右显示按键中间为界面显示或者左边显示仪表盘中间显示导航界面右边显示车速时钟等,这种情况使用一个导航栏无法实现。

安卓代码中实现导航栏的代码在frameworks\base\services\core\java\com\android\server\wm\DisplayPolicy.java文件中,从代码中我们看到系统创建了一个名为NavigationBar的BarController,然后通过layoutNavigationBar实现了导航栏的布局,那么要实现双导航栏,则我们只需增加一个BarController,并实现导航栏的布局就行。

1,在DisplayPolicy.java文件中DisplayPolicy函数类里面增加mNavigationBarController2如下:

代码语言:javascript复制mNavigationBarController2 = new BarController("NavigationBar2",

displayId,

View.NAVIGATION_BAR_TRANSIENT,

View.NAVIGATION_BAR_UNHIDE,

View.NAVIGATION_BAR_TRANSLUCENT,

StatusBarManager.WINDOW_NAVIGATION_BAR,

FLAG_TRANSLUCENT_NAVIGATION,

View.NAVIGATION_BAR_TRANSPARENT);

2,在prepareAddWindowLw函数中添加导航栏2的窗口,通过YPE_NAVIGATION_BAR_PANEL类型添加,上层应用可以通过此类型调用,添加如下:

case TYPE_NAVIGATION_BAR_PANEL:

mContext.enforceCallingOrSelfPermission(

android.Manifest.permission.STATUS_BAR_SERVICE,

"DisplayPolicy");

if (mNavigationBar2 != null) {

if (mNavigationBar2.isAlive()) {

return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;

}

}

mNavigationBar2 = win;

mNavigationBarController2.setWindow(win);

mNavigationBarController2.setOnBarVisibilityChangedListener(

mNavBarVisibilityListener2, true);

mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR,

win, null /* frameProvider */);

mDisplayContent.setInsetProvider(InsetsState.TYPE_BOTTOM_GESTURES, win,

(displayFrames, windowState, inOutFrame) -> {

inOutFrame.top -= mBottomGestureAdditionalInset;

});

mDisplayContent.setInsetProvider(InsetsState.TYPE_LEFT_GESTURES, win,

(displayFrames, windowState, inOutFrame) -> {

inOutFrame.left = 0;

inOutFrame.top = 0;

inOutFrame.bottom = displayFrames.mDisplayHeight;

inOutFrame.right = displayFrames.mUnrestricted.left + mSideGestureInset;

});

mDisplayContent.setInsetProvider(InsetsState.TYPE_RIGHT_GESTURES, win,

(displayFrames, windowState, inOutFrame) -> {

inOutFrame.left = displayFrames.mUnrestricted.right - mSideGestureInset;

inOutFrame.top = 0;

inOutFrame.bottom = displayFrames.mDisplayHeight;

inOutFrame.right = displayFrames.mDisplayWidth;

});

mDisplayContent.setInsetProvider(InsetsState.TYPE_BOTTOM_TAPPABLE_ELEMENT, win,

(displayFrames, windowState, inOutFrame) -> {

if ((windowState.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0

|| mNavigationBarLetsThroughTaps) {

inOutFrame.setEmpty();

}

});

if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR PANEL: " + mNavigationBar2);

break;

3,最后我们beginLayoutLw函数进行布局,如下:

private boolean layoutNavigationBar2(DisplayFrames displayFrames, int uiMode, boolean navVisible,

boolean navTranslucent, boolean navAllowedHidden,

boolean statusBarForcesShowingNavigation) {

if (mNavigationBar2 == null) {

return false;

}

final Rect navigationFrame = sTmpNavFrame;

boolean transientNavBarShowing = mNavigationBarController2.isTransientShowing();

// Force the navigation bar to its appropriate place and size. We need to do this directly,

// instead of relying on it to bubble up from the nav bar, because this needs to change

// atomically with screen rotations.

final int rotation = displayFrames.mRotation;

final int displayHeight = displayFrames.mDisplayHeight;

final int displayWidth = displayFrames.mDisplayWidth;

final Rect dockFrame = displayFrames.mDock;

mNavigationBarPosition2 = NAV_BAR_RIGHT;

final Rect cutoutSafeUnrestricted = sTmpRect;

cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);

cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);

// Landscape screen; nav bar goes to the right.

final int left = cutoutSafeUnrestricted.right

- getNavigationBarWidth(rotation, uiMode);

navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);

displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;

if (transientNavBarShowing) {

mNavigationBarController2.setBarShowingLw(true);

} else if (navVisible) {

mNavigationBarController2.setBarShowingLw(true);

dockFrame.right = displayFrames.mRestricted.right =

displayFrames.mRestrictedOverscan.right = left;

} else {

// We currently want to hide the navigation UI - unless we expanded the status bar.

mNavigationBarController2.setBarShowingLw(statusBarForcesShowingNavigation);

}

if (navVisible && !navTranslucent && !navAllowedHidden

&& !mNavigationBar2.isAnimatingLw()

&& !mNavigationBarController2.wasRecentlyTranslucent()) {

// If the nav bar is currently requested to be visible, and not in the process of

// animating on or off, then we can tell the app that it is covered by it.

displayFrames.mSystem.right = left;

}

// Make sure the content and current rectangles are updated to account for the restrictions

// from the navigation bar.

displayFrames.mCurrent.set(dockFrame);

displayFrames.mVoiceContent.set(dockFrame);

displayFrames.mContent.set(dockFrame);

// And compute the final frame.

sTmpRect.setEmpty();

mNavigationBar2.getWindowFrames().setFrames(navigationFrame /* parentFrame */,

navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,

displayFrames.mDisplayCutoutSafe /* contentFrame */,

navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,

navigationFrame /* stableFrame */,

displayFrames.mDisplayCutoutSafe /* outsetFrame */);

mNavigationBar2.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);

mNavigationBar2.computeFrameLw();

mNavigationBarController2.setContentFrame(mNavigationBar2.getContentFrameLw());

if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar2 frame: " + navigationFrame);

return mNavigationBarController2.checkHiddenLw();

}

除此之外还需进行左右逻辑控制和一些布局调整,这样应用就可以通过TYPE_NAVIGATION_BAR_PANEL类型设置来显示另外一个导航栏了。