1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
      Android8.1平臺SystemUI導(dǎo)航欄加載的示例分析

      小編給大家分享一下Android8.1平臺SystemUI導(dǎo)航欄加載的示例分析,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

      新華ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!

      需求

      基于MTK8163 8.1平臺定制導(dǎo)航欄部分,在左邊增加音量減,右邊增加音量加

      思路

      需求開始做之前,一定要研讀SystemUI Navigation模塊的代碼流程!!!不要直接去網(wǎng)上copy別人改的需求代碼,盲改的話很容易出現(xiàn)問題,然而無從解決。網(wǎng)上有老平臺(8.0-)的講解System UI的導(dǎo)航欄模塊的博客,自行搜索。8.0對System UI還是做了不少細節(jié)上的改動,代碼改動體現(xiàn)上也比較多,但是總體基本流程并沒變。

      源碼閱讀可以沿著一條線索去跟代碼,不要過分在乎代碼細節(jié)!例如我客制化這個需求,可以跟著導(dǎo)航欄的返回(back),桌面(home),最近任務(wù)(recent)中的一個功能跟代碼流程,大體知道比如recen這個view是哪個方法調(diào)哪個方法最終加載出來,加載的關(guān)鍵代碼在哪,點擊事件怎么生成,而不在意里面的具體邏輯判斷等等。

      代碼流程

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

      從狀態(tài)欄入口開始看。

      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();//創(chuàng)建導(dǎo)航欄
          }
        } catch (RemoteException ex) {
        }
      }

      2.進入 createNavigationBar 方法,發(fā)現(xiàn)主要是用 NavigationBarFragment 來管理.

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

      3.看 NavigationBarFragment 的create方法,終于知道,是WindowManager去addView了導(dǎo)航欄的布局,最終add了fragment的onCreateView加載的布局。(其實SystemUI所有的模塊都是WindowManager來加載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) //注意!fragment里onCreateView加載的布局是add到這個Window屬性的view里的。
            .commit();
        fragmentHost.addTagListener(TAG, listener);
        return navigationBarView;
       }
      }

      4.SystemUI\res\layout\navigation_bar_window.xml;

      來看WindowManager加載的這個view的布局:navigation_bar_window.xml,發(fā)現(xiàn)根布局是自定義的view類NavigationBarFrame.(其實SystemUI以及其他系統(tǒng)應(yīng)用如Launcher,都是這種自定義view的方式,好多邏輯處理也都是在自定義view里,不能忽略)

       
      
      

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

      我們進入NavigationBarFrame類。發(fā)現(xiàn)類里并不是我們的預(yù)期,就是一個FrameLayout,對DeadZone功能下的touch事件做了手腳,不管了。

      6.再回來看看NavigationBarFragment的生命周期呢。onCreateView()里,導(dǎo)航欄的真正的rootView。

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

      進入導(dǎo)航欄的真正根布局:navigation_bar.xml,好吧又是自定義view,NavigationBarView 和 NavigationBarInflaterView 都要仔細研讀。

      
      
      
      

      7.SystemUI\src\com\android\systemui\statusbar\phone\NavigationBarInflaterView.java;繼承自FrameLayout

      先看構(gòu)造方法,因為加載xml布局首先走的是初始化

      public NavigationBarInflaterView(Context context, AttributeSet attrs) {
        super(context, attrs);
        createInflaters();//根據(jù)屏幕旋轉(zhuǎn)角度創(chuàng)建子view(單個back home or recent)的父布局
        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();
      }

      再看onFinishInflate()方法,這是view的生命周期,每個view被inflate之后都會回調(diào)。

      @Override
      protected void onFinishInflate() {
        super.onFinishInflate();
        inflateChildren();//進去看無關(guān)緊要 忽略
        clearViews();//進去看無關(guān)緊要 忽略
        inflateLayout(getDefaultLayout());//關(guān)鍵方法:加載了 back.home.recent三個按鈕的layout
      }

      看inflateLayout():里面的newLayout參數(shù)很重要!!!根據(jù)上一個方法看到getDefaultLayout(),他return了一個在xml寫死的字符串。再看inflateLayout方法,他解析分割了xml里配置的字符串,并傳給了inflateButtons方法

      protected void inflateLayout(String newLayout) {
        mCurrentLayout = newLayout;
        if (newLayout == null) {
          newLayout = getDefaultLayout();
        }
        String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);//根據(jù)“;”號分割成長度為3的數(shù)組
        String[] start = sets[0].split(BUTTON_SEPARATOR);//根據(jù)“,”號分割,包含 left[.5W]和back[1WC]
        String[] center = sets[1].split(BUTTON_SEPARATOR);//包含home
        String[] end = sets[2].split(BUTTON_SEPARATOR);//包含recent[1WC]和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]

      再看inflateButtons()方法,遍歷加載inflateButton:

      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);
        }
      }
      @Nullable
      protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,
          boolean start) {
        LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;
        View v = createView(buttonSpec, parent, inflater);//創(chuàng)建view
        if (v == null) return null;
        v = applySize(v, buttonSpec, landscape, start);
        parent.addView(v);//addView到父布局
        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;
      }

      我們來看createView()方法:以home按鍵為例,加載了home的button,其實是加載了 R.layout.home 的layout布局

      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 
      //這里布局里沒有src顯示home的icon,肯定是在代碼里設(shè)置了
      //這里也是自定義view:KeyButtonView
      

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

      先來看KeyButtonView的構(gòu)造方法:我們之前xml的systemui:keyCode=”3”方法在這里獲取。再來看Touch事件,通過sendEvent()方法可以看出,back等view的點擊touch事件不是自己處理的,而是交由系統(tǒng)以實體按鍵(keycode)的形式處理的.

      當然KeyButtonView類還處理了支持長按的button,按鍵的響聲等,這里忽略。

      至此,導(dǎo)航欄按鍵事件我們梳理完畢。

      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);//關(guān)鍵方法
            } 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;
        //這里根據(jù)mCode new了一個KeyEvent事件,通過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.還遺留一個問題:設(shè)置圖片的icon到底在哪?我們之前一直閱讀的是NavigationBarInflaterView,根據(jù)布局我們還有一個類沒有看,NavigationBarView.java

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

      進入NavigationBarView類里,找到構(gòu)造方法。

      public NavigationBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mDisplay = ((WindowManager) context.getSystemService(
            Context.WINDOW_SERVICE)).getDefaultDisplay();
        ...
        ...
        updateIcons(context, Configuration.EMPTY, mConfiguration);//關(guān)鍵方法
        mBarTransitions = new NavigationBarTransitions(this);
        //mButtonDispatchers 是維護這些home back recent圖標view的管理類,會傳遞到他的child,NavigationBarInflaterView類中
        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.accessibility_button,new ButtonDispatcher(R.id.accessibility_button));
      }
       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);
          //亮色的icon資源
          iconLight = mNavBarPlugin.getRecentImage(
                        ctx.getDrawable(R.drawable.ic_sysbar_recent));
          //暗色的icon資源
          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.從第10可以看到,以recent為例,在初始化時得到了mRecentIcon的資源,再看誰調(diào)用了了mRecentIcon就可知道,即反推看調(diào)用流程。

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

      updateRecentsIcon這個方法設(shè)置了recent圖片的資源,再看誰調(diào)用了updateRecentsIcon方法:onConfigurationChanged屏幕旋轉(zhuǎn)會重新設(shè)置資源圖片

      @Override
      protected 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()也調(diào)用了setNavigationIconHints()方法:

      public void reorient() {
        updateCurrentView();
        ...
        setNavigationIconHints(mNavigationIconHints, true);
        getHomeButton().setVertical(mVertical);
      }

      再朝上推,最終追溯到NavigationBarFragment的onConfigurationChanged()方法 和 NavigationBarView的onAttachedToWindow()和onSizeChanged()方法。也就是說,在NavigationBarView導(dǎo)航欄這個布局加載的時候就會設(shè)置圖片資源,和長度改變,屏幕旋轉(zhuǎn)都有可能引起重新設(shè)置

      至此,SystemUI的虛擬導(dǎo)航欄模塊代碼流程結(jié)束。

      看完了這篇文章,相信你對“Android8.1平臺SystemUI導(dǎo)航欄加載的示例分析”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!


      分享名稱:Android8.1平臺SystemUI導(dǎo)航欄加載的示例分析
      當前網(wǎng)址:http://www.ef60e0e.cn/article/iedgcj.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        昌平区| 巫溪县| 嘉峪关市| 周口市| 海晏县| 邳州市| 普兰县| 中卫市| 墨竹工卡县| 若羌县| 郯城县| 丽水市| 池州市| 繁昌县| 方正县| 阿荣旗| 南溪县| 霍州市| 松滋市| 丰都县| 察雅县| 永清县| 昌平区| 荔浦县| 玉溪市| 青阳县| 丽江市| 彭水| 娄烦县| 水富县| 高要市| 安庆市| 乐平市| 厦门市| 浮梁县| 寿宁县| 宣武区| 曲周县| 丰镇市| 壶关县| 如东县|