Android优雅地实现夜间模式

本文基于两个前提

  • 1.代码已经基本完成,并且不容易进行大规模的重构。
    2.使用XML配置式完成模式切换,而不是采用大量的动态代码标记。
    3.不希望重启Activity 就能动态切换主题

本文主要使用技术:

    1. Android skinloader
    2. Android res 加载原理

一、Android Skinloader 原理剖析

Android skin loader 是在 View 进行LayoutInflate 加载Xml布局文件,解析皮肤标记然后进行标记收集。
其中LayoutInflater 采用策略模式解析XML,所以可以在解析XML的文件的时候,采用相应的标记策略定义自己的解析策略,遍历并且保存所有带有换肤标记的View节点id属性,然后进行换肤。
源码如下:

private void parseSkinAttr(Context context, AttributeSet attrs, View view) {
    List viewAttrs = new ArrayList();
    for (int i = 0; i < attrs.getAttributeCount(); i++){
        String attrName = attrs.getAttributeName(i);
        String attrValue = attrs.getAttributeValue(i);

        if(!AttrFactory.isSupportedAttr(attrName)){
            continue;
        }

        if(attrValue.startsWith("@")){
            try {
                int id = Integer.parseInt(attrValue.substring(1));
                String entryName = context.getResources().getResourceEntryName(id);
                String typeName = context.getResources().getResourceTypeName(id);
                SkinAttr mSkinAttr = AttrFactory.get(attrName, id, entryName, typeName);
                if (mSkinAttr != null) {
                    viewAttrs.add(mSkinAttr);
                }
            } catch (NumberFormatException e) {
                e.printStackTrace();
            } catch (NotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    if(!ListUtils.isEmpty(viewAttrs)){
        SkinItem skinItem = new SkinItem();
        skinItem.view = view;
        skinItem.attrs = viewAttrs;

        mSkinItems.add(skinItem);

        if(SkinManager.getInstance().isExternalSkin()){
            skinItem.apply();
        }
    }
}
public static SkinAttr get(String attrName, int attrValueRefId, String attrValueRefName, String typeName){

        SkinAttr mSkinAttr = null;

        if(BACKGROUND.equals(attrName)){ 
            mSkinAttr = new BackgroundAttr();
        }else if(TEXT_COLOR.equals(attrName)){ 
            mSkinAttr = new TextColorAttr();
        }else if(LIST_SELECTOR.equals(attrName)){ 
            mSkinAttr = new ListSelectorAttr();
        }else if(DIVIDER.equals(attrName)){ 
            mSkinAttr = new DividerAttr();
        }else{
            return null;
        }

        mSkinAttr.attrName = attrName;
        mSkinAttr.attrValueRefId = attrValueRefId;
        mSkinAttr.attrValueRefName = attrValueRefName;
        mSkinAttr.attrValueTypeName = typeName;
        return mSkinAttr;
    }**

二、Android主题策略已经资源加载原理

在Android studio 工程目录中定义了大量资源文件,其中有一个文件夹是关于夜间模式(values-night),这个是设置Android夜间模式资源位置。
其主要技术点为同名资源,不同资源目录的替换。
就是主动触发模式切换然后切换资源的加载路径,其源码如下:
    final Resources resources = getContext().getApplicationContext().getResources();
     final Configuration configuration = resources.getConfiguration();
     configuration.uiMode = Configuration.UI_MODE_NIGHT_YES;
     DisplayMetrics dm = resources.getDisplayMetrics();
     resources.updateConfiguration(configuration, dm);

三、两者结合构建SkinLoader夜间模式

主要原理就是在需要修改颜色的控件上打上标记,并且在在vaules 和 values-night 定义重名的颜色,当模式切换      的时候调用遍历好的标记View,切换颜色,达到实时切换的效果。

主要代码如下:
定义 values

#ff77dd88

定义values-night

#ff774488

主要代码:

private void setTrigger(View view) {
        view.findViewById(R.id.night_mode).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final Resources resources = getContext().getApplicationContext().getResources();
                final Configuration configuration = resources.getConfiguration();
                configuration.uiMode = Configuration.UI_MODE_NIGHT_YES;
                DisplayMetrics dm = resources.getDisplayMetrics();
                resources.updateConfiguration(configuration, dm);
                mSkinInflaterFactory.applySkin();
            }
        });


    }

资源下载地址:

http://download.csdn.net/detail/gezihau/9508825

你可能感兴趣的:(android)