动态更换app启动图标

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HUandroid/article/details/78831250
  1. 类似于双11与双12活动,app的启动图标会自动进行更换。原理很简单,通过PackageManager来启动或者禁止组件。
  2. 预埋多个app入口,放置不同的icon。通过改变启动的activity来更换图片

    常规启动
    首先看下在清单文件中常规启动设置如下:

<activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

action 代表了系统中已经定义了一系列常用的动作, ACTION_MAIN:Android Application的入口,每个Android应用必须且只能包含一个此类型的Action声明。 
category 指定当前动作(Action)被执行的环境 ,CATEGORY_LAUNCHER:设置该组件为在当前应用程序启动器中优先级最高的Activity,通常为入口ACTION_MAIN配合使用。 

activity-alias介绍
这里首先要介绍下清单文件中一个不常用的标签,activity-alias,是作为一个已存在的activity的别名,则应该可以通过该别名标签声明快速打开目标Activity。因此activity-alias可用来设置某个Activity的快捷入口,可以放在桌面上或者通过该别名被其他组件快速调起。常用属性如下:

  • android:enable 该属性用来决定目标Activity可否通过别名被系统实例化,默认为true。需要注意的是application也有enable属性,只用当它们同时为true时,activity-alias的enable才生效。是否为启动页
  • android:exported 该属性为true的话,则目标Activity可被其他应用调起,如为false则只能被应用自身调起。其默认值根据activity-alias是否包含intent-filter元素决定,如果有的话,则默认为true;没有的话则为false。其实也很好理解,如果有intent-filter,则目标Activity可以匹配隐式Intent,因此可被外部应用唤起;如果没有intent-filter,则目标Activity要被调起的话必须知道其精确类名,因为只有应用本身才知道精确类名,所以此时默认为false。
  • android:icon 该属性允许自定义icon,可以不同于应用本身在桌面的icon。如果需要在桌面上创建快捷入口,也许产品会要求换个不同的icon。设置不同启动图标
  • android:label 该属性类似于android:icon,设置不同名称。
  • android:name 。activity元素的name属性实质上都会指向一个具体的Activity类,而activity-alias的name属性仅作为一个唯一标识而已。
  • android:permission 该属性指明了通过别名声明调起目标Activity所必需的权限。
  • android:targetActivity 该属性指定了目标Activity,即通过activity-alias调起的Activity是哪个,此属性其实类似于activity标签中的name属性,需要规范的Activity包名类名。更换图标必须指向同一activity

    多个启动入口清单文件配置

<activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <activity-alias
            android:name=".Test11"
            android:label="切换标签11"
            android:enabled="false"
            android:targetActivity=".MainActivity"
            android:icon="@mipmap/laugch_11"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".Test12"
            android:label="切换标签12"
            android:enabled="false"
            android:targetActivity=".MainActivity"
            android:icon="@mipmap/laugch_12"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity-alias>

代码中动态设置app入口
要做到动态切换预埋的别名入口,需要用到PackageManager来管理组件,其中有setComponentEnabledSetting来启动或者禁止组件,这里就是来禁止默认,启动双11的别名入口达到更换app图标。浅层次源码:

 /**
     * Set the enabled setting for a package component (activity, receiver, service, provider).
     * This setting will override any enabled state which may have been set by the component in its
     * manifest.
     *
     * @param componentName The component to enable
     * @param newState The new enabled state for the component.  The legal values for this state
     *                 are:
     *                   {@link #COMPONENT_ENABLED_STATE_ENABLED},
     *                   {@link #COMPONENT_ENABLED_STATE_DISABLED}
     *                   and
     *                   {@link #COMPONENT_ENABLED_STATE_DEFAULT}
     *                 The last one removes the setting, thereby restoring the component's state to
     *                 whatever was set in it's manifest (or enabled, by default).
     * @param flags Optional behavior flags: {@link #DONT_KILL_APP} or 0.
     */
    public abstract void setComponentEnabledSetting(ComponentName componentName,
            int newState, int flags);

setComponentEnabledSetting参数介绍
componentName: 组件名字
newState:组件新的状态,可以设置三个值,分别是如下:
不可用状态:COMPONENT_ENABLED_STATE_DISABLED
可用状态:COMPONENT_ENABLED_STATE_ENABLED
默认状态:COMPONENT_ENABLED_STATE_DEFAULT
flags:行为标签,值可以是DONT_KILL_APP或者0。 0说明杀死包含该组件的app

了解了功能就可以在代码中设置了:

拿到包管理器和三个组件

//代码省略
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //拿到package管理器
        mPM = getApplicationContext().getPackageManager();

        //默认的启动页组件
        mComponent = getComponentName();
        //双11组件
        mComponent11 = new ComponentName(getBaseContext(), "com.hu.test.Test11");
        //双12组件
        mComponent12 = new ComponentName(getBaseContext(), "com.hu.test.Test12");
//代码省略

设置两个方法来启动和关闭某个组件

 /**
     * 开启某个组件
     * @param componentName
     */
    private void enableComponent(ComponentName componentName){

        mPM.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);
    }

    /**
     * 关闭某个组件
     * @param componentName
     */
    private void disabledComponent(ComponentName componentName){

        mPM.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
    }

根据情况来启动某个组件设置3个方法如下:

/**
     * 启动为双11
     */
    private void changeDouble11(){
        disabledComponent(mComponent);
        disabledComponent(mComponent12);
        enableComponent(mComponent11);
    }

    /**
     * 启动为双12
     */
    private void changeDouble12(){
        disabledComponent(mComponent);
        disabledComponent(mComponent11);
        enableComponent(mComponent12);
    }

    /**
     * 活动结束或者默认
     */
    private void restoreComponent(){
        disabledComponent(mComponent11);
        disabledComponent(mComponent12);
        enableComponent(mComponent);
    }

随便找个监听去切换:

                        //代码省略
                        case R.id.ll_question://双11活动
                            changeDouble11();
                            break;
                        case R.id.ll_skin://双12活动
                            changeDouble12();
                            break;
                       //代码省略

看下实际效果图:
这里写图片描述

会有一个短暂的过程,所以一般更换图标要在夜深人静的时候更换。
(备注:调试时候,如果已经更换了图标,在重新run app的时候,会报以下错误:
Error: Activity class {com.hu.test/com.hu.test.MainActivity} does not exist.
找不到启动activity,因为更换图标后,默认的activity被PM关闭了,不在是清单文件中的默认启动activity,所以会报这个错误,解决方法:1>,卸载app,重新run app;2>,app中通过PM将默认activity重新开启,预埋的关闭)。
如有不足,请大佬们指出。

猜你喜欢

转载自blog.csdn.net/HUandroid/article/details/78831250