android9 SystemUI-NavigationBar

一.NavigationBar的创建

NavigationBar的创建是从StatusBar.makeStatusBarView 开始的

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

在NavigationBarFragment的onCreate方法中创建了mNavigationBarView:

public class NavigationBarFragment  ...{
    
    
   public static View create(Context context, FragmentListener listener) {
    
    
       ...
       View navigationBarView = LayoutInflater.from(context).inflate(
                R.layout.navigation_bar_window, null);         
       context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
       NavigationBarFragment fragment = new NavigationBarFragment();
       fragmentHost.getFragmentManager().beginTransaction()
                .replace(R.id.navigation_bar_frame, fragment, TAG)
                .commit();
       ...
       return navigationBarView;
   }
}

WindowManager添加了navigation_bar_window布局,接着新键NavigationBarFragment,NavigationBarFragment.onCreateView:

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

onCreateView加载了导航栏的根布局R.layout.navigation_bar.xml

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

二.每个按钮创建流程

导航栏的容器为NavigationBarInflaterView,继承自FrameLayout:

public class NavigationBarInflaterView extends FrameLayout{
    
    
    ...
    @Override
    protected void onFinishInflate() {
    
    
        super.onFinishInflate();
        inflateChildren();
        clearViews();
        inflateLayout(getDefaultLayout());
    }
    ...
    protected String getDefaultLayout() {
    
    
        final int defaultResource = mOverviewProxyService.shouldShowSwipeUpUI()
                ? R.string.config_navBarLayoutQuickstep
                : R.string.config_navBarLayout;
        return mContext.getString(defaultResource);
    }
}

当NavigationBarInflaterView实例化完成之后调用了inflateLayout方法,参数由getDefaultLayout方法返回,对应的字符串在config.xml中配置:

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

可以看到包含了back、home、recent这些字符串,那么是怎么处理的呢?
NavigationBarInflaterView.inflateLayout():

protected void inflateLayout(String newLayout) {
    
    
    mCurrentLayout = newLayout;
    if (newLayout == null) {
    
    
        newLayout = getDefaultLayout();
    }
    ...
   //GRAVITY_SEPARATOR = ";";
    String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);
   //BUTTON_SEPARATOR = ",";
    String[] start = sets[0].split(BUTTON_SEPARATOR);
    String[] center = sets[1].split(BUTTON_SEPARATOR);
    String[] end = sets[2].split(BUTTON_SEPARATOR);
    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);
    inflateButtons(end, mRot0.findViewById(R.id.ends_group), isRot0Landscape, false);
    inflateButtons(end, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, false);
    ...
}

可以看到先用“;”号分割得到sets,因为配置中有两个";" 所以sets为长度为3的数组,在对数组中每个字符用“,”分割得到start、center、end三个数组。分成三部分后再转入inflateButtons方法:

   private void inflateButtons(String[] buttons, ViewGroup parent, boolean landscape,
            boolean start) {
    
    
        for (int i = 0; i < buttons.length; i++) {
    
    
            inflateButton(buttons[i], parent, landscape, start);
        }
    }

inflateButtons遍历数组调用inflateButton方法:

 @Nullable
    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;
        ...
        return v;
    }

最终每个字符传到了createView方法:

 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_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);
        } 
        ...
        return v;
    }

createView方法根据传入的字符串创建对应的布局,如home对应R.layout.home、back对应R.layout.back。。。
至此,导航栏每个按钮对应布局终于加载到了。

最后回到NavigationBarFragment.onViewCreated:

 @Override
 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    
    
    ...
    prepareNavigationBarView();
    ...
 }

view创建完之后调用了prepareNavigationBarView方法:

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();
    }

prepareNavigationBarView方法获取了home键、返回键及任务键,并添加了点击事件Listener。

三.总结

根据上述分析,在导航栏添加按键大致需要以下步骤:
1.修改默认布局配置config.xml中的字段:config_navBarLayout
2. NavigationBarInflaterView中,inflateLayout(String newLayout)方法中对增加的字段进行解析。
3.NavigationBarInflaterView中,inflateButton()方法处增加相应的创建Button View的方法。
4.PhoneStatusBar中,添加按键点击事件处理方法并在prepareNavigationBarView()方法中设置点击事件,设置按键可见。
5.NavigationBarView构造方法里添加到mButtonDisatchers集合,
如:mButtonDisatchers.put(R.id.back, new ButtonDispatcher(R.id.back));

猜你喜欢

转载自blog.csdn.net/weixin_40652755/article/details/122687930
今日推荐