Android实现夜间模式的方法(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。
最近整理了几篇在乐视实习时写的文章,都是一些简单的技术调研,Android夜间模式是当时做技术分享的内容,供大家参考,欢迎一起讨论~

一.夜间模式的简单介绍

随着人们夜间阅读的需求越来越扩大化,许多应用也都在设置中增加了“夜间模式”这个选项。关于夜间模式的实现,有很多种方法。在此介绍几种比较常见的夜间模式的实现方法。
首先,夜间模式是Android换肤的一种,如果应用中的夜间模式是多种皮肤的其中之一,则可以从apk外部加载皮肤资源,通过下载额外的apk文件,然后获取该apk文件中的资源文件。从实现难度上来讲,换肤的实现会比夜间模式更复杂些,但是实现方式思路也已经比较成熟。两个比较常见的应用的夜间模式实现效果如下:

知乎夜间模式.png
简书夜间模式.png

可以有一些APP是深蓝底色,有些则是黑色,字体一般为灰色。总体来说都要以暗色为底色,降低亮度和对比度。

二.夜间模式的实现方案——应用换肤技术

1.zip压缩包式皮肤

应用可设置一个默认路径。如果用户选择某个皮肤,则解压该皮肤.zip到这个文件夹中。该皮肤也可以自定义扩展名,但是都为zip格式(例如墨迹天气皮肤扩展名是mja,搜狗输入法的皮肤扩展名是sga)

实现技术: 该技术重点在于如何去读取zip文件中的资源以及皮肤文件存放策略。

实现方案:
如果每次启动都读取SD卡上的皮肤文件,就会影响APP执行速度。最好是提供设置皮肤的界面,把用户选择的皮肤文件解压缩到皮肤路径(例如”/data/data/[package name]/skin” )下,这样不需要跨存储器读取,速度较快,而且不需要每次都去zip压缩包中读取

下面是一些关于处理zip文件的方法

    public void doZip(String srcFile, String destFile) {
        // zipDirectoryPath:需要压缩的文件夹名  
        File zipDir;  
        String dirName;    
        zipDir = new File(srcFile);  
        dirName = zipDir.getName();  
        try {  
            this.zipOut = new ZipOutputStream(
    new BufferedOutputStream(  
                    new FileOutputStream(destFile)));  
            //设置压缩的注释  
            zipOut.setComment("comment");  
            //设置压缩的编码,如果要压缩的路径中有中文,就用下面的编码  
            zipOut.setEncoding("GBK");  
  //启用压缩   
            zipOut.setMethod(ZipOutputStream.DEFLATED);     
            //压缩级别为最强压缩            
    zipOut.setLevel(Deflater.BEST_COMPRESSION);                 
            handleDir(zipDir, this.zipOut,dirName);  
            this.zipOut.close();  
        } catch (IOException ioe) {  
            ioe.printStackTrace();  
        }  
    }

public void unZip(String unZipfile, String destFile) {
        FileOutputStream fileOut;  
        File file;  
        InputStream inputStream;    
        try {  
            this.zipFile = new ZipFile(unZipfile);  
  
            for (Enumeration entries = this.zipFile.getEntries(); 
      entries  .hasMoreElements();) {  
                ZipEntry entry = (ZipEntry) entries.nextElement();  
                file = new File(destFile+File.separator+entry.getName());  
  
                if (entry.isDirectory()) {  
                    file.mkdirs();  
                } else {  
                    // 如果指定文件的目录不存在,则创建之.  
                    File parent = file.getParentFile();  
                    if (!parent.exists()) {  
                        parent.mkdirs();  
                    }  
 inputStream = zipFile.getInputStream(entry);  
  
                    fileOut = new FileOutputStream(file);  
                    while ((this.readedBytes = inputStream.read(
this.buf)) > 0) {  
                        fileOut.write(this.buf, 0, this.readedBytes);  
                    }  
                    fileOut.close();  
  
                    inputStream.close();  
                }  
            }  
            this.zipFile.close();  
        } catch (IOException ioe) {  
            ioe.printStackTrace();  
        }  
    }    
 /**
     * 获得压缩文件内文件列表
     * 
     * @param zipFile 压缩文件
     * @return 压缩文件内文件名称
     * @throws ZipException 压缩文件格式有误时抛出
     * @throws IOException 当解压缩过程出错时抛出
     */
    public static ArrayList<String> getEntriesNames(File zipFile) throws ZipException, IOException {
        ArrayList<String> entryNames = new ArrayList<String>();
        Enumeration<?> entries = getEntriesEnumeration(zipFile);
        while (entries.hasMoreElements()) {
            ZipEntry entry = ((ZipEntry) entries.nextElement());
            entryNames.add(new String(getEntryName(entry).getBytes("GB2312"), "8859_1"));
        }
        return entryNames;
    }
 /**
     * 获得压缩文件内压缩文件对象以取得其属性
     * 
     * @param zipFile 压缩文件
     * @return 返回一个压缩文件列表
     * @throws ZipException 压缩文件格式有误时抛出
     * @throws IOException IO操作有误时抛出
     */
    public static Enumeration<?> getEntriesEnumeration(File zipFile) throws ZipException,
            IOException {
        ZipFile zf = new ZipFile(zipFile);
        return zf.entries();

    }

具体demo可见 https://github.com/luozheng1985/skin_demo
这种方式的优点是:皮肤资源的格式定义很随意可以是zip也可以是自定义的格式,只要程序中能够解析到资源就行,缺点是需要读取并解析文件,导致效率上会比较差。

2.apk文件换肤

这种方法通过获取其他程序的context来获取皮肤资源。我们知道android程序中要获取drawable、layout等资源,都要通过context.getResources().getXXX的方式。那么如果我们可以拿到其他程序的context,那么那个程序就可以作为皮肤程序来提供资源给主程序使用了。android中两个程序相互读取数据的条件是:两个程序的共享用户id相同,通过AndroidManifest.xml中的android:sharedUserId属性配置;两个程序签名相同。想要改变皮肤时,改变提供资源的context为皮肤程序的context,然后刷新即可。
具体实现步骤如下:

  1. 在主应用程序 和 皮肤资源程序的AndroidManifest.xml中配置相同sharedUserId。
    android:sharedUserId是指共用一个uid,也就是,凡是这个属性相同的工程,都会共用同一个uid
    *为了让用户无感知,需要安装后皮肤APk后,让自己不可以打开,且不生成桌面图标,因此要去掉AndroidManifest.xml的如下代码:
<intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
  1. 皮肤资源apk 与 主应用apk中对同一功能的皮肤资源文件名要一致。

  2. 在主程序中 获取到皮肤资源apk对应的Context,

Context skinContext = createPackageContext("皮肤资源包名",Context.CONTEXT_IGNORE_SECURITY);

通过返回的context对象就可以访问到皮肤资源apk中的任何资源
如 :

Drawable drawable =skinContext.getResources().getDrawable(R.drawable.bg_title)

(皮肤资源Apk 无需包含任何UI Activity,只需要包含需要更换皮肤的res资源文件)

优点:可定期提供换肤包供下载,换肤方式灵活,同时效率比较高。
缺点:如需使用某个皮肤,必须安装该皮肤。但其实现起来还是用代码的方式来提供置换的。同时,让两个工程来共享一个进程,这样做十分的危险。此外,安装了很多皮肤后,应用程序列表里面会有很多皮肤程序。在主程序卸载后,皮肤工程不能同样的卸载。如卸载腾讯微博之后,安装的皮肤apk没有被卸载。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,444评论 25 707
  • 之前发过一个帖子,但是那个帖子有点问题我就重新发一个吧,下面的源码是我从今年开始不断整理源码区和其他网站上的安卓例...
    passiontim阅读 21,879评论 181 334
  • 只有世界它自己最明白,纵然你渺如沙粒,它也会不舍独留小小的你去面对偌大的天空和陆地 时光洪流带走沉默的花的名姓 涌...
    落了小心思的灯阅读 268评论 0 0
  • 1、 不想上班,想离开上家公司已不是一朝一夕的事情,的确很多人都会说每个人都会有不想上班的倦怠期,只想睡到自然醒,...
    NANA0阅读 727评论 1 3
  • 愿你出走半生,归来仍是少年 愿你历尽千帆,归来仍是少年 践行才是唯一有效的学习手段 七年就是一辈子,人生有很多辈子...
    椰子数学阅读 164评论 0 0