100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Android SystemUI之NavigationBar 导航栏(四)

Android SystemUI之NavigationBar 导航栏(四)

时间:2020-05-23 06:10:27

相关推荐

Android SystemUI之NavigationBar 导航栏(四)

Android SystemUI系列:

1.Android SystemUI之启动流程(一)

2.Android SystemUI之StatusBar,状态栏(二)

3.Android SystemUI之下拉菜单,通知栏,快捷面板(三)

4.Android SystemUI之NavigationBar,导航栏(四)

5.Android SystemUI之Recent,近期列表(五)

一、导航栏的创建

1.先上一幅导航栏的View结构图。如下。

2.导航栏的创建

StatusBar.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 = NavigationBarFragment.create(mContext, (tag, fragment) -> {mNavigationBar = (NavigationBarFragment) fragment;if (mLightBarController != null) {mNavigationBar.setLightBarController(mLightBarController);}mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);});}

NavigationBarFragment.create

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.accessibilityTitle = context.getString(R.string.nav_bar);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;}

上述代码做了两件事:1.创建navigationBarView 并且把navigationBarView添加到windowManager中。

2.创建NavigationBarFragment 替换navigation_bar_window的布局文件,改成navigation_bar

navigation_bar.xml

<com.android.systemui.statusbar.phone.NavigationBarViewxmlns:android="/apk/res/android"xmlns:systemui="/apk/res-auto"android:layout_height="match_parent"android:layout_width="match_parent"android:background="@drawable/system_bar_background"><com.android.systemui.statusbar.phone.NavigationBarInflaterViewandroid:id="@+id/navigation_inflater"android:layout_width="match_parent"android:layout_height="match_parent" /></com.android.systemui.statusbar.phone.NavigationBarView>

我们先看NavigationBarInflaterView

public NavigationBarInflaterView(Context context, AttributeSet attrs) {super(context, attrs);createInflaters();mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();Mode displayMode = mDisplay.getMode();isRot0Landscape = displayMode.getPhysicalWidth() > displayMode.getPhysicalHeight();mOverviewProxyService = Dependency.get(OverviewProxyService.class);}private void createInflaters() {mLayoutInflater = LayoutInflater.from(mContext);Configuration landscape = new Configuration();landscape.setTo(mContext.getResources().getConfiguration());landscape.orientation = Configuration.ORIENTATION_LANDSCAPE;mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape));}@Overrideprotected void onFinishInflate() {super.onFinishInflate();inflateChildren();//添加mRot0和mRot90,就是横竖屏的布局clearViews();//清空所有的viewinflateLayout(getDefaultLayout());}private void inflateChildren() {removeAllViews();mRot0 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout, this, false);mRot0.setId(R.id.rot0);addView(mRot0);mRot90 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_rot90, this,false);mRot90.setId(R.id.rot90);addView(mRot90);updateAlternativeOrder();}

上述代码就是在加载导航栏布局文件,如果是0度的时候加载navigation_layout,如果是90度的时候加载navigation_layout_rot90,横屏竖屏加载的layout不同。

protected void inflateLayout(String newLayout) {mCurrentLayout = newLayout;if (newLayout == null) {newLayout = getDefaultLayout();}String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);if (sets.length != 3) {Log.d(TAG, "Invalid layout.");newLayout = getDefaultLayout();sets = newLayout.split(GRAVITY_SEPARATOR, 3);}String[] start = sets[0].split(BUTTON_SEPARATOR);String[] center = sets[1].split(BUTTON_SEPARATOR);String[] end = sets[2].split(BUTTON_SEPARATOR);// Inflate these in start to end order or accessibility traversal will be messed up.inflateButtons(start, mRot0.findViewById(R.id.ends_group), isRot0Landscape, true);inflateButtons(start, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, true);inflateButtons(center, mRot0.findViewById(R.id.center_group), isRot0Landscape, false);inflateButtons(center, mRot90.findViewById(R.id.center_group), !isRot0Landscape, false);addGravitySpacer(mRot0.findViewById(R.id.ends_group));addGravitySpacer(mRot90.findViewById(R.id.ends_group));inflateButtons(end, mRot0.findViewById(R.id.ends_group), isRot0Landscape, false);inflateButtons(end, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, false);updateButtonDispatchersCurrentView();}

protected String getDefaultLayout() {final int defaultResource = mOverviewProxyService.shouldShowSwipeUpUI()? R.string.config_navBarLayoutQuickstep: R.string.config_navBarLayout;return mContext.getString(defaultResource);}

protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,boolean start) {LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;View v = createView(buttonSpec, parent, inflater);if (v == null) return null;v = applySize(v, buttonSpec, landscape, start);//计算view的宽的大小parent.addView(v);addToDispatchers(v);View lastView = landscape ? mLastLandscape : mLastPortrait;View accessibilityView = v;if (v instanceof ReverseRelativeLayout) {accessibilityView = ((ReverseRelativeLayout) v).getChildAt(0);}if (lastView != null) {accessibilityView.setAccessibilityTraversalAfter(lastView.getId());}if (landscape) {mLastLandscape = accessibilityView;} else {mLastPortrait = accessibilityView;}return v;}

private View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {View v = null;String button = extractButton(buttonSpec);if (LEFT.equals(button)) {String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, NAVSPACE);button = extractButton(s);} else if (RIGHT.equals(button)) {String s = Dependency.get(TunerService.class).getValue(NAV_BAR_RIGHT, MENU_IME_ROTATE);button = extractButton(s);}// Let plugins go first so they can override a standard view if they want.for (NavBarButtonProvider provider : mPlugins) {v = provider.createView(buttonSpec, parent);if (v != null) return v;}if (HOME.equals(button)) {v = inflater.inflate(R.layout.home, parent, false);} else if (BACK.equals(button)) {v = inflater.inflate(R.layout.back, parent, false);} //laiyw add for volume Up/Downelse if (VOLUME_DOWN.equals(button)) {v = inflater.inflate(R.layout.volume_down, parent, false);}else if (VOLUME_UP.equals(button)) {v = inflater.inflate(R.layout.volume_up, parent, false);//laiyw add for volume Up/Down} else if (RECENT.equals(button)) {v = inflater.inflate(R.layout.recent_apps, parent, false);} else if (MENU_IME_ROTATE.equals(button)) {v = inflater.inflate(R.layout.menu_ime, parent, false);} else if (NAVSPACE.equals(button)) {v = inflater.inflate(R.layout.nav_key_space, parent, false);} else if (CLIPBOARD.equals(button)) {v = inflater.inflate(R.layout.clipboard, parent, false);} else if (CONTEXTUAL.equals(button)) {v = inflater.inflate(R.layout.contextual, parent, false);} else if (button.startsWith(KEY)) {String uri = extractImage(button);int code = extractKeycode(button);v = inflater.inflate(R.layout.custom_key, parent, false);((KeyButtonView) v).setCode(code);if (uri != null) {if (uri.contains(":")) {((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));} else if (uri.contains("/")) {int index = uri.indexOf('/');String pkg = uri.substring(0, index);int id = Integer.parseInt(uri.substring(index + 1));((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));}}}return v;}

上述的代码都比较简单,有两点:

第一、导航栏显示哪些控件是由getDefaultLayout来决定。

<stringname="config_navBarLayout"translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string>

一般情况下我们是home,recent,back这三个键,如果你需要加其他的就在这个配置文件夹。同时在createView添加对应的布局文件。

第二、createView方法创建对应的布局文件,并且添加到导航栏中。

home.xml

<com.android.systemui.statusbar.policy.KeyButtonViewxmlns:android="/apk/res/android"xmlns:systemui="/apk/res-auto"android:id="@+id/home"android:layout_width="@dimen/navigation_key_width"android:layout_height="match_parent"android:layout_weight="0"systemui:keyCode="3"android:scaleType="center"android:contentDescription="@string/accessibility_home"android:paddingStart="@dimen/navigation_key_padding"android:paddingEnd="@dimen/navigation_key_padding"/>

back.xml

<com.android.systemui.statusbar.policy.KeyButtonViewxmlns:android="/apk/res/android"xmlns:systemui="/apk/res-auto"android:id="@+id/back"android:layout_width="@dimen/navigation_key_width"android:layout_height="match_parent"android:layout_weight="0"systemui:keyCode="4"android:scaleType="center"android:contentDescription="@string/accessibility_back"android:paddingStart="@dimen/navigation_key_padding"android:paddingEnd="@dimen/navigation_key_padding"/>

recent_app.xml

<com.android.systemui.statusbar.policy.KeyButtonViewxmlns:android="/apk/res/android"xmlns:systemui="/apk/res-auto"android:id="@+id/recent_apps"android:layout_width="@dimen/navigation_key_width"android:layout_height="match_parent"android:layout_weight="0"android:scaleType="center"android:contentDescription="@string/accessibility_recent"android:paddingStart="@dimen/navigation_key_padding"android:paddingEnd="@dimen/navigation_key_padding"/>

那么我们现在布局文件都添加完成了,但是你会发现在NavigationBarInflaterView没有对资源文件添加的代码已经控件点击触摸事件处理逻辑。那么这两部分代码在哪里呢?

答案是:

1.NavigationBarView 完成资源文件添加。

2.NavigationBarFragment 添加点击事件和触摸事件的处理逻辑。

3.NavigationBarView

public NavigationBarView(Context context, AttributeSet attrs) {super(context, attrs);mDisplay = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();mVertical = false;mShowMenu = false;mShowAccessibilityButton = false;mLongClickableAccessibilityButton = false;mOverviewProxyService = Dependency.get(OverviewProxyService.class);mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);mConfiguration = new Configuration();mConfiguration.updateFrom(context.getResources().getConfiguration());M : New plugin architecture - Navigation bar plugintry {mSystemUICustomizationFactory =OpSystemUICustomizationFactoryBase.getOpFactory(context);mNavBarPlugin = mSystemUICustomizationFactory.makeNavigationBar(context);} catch (Exception e) {if (DEBUG) Log.d(TAG,"mNavBarPlugin init fail");e.printStackTrace();}reloadNavIcons();//初始化加载资源,主要是图片。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(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));mButtonDispatchers.put(R.id.volume_down, new ButtonDispatcher(R.id.volume_down));mButtonDispatchers.put(R.id.volume_up, new ButtonDispatcher(R.id.volume_up));getVolumeDownButton().setLongClickable(false);getVolumeUpButton().setLongClickable(false);mButtonDispatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu));mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));mButtonDispatchers.put(R.id.accessibility_button,new ButtonDispatcher(R.id.accessibility_button));mButtonDispatchers.put(R.id.rotate_suggestion,new ButtonDispatcher(R.id.rotate_suggestion));mButtonDispatchers.put(R.id.menu_container,new ButtonDispatcher(R.id.menu_container));mDeadZone = new DeadZone(this);}

private void reloadNavIcons() {updateIcons(mContext, Configuration.EMPTY, mConfiguration);}

private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);if (oldConfig.orientation != newConfig.orientation|| oldConfig.densityDpi != newConfig.densityDpi) {mDockedIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_docked);mHomeDefaultIcon = getHomeDrawable(lightContext, darkContext);}if (oldConfig.densityDpi != newConfig.densityDpi|| oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {Log.d("yangxiucheng", "oldConfig.densityDpi != newConfig.densityDpi");mBackIcon = getBackDrawable(lightContext, darkContext);mRecentIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_recent);mMenuIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_menu);mAccessibilityIcon = getDrawable(lightContext, darkContext,R.drawable.ic_sysbar_accessibility_button, false /* hasShadow */);//mImeIcon = getDrawable(lightContext, darkContext, R.drawable.ic_ime_switcher_default,//false /* hasShadow */);updateRotateSuggestionButtonStyle(mRotateBtnStyle, false);if (ALTERNATE_CAR_MODE_UI) {updateCarModeIcons(ctx);}}mVolumeDown = getDrawable(ctx,R.drawable.ic_sysbar_volume_down,R.drawable.ic_sysbar_volume_down_dark);mVolumeUp = getDrawable(ctx,R.drawable.ic_sysbar_volume_up,R.drawable.ic_sysbar_volume_up_dark);}

上述代码都比较简单,可以结合源码看一些细节,就不做太多说明

4.NavigationBarFragment

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);mNavigationBarView = (NavigationBarView) view;mNavigationBarView.setDisabledFlags(mDisabledFlags1);mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel());mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);mNavigationBarView.setOnTouchListener(this::onNavigationTouch);if (savedInstanceState != null) {mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);}prepareNavigationBarView();//添加home ,recent触摸事件回调checkNavBarModes();setDisabled2Flags(mDisabledFlags2);IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);filter.addAction(Intent.ACTION_SCREEN_ON);filter.addAction(Intent.ACTION_USER_SWITCHED);getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);notifyNavigationBarScreenOn();mOverviewProxyService.addCallback(mOverviewProxyListener);}

private void prepareNavigationBarView() {mNavigationBarView.reorient();//近期列表安静控制ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();recentsButton.setOnClickListener(this::onRecentsClick);recentsButton.setOnTouchListener(this::onRecentsTouch);recentsButton.setLongClickable(true);recentsButton.setOnLongClickListener(this::onLongPressBackRecents);ButtonDispatcher backButton = mNavigationBarView.getBackButton();backButton.setLongClickable(true);ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();homeButton.setOnTouchListener(this::onHomeTouch);homeButton.setOnLongClickListener(this::onHomeLongClick);ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();accessibilityButton.setOnClickListener(this::onAccessibilityClick);accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);updateAccessibilityServicesState(mAccessibilityManager);ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);updateScreenPinningGestures();}

上述代码中的setOnClickListener,setOnTouchListener,setLongClickable,setOnLongClickListener就是给对应的控件添加控制代码

到现在为止整个导航栏的View的添加,资源图片的加载,以及点击触摸事件的响应逻辑都讲了,其中还有一些细节,结合源码看应该不难,导航栏的讲解也就到此结束。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。