關(guān)于安卓應(yīng)用圖標(biāo)的幾個(gè)問(wèn)題

一偏化、 ic_launcher.png --> drawable vs mipmap

元旦要到了励翼,如果要在當(dāng)天發(fā)版闲先,想必各位工程師最近應(yīng)該都提交了代碼骡和,之后元旦也少不了加班。

今天我也提交了代碼崭参,沒(méi)過(guò)多久 QA 就跑過(guò)來(lái),對(duì)我說(shuō)款咖,Arno 你改了啥何暮? 怎么連圖標(biāo)都沒(méi)了?我仔細(xì)一看铐殃,確實(shí)沒(méi)有了海洼,變成了 Ecplise 的綠色機(jī)器人。奇怪的是富腊,我用的是 AS 編譯的坏逢,應(yīng)該沒(méi)有那個(gè)圖片。

事實(shí)上赘被,之前一直是沒(méi)有問(wèn)題的是整。那么應(yīng)該是這次的操作出了問(wèn)題,想想這次更換 logo 我都做了些什么民假。由于工程比較古老浮入,是 Eclipse 轉(zhuǎn)成 AS 的工程,我看到 drawable 路徑下 ldpi mdpi hdpi xhdpi xxhdpi 中各有一個(gè) ic_launcher.png 羊异,但是實(shí)際上只有兩個(gè)尺寸事秀,所以我只保留了兩張圖彤断,刪除了 ldpi mdpi xxdpi 的圖片。

那么大概是圖片出了問(wèn)題易迹,我來(lái)到了 Manifest 文件宰衙,追蹤這個(gè)文件的路徑:


效果圖-g.png

可以看到,明明在 drawable 路徑下不存在的圖片睹欲,但是它自動(dòng)取出了默認(rèn)圖幫我們補(bǔ)齊了供炼。想起之前學(xué)習(xí) Android 的時(shí)候, ic_launcher.png 都是放在 mipmap 路徑下的句伶。所以嘗試把 hdpi 和 xhdpi 的文件從 drawable 路徑移動(dòng)到 mipmap 路徑后劲蜻,更改 Manifest 文件中的引用,顯示就正確了考余。

二先嬉、修改桌面圖標(biāo)

既然提到了圖標(biāo),就想順便提一個(gè)很早就有的疑問(wèn):怎么動(dòng)態(tài)修改桌面的圖標(biāo)楚堤?之前是一個(gè)微信公眾號(hào)推送的文章疫蔓,但是后來(lái)怎么也找不到了,這次趁機(jī)會(huì)身冬,順便解決這個(gè)問(wèn)題衅胀。

需求很簡(jiǎn)單,就是要在活動(dòng)時(shí)間酥筝,使 app 圖標(biāo)自動(dòng)變?yōu)榛顒?dòng)圖標(biāo)滚躯,活動(dòng)失效后,讓圖標(biāo)變回原來(lái)的樣子嘿歌。比如這次要元旦了掸掏,就目前而言,替換圖標(biāo)的方式就是在我們發(fā)版之后更新我們的 app宙帝,但是我們可以看到淘寶之類的 app 可以在不更新的情況下丧凤,切換圖標(biāo)。這個(gè)是怎么做到的呢步脓?利用 google 檢索愿待,可以很快找到解決方案。

我們知道靴患,桌面圖標(biāo)的名稱仍侥、圖片設(shè)置都是在 Application 節(jié)點(diǎn)下進(jìn)行設(shè)置。代碼如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.arno.logoex">
    <!--一些權(quán)限聲明-->
    <application android:allowBackup="true"
        android:icon="@mipmap/icon_normal"
        android:label="@string/app_name"
        android:supportsRtl="true" android:theme="@style/AppTheme">
    <!-其他聲明-->
    </application>

這里設(shè)置的鸳君,是針對(duì)全局入口的屬性配置访圃,也就是說(shuō),如果有多個(gè)入口相嵌,且入口信息中沒(méi)有配置名稱和圖片腿时,那么就會(huì)默認(rèn)使用這里配置的信息况脆。因此,對(duì)于大部分的應(yīng)用而言批糟,我們只需要在這里設(shè)置了應(yīng)用名稱和圖標(biāo)格了。但是如果你的應(yīng)用需要像淘寶天貓那樣,在某個(gè)時(shí)候更改圖標(biāo)徽鼎、甚至需要更改應(yīng)用名稱或者入口活動(dòng)頁(yè)盛末,那么你就需要來(lái)看看這里的知識(shí)了。

Android 在清單文件中提供了 <activity-alias/> 標(biāo)簽否淤,這個(gè)標(biāo)簽可以作為某個(gè) targetActivity 的別名悄但,其中,這個(gè) targetActivity 需要定義在別名之前并且在相同的<application/>下石抡。接下來(lái)標(biāo)簽的大部分介紹都來(lái)自官網(wǎng)檐嚣,國(guó)內(nèi)也可以進(jìn),放心點(diǎn)擊:鏈接


從文檔得知?jiǎng)e名可以設(shè)置的屬性:

<activity-alias 
        android:enabled=["true" | "false"] 
        android:exported=["true" | "false"] 
        android:icon="drawable resource" 
        android:label="string resource" 
        android:name="string" 
        android:permission="string" 
        android:targetActivity="string" >
 . . .
</activity-alias>

下面是各個(gè)屬性的含義:

  • android:enabled 屬性啰扛,布爾類型嚎京,是否開(kāi)啟別名設(shè)置,默認(rèn)值為 true隐解;
  • android:exported 屬性鞍帝,布爾類型,是否支持其他應(yīng)用通過(guò)這個(gè)別名訪問(wèn)目標(biāo) Activity煞茫,默認(rèn)值為 true帕涌;
  • android:iconlabel 屬性:類似 <activity> 標(biāo)簽,表示目標(biāo) Activity 的顯示圖標(biāo)和標(biāo)簽续徽;
  • android:label 屬性:當(dāng)指定 Activity 為啟動(dòng)頁(yè)面時(shí)宵膨,這個(gè)值就會(huì)被作為顯示的應(yīng)用名稱。
  • android:name 屬性:Activity 別名炸宵,在 <activity> 標(biāo)簽中, name 屬性必須與對(duì)應(yīng) Activity 文件的名字保持一致谷扣,而這里的別名可任意設(shè)置土全,保證唯一性即可;
  • android:permission 屬性:權(quán)限設(shè)置会涎,對(duì)別名的使用加以限制裹匙,詳細(xì)可以看 自定義屬性
  • android:targetActivity 屬性:指定別名能夠啟動(dòng)的目標(biāo) Activity末秃,注意概页,屬性值一定要對(duì)應(yīng)到 <activity> 標(biāo)簽中的 name 屬性,并且該 <activity> 標(biāo)簽一定要位于<activity-alias> 標(biāo)簽前面练慕;

了解了<activity-alias>惰匙,接下來(lái)我們就可以寫代碼了技掏。

2.1 利用別名預(yù)設(shè)入口

<application android:allowBackup="true"
    android:icon="@mipmap/icon_normal"
    android:label="@string/app_name"
    android:supportsRtl="true" android:theme="@style/AppTheme">
    <activity
        android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <!--<category android:name="android.intent.category.LAUNCHER" />-->
        </intent-filter>
    </activity>
    <!--name:組件名字-->
    <!--enabled:該組件是否啟動(dòng)-->
    <!--icon:組件圖標(biāo)-->
    <!--label:組件標(biāo)簽說(shuō)明-->
    <!--targetActivity:組件的目標(biāo)類-->
    <activity-alias
        android:name=".EntranceDefault"
        android:enabled="true"
        android:icon="@mipmap/icon_normal"
        android:label="EntranceDefault"
        android:targetActivity=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>
    <activity-alias
        android:name=".EntranceSpec"
        android:enabled="false"
        android:icon="@mipmap/icon_1"
        android:label="EntranceSpec"
        android:targetActivity=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>
</application>
2.2 代碼選擇入口

方法比較簡(jiǎn)單,主要是利用 PackageManager 的一個(gè)方法项鬼。這里寫了一個(gè)方法類哑梳,在 app 啟動(dòng)的時(shí)候調(diào)用 init 方法,將所有入口別名初始化绘盟,然后在切換入口的時(shí)候調(diào)用 enable 方法鸠真。具體實(shí)現(xiàn)可以查看下面的代碼:

/**
 * Created by Arno on 2018/1/2.
 *
 * 入口切換類,用于更改應(yīng)用入口達(dá)到更換圖標(biāo)的目的
 *
 * 需要更改清單文件中入口activity的配置龄毡,將 launcher 去掉吠卷,并添加別名
 * 
 * <!--name:組件名字-->
 * <!--enabled:該組件是否啟動(dòng)-->
 * <!--icon:組件圖標(biāo)-->
 * <!--label:組件標(biāo)簽說(shuō)明-->
 * <!--targetActivity:組件的目標(biāo)類-->
 * <activity-alias
 *      android:name=".EntranceDefault"
 *      android:enabled="true"
 *      android:icon="@mipmap/icon_normal"
 *      android:label="@string/app_name"
 *      android:targetActivity=".MainActivity">
 *      <intent-filter>
 *          <action android:name="android.intent.action.MAIN" />

 *          <category android:name="android.intent.category.LAUNCHER" />
 *      </intent-filter>
 * </activity-alias>
 *
 */
public class EntranceUtils {
    private static final String TAG = "EntranceUtils";
    private Map<String, ComponentName> mEntranceMap;
    private PackageManager mPackageManager;

    public static EntranceUtils getInstance() {
        return InstanceHolder.instance;
    }

    /**
     * 初始化
     * @param context           context
     * @param componentNames    組件別名數(shù)組,需要將默認(rèn)入口別名放在第一位
     */
    public void init(Context context, String... componentNames) {
        if (mPackageManager == null) {
            mPackageManager = context.getPackageManager();
        }
        if (mEntranceMap == null) {
            mEntranceMap = new HashMap<>();
        }
        for (int i = 0; i < componentNames.length; i++) {
            ComponentName value = new ComponentName(context, componentNames[i]);
            mEntranceMap.put(componentNames[i], value);
            //默認(rèn)情況下沦零,組件的 enable 狀態(tài)為 default祭隔,需要手動(dòng)設(shè)置
            mPackageManager.setComponentEnabledSetting(
                    value,
                    i == 0 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                    PackageManager.DONT_KILL_APP);
        }
    }

    public void enable(Context context, String componentName) {
        ComponentName component = mEntranceMap.get(componentName);
        if (component != null && mPackageManager.getComponentEnabledSetting(component) == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
            for (Map.Entry<String, ComponentName> entry : mEntranceMap.entrySet()) {
                int newState = componentName.equals(entry.getKey()) ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
                mPackageManager.setComponentEnabledSetting(
                        entry.getValue(),
                        newState,
                        PackageManager.DONT_KILL_APP);
            }
            restartApp(context);
        }
    }

    private void restartApp(Context context) {
        Intent intent = context.getPackageManager()
                .getLaunchIntentForPackage(context.getPackageName());
        PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
        AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 2000, restartIntent); // 2秒鐘后重啟應(yīng)用
        System.exit(0);

    }

    private static class InstanceHolder {
        private static EntranceUtils instance = new EntranceUtils();
    }

}

不過(guò)在剛剛調(diào)用了 enable 方法的幾秒內(nèi),如果回到桌面并點(diǎn)擊應(yīng)用圖標(biāo)蠢终,會(huì)報(bào)出未安裝應(yīng)用的異常序攘,這個(gè)是正常現(xiàn)象寻拂,過(guò)一會(huì)之后程奠,應(yīng)用圖標(biāo)切換好了,就可以再次進(jìn)入祭钉。因此瞄沙,請(qǐng)謹(jǐn)慎選擇切換入口的時(shí)機(jī),以免造成不好的用戶體驗(yàn)慌核。除此之外距境,你還可以在調(diào)用后重啟 app,以加快這個(gè)切換的過(guò)程垮卓。

附上調(diào)用代碼:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    EntranceUtils.getInstance().init(this
            ,"com.arno.logoex.EntranceDefault"
            ,"com.arno.logoex.EntranceSpec");
}

public void onClick(View view){
    EntranceUtils.getInstance().enable(this,"com.arno.logoex.EntranceSpec");
}

以上垫桂。

參考:
1.developers_activity-alias
2.android 動(dòng)態(tài)修改app icon
3.Android動(dòng)態(tài)更換應(yīng)用Icon之玩轉(zhuǎn)桌面圖標(biāo)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市粟按,隨后出現(xiàn)的幾起案子诬滩,更是在濱河造成了極大的恐慌,老刑警劉巖灭将,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疼鸟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡庙曙,警方通過(guò)查閱死者的電腦和手機(jī)空镜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吴攒,你說(shuō)我怎么就攤上這事张抄。” “怎么了舶斧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵欣鳖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我茴厉,道長(zhǎng)泽台,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任矾缓,我火速辦了婚禮怀酷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嗜闻。我一直安慰自己蜕依,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布琉雳。 她就那樣靜靜地躺著样眠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪翠肘。 梳的紋絲不亂的頭發(fā)上檐束,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音束倍,去河邊找鬼被丧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛绪妹,可吹牛的內(nèi)容都是我干的甥桂。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼邮旷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼黄选!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起婶肩,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤办陷,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后狡孔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜂嗽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年苗膝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片植旧。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辱揭,死狀恐怖离唐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情问窃,我是刑警寧澤亥鬓,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站域庇,受9級(jí)特大地震影響嵌戈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜听皿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一熟呛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尉姨,春花似錦庵朝、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至覆致,卻和暖如春侄旬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背篷朵。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工勾怒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人声旺。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓笔链,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親腮猖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鉴扫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,519評(píng)論 25 707
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程,因...
    小菜c閱讀 6,358評(píng)論 0 17
  • Application 標(biāo)簽 android:allowTaskReparenting android:allow...
    Shawn_Dut閱讀 7,763評(píng)論 2 61
  • 1.運(yùn)動(dòng)的人比不運(yùn)動(dòng)的人睡眠質(zhì)量更高澈缺,盡管可能時(shí)長(zhǎng)一樣 2.睡前六小時(shí)不要喝咖啡 3.喝酒后在凌晨3點(diǎn)到早上八點(diǎn)其...
    Emily_愛(ài)美麗626閱讀 219評(píng)論 0 0
  • 校園里有兩條路 一條人多 一條車多 我走了人多的那條 校門口的煲仔飯很好吃 我獨(dú)自去享受美味 哦 我愛(ài)的土豆肉末賣...
    平蕪aay閱讀 200評(píng)論 0 0