Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to load SystemUI Navigation Bar with Android

2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)05/31 Report--

Today, I would like to share with you how to achieve Android SystemUI navigation bar loading related knowledge points, detailed content, clear logic, I believe that most people still know too much about this knowledge, so share this article for your reference, I hope you have something to gain after reading this article, let's take a look at it.

Demand

Customize the navigation bar based on the MTK8163 8.1 platform, adding volume subtraction on the left and volume addition on the right

Train of thought

Before you start to do the requirements, be sure to study the code flow of the SystemUI Navigation module! Do not directly go online to copy the demand code changed by others, if you change it blindly, it is easy to have problems, but there is no way to solve it. On the Internet, there are blogs on the old platform (8.0 -) that explain the navigation bar module of System UI, and search on your own. 8.0 has made a lot of detailed changes to System UI, and there are many code changes, but the overall basic process has not changed.

Source code reading can follow a clue to the code, do not care too much about the details of the code! For example, I customized this requirement, you can follow the navigation bar return (back), desktop (home), the recent task (recent) in a function and code flow, generally know, such as recen this view which method is which method to call which method to load out, where the key code loaded, click events how to generate, regardless of the specific logic inside the judgment and so on.

Code flow

1.SystemUI\ src\ com\ android\ systemui\ statusbar\ phone\ StatusBar.java

Start with the status bar entrance.

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 (); / / create navigation bar}} catch (RemoteException ex) {}}

two。 Entering the createNavigationBar method, it is found that it is mainly managed by NavigationBarFragment.

Protected void createNavigationBar () {mNavigationBarView = NavigationBarFragment.create (mContext, (tag, fragment)-> {mNavigationBar = (NavigationBarFragment) fragment; if (mLightBarController! = null) {mNavigationBar.setLightBarController (mLightBarController);} mNavigationBar.setCurrentSysuiVisibility (mSystemUiVisibility);});}

3. Looking at NavigationBarFragment's create method, I finally know that WindowManager went to addView the layout of the navigation bar, and finally add the layout loaded by fragment's onCreateView. (in fact, all modules of SystemUI are WindowManager to load View.)

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) / / Note! The layout loaded by onCreateView in fragment is add into the view of this Window attribute. .commit (); fragmentHost.addTagListener (TAG, listener); return navigationBarView;}}

4.SystemUI\ res\ layout\ navigation_bar_window.xml

Looking at the layout of the view loaded by WindowManager: navigation_bar_window.xml, it is found that the root layout is a custom view class NavigationBarFrame. (in fact, SystemUI and other system applications such as Launcher are in this way of custom view, and a lot of logic processing is in custom view, which cannot be ignored.)

5.SystemUI\ src\ com\ android\ systemui\ statusbar\ phone\ NavigationBarFrame.java

Let's go into the NavigationBarFrame class. It is found that the class is not what we expected, just a FrameLayout, tampering with the touch event under the DeadZone function, regardless.

6. Come back and look at the life cycle of NavigationBarFragment. In onCreateView (), the real rootView of the navigation bar.

@ Overridepublic View onCreateView (LayoutInflater inflater, @ Nullable ViewGroup container, Bundle savedInstanceState) {return inflater.inflate (R.layout.navigation_bar, container, false);}

Enter the real root layout of the navigation bar: navigation_bar.xml, well, custom view,NavigationBarView and NavigationBarInflaterView should be studied carefully.

7.SystemUI\ src\ com\ android\ systemui\ statusbar\ phone\ NavigationBarInflaterView.java; inherited from FrameLayout

Look at the construction method first, because the first thing to load the xml layout is initialization

Public NavigationBarInflaterView (Context context, AttributeSet attrs) {super (context, attrs); createInflaters (); / / create the parent layout of the child view (single back home or recent) based on the screen rotation Display display = ((WindowManager) context.getSystemService (Context.WINDOW_SERVICE)). GetDefaultDisplay (); Mode displayMode = display.getMode (); isRot0Landscape = displayMode.getPhysicalWidth () > displayMode.getPhysicalHeight ();} 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 ();}

Then look at the onFinishInflate () method, which is the life cycle of view, and each view is called back after it is inflate.

@ Overrideprotected void onFinishInflate () {super.onFinishInflate (); inflateChildren (); / / ignore clearViews () if it doesn't matter; / / ignore inflateLayout (getDefaultLayout ()) if it doesn't matter; / / key method: load layout with three buttons of back.home.recent

Look at inflateLayout (): the newLayout parameter inside is very important! Seeing getDefaultLayout () from the previous method, he return a string written dead in xml. Then look at the inflateLayout method, which parses and splits the string configured in xml and passes it to the inflateButtons method

Protected void inflateLayout (String newLayout) {mCurrentLayout = newLayout; if (newLayout = = null) {newLayout = getDefaultLayout ();} String [] sets = newLayout.split (GRAVITY_SEPARATOR, 3); / / according to " "the sign is divided into an array of length 3 String [] start = sets [0] .split (BUTTON_SEPARATOR); / / based on the", "sign, it contains left [.5W] and back[ 1WC] String [] center = sets [1] .split (BUTTON_SEPARATOR); / / contains home String [] end = sets [2] .split (BUTTON_SEPARATOR); / / contains home String [1WC] and right [.5W] / / 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);} protected String getDefaultLayout () {return mContext.getString (R.string.config_navBarLayout);}

SystemUI\ res\ values\ config.xml

Left [.5W], back [1WC]; home;recent [1WC], right [.5W]

Then take a look at the inflateButtons () method, which iterates through loading inflateButton:

Private void inflateButtons (String [] buttons, ViewGroup parent, boolean landscape, boolean start) {for (int I = 0; I < buttons.length; iTunes +) {inflateButton (buttons [I], parent, landscape, start);} @ Nullableprotected View inflateButton (String buttonSpec, ViewGroup parent, boolean landscape, boolean start) {LayoutInflater inflater = landscape? MLandscapeInflater: mLayoutInflater; View v = createView (buttonSpec, parent, inflater); / / create view if (v = = null) return null; v = applySize (v, buttonSpec, landscape, start); parent.addView (v); / / addView to parent layout addToDispatchers (v); View lastView = landscape? MLastLandscape: mLastPortrait; View accessibilityView = v; if (v instanceof ReverseFrameLayout) {accessibilityView = ((ReverseFrameLayout) v) .getChildAt (0);} if (lastView! = null) {accessibilityView.setAccessibilityTraversalAfter (lastView.getId ());} if (landscape) {mLastLandscape = accessibilityView;} else {mLastPortrait = accessibilityView;} return v;}

Let's take a look at the createView () method: take the home button as an example, loading the button of home is actually loading the layout layout of R.layout.home

Private View createView (String buttonSpec, ViewGroup parent, LayoutInflater inflater) {View v = null;. 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);} else if (RECENT.equals (button)) {v = inflater.inflate (R.layout.recent_apps, parent, false) } else if (MENU_IME.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);}. Return v;} / / SystemUI\ res\ layout\ home.xml / / there is no src displaying the icon of home in the layout here. You must have set / / custom view:KeyButtonView in the code.

8.SystemUI\ src\ com\ android\ systemui\ statusbar\ policy\ KeyButtonView.java

Let's take a look at the construction of KeyButtonView: our previous systemui:keyCode= "3" method of xml is obtained here. Let's take a look at the Touch event. Through the sendEvent () method, we can see that the click touch event of view such as back is not handled by itself, but by the system in the form of keycode.

Of course, the KeyButtonView class also handles the button that supports long press, the sound of keys, and so on, which are ignored here.

At this point, we have combed the events of the navigation bar buttons.

Public KeyButtonView (Context context, AttributeSet attrs, int defStyle) {super (context, attrs); TypedArray a = context.obtainStyledAttributes (attrs, R.styleable.KeyButtonView, defStyle, 0); mCode = a.getInteger (R.styleable.KeyButtonView_keyCode, 0); mSupportsLongpress = a.getBoolean (R.styleable.KeyButtonView_keyRepeat, true); mPlaySounds = a.getBoolean (R.styleable.KeyButtonView_playSound, true); TypedValue value = new TypedValue () If (a.getValue (R.styleable.KeyButtonView_android_contentDescription, value)) {mContentDescriptionRes = value.resourceId;} a.recycle (); setClickable (true); mTouchSlop = ViewConfiguration.get (context). GetScaledTouchSlop (); mAudioManager = (AudioManager) context.getSystemService (Context.AUDIO_SERVICE); mRipple = new KeyButtonRipple (context, this); setBackground (mRipple);}. Public boolean onTouchEvent (MotionEvent ev) {. Switch (action) {case MotionEvent.ACTION_DOWN: mDownTime = SystemClock.uptimeMillis (); mLongClicked = false; setPressed (true); if (mCode! = 0) {sendEvent (KeyEvent.ACTION_DOWN, 0, mDownTime); / / key method} else {/ / Provide the same haptic feedback that the system offers for virtual keys. PerformHapticFeedback (HapticFeedbackConstants.VIRTUAL_KEY);} playSoundEffect (SoundEffectConstants.CLICK); removeCallbacks (mCheckLongPress); postDelayed (mCheckLongPress, ViewConfiguration.getLongPressTimeout ()); break;...} return true } void sendEvent (int action, int flags, long when) {mMetricsLogger.write (new LogMaker (MetricsEvent.ACTION_NAV_BUTTON_EVENT) .setType (MetricsEvent.TYPE_ACTION) .setSubtype (mCode) .addTaggedData (MetricsEvent.FIELD_NAV_ACTION, action) .addTaggedData (MetricsEvent.FIELD_FLAGS, flags)); final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS)! = 0? 1: 0 / / here there is a KeyEvent event based on mCode new, and the event is made effective through injectInputEvent. Final KeyEvent ev = new KeyEvent (mDownTime, when, action, mCode, repeatCount, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, InputDevice.SOURCE_KEYBOARD); InputManager.getInstance () .injectInputEvent (ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);}

9. There is also a question: where on earth is the icon for setting the picture? What we've been reading before is NavigationBarInflaterView, and according to the layout we have one more class to look at, NavigationBarView.java.

SystemUI\ src\ com\ android\ systemui\ statusbar\ phone\ NavigationBarView.java

Go to the NavigationBarView class and find the constructor.

Public NavigationBarView (Context context, AttributeSet attrs) {super (context, attrs); mDisplay = ((WindowManager) context.getSystemService (Context.WINDOW_SERVICE)). GetDefaultDisplay (); UpdateIcons (context, Configuration.EMPTY, mConfiguration); / / key method mBarTransitions = new NavigationBarTransitions (this); / / mButtonDispatchers is the management class that maintains these home back recent icons view, which is passed to his child,NavigationBarInflaterView class 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.menu, new ButtonDispatcher (R.id.menu)); mButtonDispatchers.put (R.id.ime_switcher, new ButtonDispatcher (R.id.ime_switcher)); mButtonDispatchers.put (R.id.casusibilityfantasy);} private void updateIcons (Context ctx, Configuration oldConfig, Configuration newConfig) {. IconLight = mNavBarPlugin.getHomeImage (ctx.getDrawable (R.drawable.ic_sysbar_home)); iconDark = mNavBarPlugin.getHomeImage (ctx.getDrawable (R.drawable.ic_sysbar_home_dark)); / / mHomeDefaultIcon = getDrawable (ctx, / / R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark); mHomeDefaultIcon = getDrawable (iconLight,iconDark) / / Light icon resource iconLight = mNavBarPlugin.getRecentImage (ctx.getDrawable (R.drawable.ic_sysbar_recent)); / / Dark icon resource iconDark = mNavBarPlugin.getRecentImage (ctx.getDrawable (R.drawable.ic_sysbar_recent_dark)); / / mRecentIcon = getDrawable (ctx, / / R.drawable.ic_sysbar_recent, R.drawable.ic_sysbar_recent_dark) MRecentIcon = getDrawable (iconLight,iconDark); mMenuIcon = getDrawable (ctx, R.drawable.ic_sysbar_menu, R.drawable.ic_sysbar_menu_dark);...}

10. As you can see from 10, take recent as an example, we get the resources of mRecentIcon during initialization, and then we can see who called mRecentIcon, that is, we can see the calling process.

Private void updateRecentsIcon () {getRecentsButton () .setImageDrawable (mDockedStackExists? MDockedIcon: mRecentIcon); mBarTransitions.reapplyDarkIntensity ();}

The updateRecentsIcon method sets the resource of the recent image, and then see who calls the updateRecentsIcon method: onConfigurationChanged screen rotation resets the resource picture.

@ Overrideprotected void onConfigurationChanged (Configuration newConfig) {super.onConfigurationChanged (newConfig); boolean uiCarModeChanged = updateCarMode (newConfig); updateTaskSwitchHelper (); updateIcons (getContext (), mConfiguration, newConfig); updateRecentsIcon (); if (uiCarModeChanged | | mConfiguration.densityDpi! = newConfig.densityDpi | | mConfiguration.getLayoutDirection ()! = newConfig.getLayoutDirection ()) {/ / If car mode or density changes, we need to reset the icons. SetNavigationIconHints (mNavigationIconHints, true);} mConfiguration.updateFrom (newConfig);} public void setNavigationIconHints (int hints, boolean force) {. 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 ();...}

Reorient () also calls the setNavigationIconHints () method:

Public void reorient () {updateCurrentView ();... SetNavigationIconHints (mNavigationIconHints, true); getHomeButton (). SetVertical (mVertical);}

Push up, and eventually trace back to NavigationBarFragment's onConfigurationChanged () method and NavigationBarView's onAttachedToWindow () and onSizeChanged () methods. In other words, when the layout of the NavigationBarView navigation bar is loaded, the picture resource will be set, and the length will change, and the screen rotation may cause the reset.

These are all the contents of the article "how to load the SystemUI navigation bar with Android". Thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report