当前位置: 首页 > article >正文

底部导航栏新增功能按键

场景需求:

在底部导航栏添加power案件,单击息屏,长按 关机

如下实现图
在这里插入图片描述

借此需求,需要掌握技能:

  • 底部导航栏如何实现
  • 新增、修改、删除底部导航栏流程
  • 对底部导航栏部分样式如何修改。 比如放不下、顺序排列、坑点如何修改等

修改-新增文件:

新增

\vendor\mediatek\proprietary\packages\apps\SystemUI\res\layout\power.xml         [新增底部导航 power 功能按键布局文件]
\vendor\mediatek\proprietary\packages\apps\SystemUI\res\drawable\ic_lock_power_off.xml  [新增power按键icon图标]

修改

\vendor\mediatek\proprietary\packages\apps\SystemUI\src\com\android\systemui\navigationbar\NavigationBarView.java
\vendor\mediatek\proprietary\packages\apps\SystemUI\src\com\android\systemui\navigationbar\NavigationBar.java
\vendor\mediatek\proprietary\packages\apps\SystemUI\src\com\android\systemui\navigationbar\NavigationBarInflaterView.java


需改:新增 power 功能菜单
\vendor\mediatek\proprietary\packages\apps\SystemUI\res\values\config.xml      
\vendor\mediatek\proprietary\packages\apps\SystemUI\res\values-sw900dp\config.xml
\vendor\mediatek\proprietary\packages\apps\SystemUI\res\values-sw600dp\config.xml

//按键太多,设置按键宽度,让竖屏情况下多个功能虚拟按键能够正常显示出来:我们对应的dimens 文件为values-sw600dp  故,只修改这个文件即可
/vendor/mediatek/proprietary/packages/apps/SystemUI/res/values-sw600dp/dimens.xml


具体修改点

修改点 一)
功能按键添加:power

<string name="config_navBarLayout" translatable="false">left;volume_sub,back,home,recent,wb_sunny,volume_add,power,screenshot;right</string>

修改点 二)
虚拟按键宽度和padding 重写设置:

<dimen name="navigation_key_width">90dp</dimen>

修改点 三)

NavigationBarView.java            [设置按键图标、按键放到集合中,便于封装操作]
NavigationBar.java                 [点触事件 长按、短按事件]
NavigationBarInflaterView.java   [加载布局,加载功能按键view]
见修改文件  wang 相关 add 和 end 结束位置
下文 详细说明

参考资料

Android 8.1平台SystemUI 导航栏加载流程解析:
Android 9.0 SystemUI NavigationBar
Android13 关于SystemUI更新Nav Bar add volume button && other button
Android 导航栏功能项的显示与屏蔽
Android 12 自定义底部导航栏
Android11 底部导航栏添加虚拟按钮-问题合集

实现思路和具体方案

在实现方案之前,一脸蒙蔽,那是缺乏一定的知识储备,实现后发现思路很清晰的,理解流程,一步一步实现即可。可以参考现有的实现方案,比如:home/back/recent 部分Android版本
本身就已经有的功能,照葫芦画瓢。

主要思路如下:

  • 创建功能按键布局layout[对应 xml]:参考已有的功能按键
  • 配置文件中,新增功能按键菜单,只有有了默认的配置才会加载上面的布局View
  • NavigationBar为系统加载的组件,在组件中实现点击事件,在关联的加载布局View 类
    NavigationBarInflaterView.java 和 NavigationBarView.java 实现布局的功能按键View的添加和功能view 的获取/设置View背景等,最终实现功能按键的加载。

布局创建

比如我们的power.xml

<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.navigationbar.buttons.KeyButtonView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/power"
    android:layout_width="@dimen/navigation_key_width"
    android:layout_height="match_parent"
    android:layout_weight="0"
    systemui:keyCode="26"
    android:scaleType="center"
    android:contentDescription="@string/accessibility_home"
    android:paddingStart="@dimen/navigation_key_padding"
    android:paddingEnd="@dimen/navigation_key_padding"
    />

那为什么这么创建? 找一下 已有的 功能按键 比如Home 按键:

<com.android.systemui.navigationbar.buttons.KeyButtonView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/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"
    />

这里有三个点需要注意:

  • layout_width 值的设置,底部导航栏功能按键过多,会出现不会显示全的问题,需要适配。
  • keyCode,有keyCode设置,会根据KeyButtonView 监听到keyCode
    按键值,这样可以监听并执行对应逻辑。【此需求暂无用到】
  • id: 这个id 在NavigationBarView,中的 mButtonDispatchers.put(R.id.power, new
    ButtonDispatcher(R.id.power)); 对应 匹配,不然找不到id。

布局加载

添加到NavigationBarInflaterView中去,NavigationBarInflaterView 见名知意。本身代码量不多的,就是一个View,加载功能按键子View

NavigationBarInflaterView extends FrameLayout ,正常的一个FragmentLayout ,如下举例几个简单方法

加载配置

 protected String getDefaultLayout() {
        //wangfangchen add 
		/*final int defaultResource = QuickStepContract.isGesturalMode(mNavBarMode)
                ? R.string.config_navBarLayoutHandle
                : mOverviewProxyService.shouldShowSwipeUpUI()
                        ? R.string.config_navBarLayoutQuickstep
                        : R.string.config_navBarLayout;
		*/
		final int defaultResource =		 R.string.config_navBarLayout;
		Log.d(TAG,"getDefaultLayout  "+getContext().getString(defaultResource));
		//wangfangchen end  
        return getContext().getString(defaultResource);
    }

所以我们需要新增功能按键就需要在配置 文件中添加功能按键:power

<string name="config_navBarLayout" translatable="false">left;volume_sub,back,home,recent,wb_sunny,volume_add,power,screenshot;right</string>

加载子View

@Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        inflateChildren();
        clearViews();
        inflateLayout(getDefaultLayout());
    }

   protected void inflateLayout(String newLayout) {
        mCurrentLayout = newLayout;
        if (newLayout == null) {
            newLayout = getDefaultLayout();
        }
        String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);
		Log.d(TAG, "inflateLayout  newLayout:"+newLayout);

        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, mHorizontal.findViewById(R.id.ends_group),
                false /* landscape */, true /* start */);
        inflateButtons(start, mVertical.findViewById(R.id.ends_group),
                true /* landscape */, true /* start */);

        inflateButtons(center, mHorizontal.findViewById(R.id.center_group),
                false /* landscape */, false /* start */);
        inflateButtons(center, mVertical.findViewById(R.id.center_group),
                true /* landscape */, false /* start */);

        addGravitySpacer(mHorizontal.findViewById(R.id.ends_group));
        addGravitySpacer(mVertical.findViewById(R.id.ends_group));

        inflateButtons(end, mHorizontal.findViewById(R.id.ends_group),
                false /* landscape */, false /* start */);
        inflateButtons(end, mVertical.findViewById(R.id.ends_group),
                true /* landscape */, false /* start */);

        updateButtonDispatchersCurrentView();
    }


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

创建 子view:createView

@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;

        v = applySize(v, buttonSpec, landscape, start);
        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;
    }

根据配置中的功能按键 名字字符串,加载对应的布局,也就是加载了view 到 NavigationBarView

View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {
		Log.d(TAG,"createView   buttonSpec:"+buttonSpec);
        View v = null;
        String button = extractButton(buttonSpec);
        if (LEFT.equals(button)) {
            button = extractButton(NAVSPACE);
        } else if (RIGHT.equals(button)) {
            button = extractButton(MENU_IME_ROTATE);
        }
        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);
        } else if (HOME_HANDLE.equals(button)) {
            v = inflater.inflate(R.layout.home_handle, parent, false);
        } else if (IME_SWITCHER.equals(button)) {
            v = inflater.inflate(R.layout.ime_switcher, parent, false);
        //huanghb add
        } else if (VOLUME_ADD.equals(button)) {
            v = inflater.inflate(R.layout.volume_add, parent, false);
        } else if (VOLUME_SUB.equals(button)) {
            v = inflater.inflate(R.layout.volume_sub, parent, false);
        } else if (SCREENSHOT.equals(button)) {
            v = inflater.inflate(R.layout.screenshot, parent, false);
        } 
        //wangfangchen add 
		else if (POWER.equals(button)) {
            v = inflater.inflate(R.layout.power, parent, false);
        } 
		 //wangfangchen end 
		else if (WB_SUNNY.equals(button)) {
            v = inflater.inflate(R.layout.wb_sunny, parent, false);
        } else if (button.startsWith(KEY)) {
			Log.d(TAG,"createView   buttonSpec:"+buttonSpec+"   button:"+button+"   KEY:"+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;
    }

加载布局View NavigationBarInflaterView

上面已经分析了NavigationBarInflaterView 如何加载子功能按键,那么这个View 是在哪里已经并初始化呢?
看布局:navigation_bar.xm


<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.navigationbar.NavigationBarView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/navigation_bar_view"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:background="@drawable/system_bar_background">

    <com.android.systemui.navigationbar.NavigationBarInflaterView
        android:id="@+id/navigation_inflater"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false" />

</com.android.systemui.navigationbar.NavigationBarView>

找navigation_bar.xml 加载地方
1)NavigationBar.java oreateView 方法
在这里插入图片描述

备注:NavigationBar 就暂时不分析了,可以参考其它博客或自己的System UI加载流程。

NavigationBarView View的封装

上面分析 NavigationBarView 嵌套了 NavigationBarInflaterView,也就是对NavigationBarInflaterView 的一层封装,



private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
放置:view 到数组
        //huanghb add
        mButtonDispatchers.put(R.id.volume_add, new ButtonDispatcher(R.id.volume_add));
        mButtonDispatchers.put(R.id.volume_sub, new ButtonDispatcher(R.id.volume_sub));
        mButtonDispatchers.put(R.id.screenshot, new ButtonDispatcher(R.id.screenshot));
        mButtonDispatchers.put(R.id.wb_sunny, new ButtonDispatcher(R.id.wb_sunny));
        //huanghb end
		
		  //wangfangchen add		
	     mButtonDispatchers.put(R.id.power, new ButtonDispatcher(R.id.power));
		  //wangfangchen end		

从通过view 数组,获取子view

      return mButtonDispatchers.get(R.id.recent_apps);
    }

    public ButtonDispatcher getBackButton() {
        return mButtonDispatchers.get(R.id.back);
    }

    public ButtonDispatcher getHomeButton() {
        return mButtonDispatchers.get(R.id.home);
    }

    public ButtonDispatcher getImeSwitchButton() {
        return mButtonDispatchers.get(R.id.ime_switcher);
    }

    public ButtonDispatcher getAccessibilityButton() {
        return mButtonDispatchers.get(R.id.accessibility_button);
    }
	
这不就是对子菜单功能按键的一层封装吗?	

子布局显示并控制NavigationBar

在上面已经通过分析了布局,反向关联了NavigationBarInflaterView 加载->NavigationBar 加载。就从这个角度来分析布局显示出来后如何控制。

看部分相关方法并分析

准备View prepareNavigationBarView()

  private void prepareNavigationBarView() {
		Log.d(TAG,"prepareNavigationBarView");
        mNavigationBarView.reorient();

        ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
        recentsButton.setOnClickListener(this::onRecentsClick);
        recentsButton.setOnTouchListener(this::onRecentsTouch);

        ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
        homeButton.setOnTouchListener(this::onHomeTouch);

        reconfigureHomeLongClick();

        ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
        accessibilityButton.setOnClickListener(this::onAccessibilityClick);
        accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
        updateAccessibilityServicesState(mAccessibilityManager);

        ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
        imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);

        updateScreenPinningGestures();
        //huanghb add
        //Log.d("huanghb","prepareNavigationBarView");
        ShowVolumeButton();
        ShowScreenShotButton();
        ShowWbSunnyButton();
        //huanghb end
		
		//wangfangchen add 
		 ShowPowerButton();
		//wangfangchen end 
		
    }

设置点击事件 长按事件

//wangfangchen add 
    private void ShowPowerButton(){
		Log.d(TAG,"ShowPowerButton");
        ButtonDispatcher powerButton = mNavigationBarView.getPowerButton();
       // String customScreenshot = SystemProperties.get("persist.fise.screenshot.icon","0");
        powerButton.setOnClickListener(this::onPowerClick);
        //powerButton.setOnTouchListener(this::onScreenShotTouch);
	    powerButton.setOnLongClickListener(this::onPowerLongClick);
        powerButton.setVisibility(View.VISIBLE); 
    }
    //wangfangchen end 	
	
	
	//wangfangchen add 
    private void onPowerClick(View v) {
        Log.d(TAG,"onPowerClick");
		 mHandler.removeCallbacks(mPower);
		 mHandler.postDelayed(mPower , 200);
    }
	

   private boolean onPowerLongClick(View v) {
	    Log.d(TAG,"onPowerLongClick");
		    setActionNavigationbar();
			mHandler.removeCallbacks(mPowerReboot);
            mHandler.postDelayed(mPowerReboot, 200);
		
        return  true;
    }

	//wangfangchen end 

在对应的线程里面执行逻辑即可

总结

上面分析就很明朗了,需要搞清楚
1)功能按键 布局,如何加载
2)配置 功能菜单 ,如何配置
2)三个类:NavigationBarView NavigationBar NavigationBarInflaterView 联系


http://www.kler.cn/a/421566.html

相关文章:

  • python脚本:Word文档批量转PDF格式
  • pytest自定义命令行参数
  • Redis+Caffeine 多级缓存数据一致性解决方案
  • 底部导航栏新增功能按键
  • linux模拟试题
  • oracle数据库日常操作
  • 类加载子系统
  • Java开发利器:IDEA的安装与使用(上)
  • 【音视频】HLS和DASH 流媒体协议的详细介绍和实现方式
  • C++知识整理day3类与对象(下)——赋值运算符重载、取地址重载、列表初始化、友元、匿名对象、static
  • git推送多个仓库
  • 十,[极客大挑战 2019]Secret File1
  • uniapp 自定义导航栏增加首页按钮,仿微信小程序操作胶囊
  • Flink 热存储维表 使用 Guava Cache 减轻访问压力
  • JVM 内存结构 详解
  • postgresql导出/导入数据库
  • 环境安装与配置:全面了解 Go 语言的安装与设置
  • 【linux】(26)shell脚本-变量、位置变量
  • LeetCode—560. 和为 K 的子数组(中等)
  • Windows远程桌面连接到Linux
  • 计算机视觉硬件知识点整理六:工业相机选型
  • C++ Qt——从入门到入土 (二)
  • PyTorch 实现动态输入
  • 快速讲图片中的公式粘贴到word中
  • 大模型安全科技发展仍处在起步阶段
  • 【AI系统】昇腾异构计算架构 CANN