Giter Club home page Giter Club logo

android-skin-support's Introduction

Android-skin-support

中文 | In English

skin-support build license

介绍

Android-skin-support: 一款 Android 换肤框架, 极低的学习成本, 极好的用户体验.

只需要一行代码, 就可以实现换肤, 你值得拥有!!!

SkinCompatManager.withoutActivity(this).loadSkin();

就这么简单, 你的APK已经拥有了强大的换肤功能, 当然现在是拥有了换肤功能, 别忘了制作皮肤包.

功能

  • 支持布局中用到的资源换肤。
  • 支持代码中设置的资源换肤。
  • 默认支持大部分基础控件,Material Design换肤。
  • 支持动态设置主题颜色值,支持选择sdcard上的图片作为drawable换肤资源。
  • 支持多种加载策略(应用内/插件式/自定义sdcard路径/zip等资源等)。
  • 资源加载优先级: 动态设置资源-加载策略中的资源-插件式换肤/应用内换肤-应用资源。
  • 支持定制化,选择需要的模块加载。
  • 支持矢量图(vector/svg)换肤。
  • skin-support 4.0.0以上支持AndroidX,4.0.0以下支持support库

详细内容, 请查看更新日志,那里有所有功能提交记录

TODO

  • 解耦androidx
  • 支持原生组件换肤
  • 支持多语言。
  • 支持多字体。
  • 支持Preference。
  • skin-mobile 实现:
    • 动态修改主题颜色值
    • 控件使用案例
    • 关于页面
  • Wiki

目录结构

demo // 换肤demo 集合

skin-sample(skin-app) // demo app

skin-night // 夜间模式皮肤工程

androidx // Android 原生控件

skin-support // 换肤框架

skin-support-appcompat // 换肤框架, 基础控件支持

skin-support-cardview // 换肤框架, CardView 支持

skin-support-design // 换肤框架, Material Design 支持

skin-support-constraint-layout // 换肤框架, ConstraintLayout 支持

third-part-support // 第三方控件换肤支持

Demo

default app-in plug-in

用法

最新版本选择, 请查看更新日志

导入:

support library

如果项目中还在使用support库,添加以下依赖

implementation 'skin.support:skin-support:3.1.4'                   // skin-support 基础控件支持
implementation 'skin.support:skin-support-design:3.1.4'            // skin-support-design material design 控件支持[可选]
implementation 'skin.support:skin-support-cardview:3.1.4'          // skin-support-cardview CardView 控件支持[可选]
implementation 'skin.support:skin-support-constraint-layout:3.1.4' // skin-support-constraint-layout ConstraintLayout 控件支持[可选]

在Application的onCreate中初始化

@Override
public void onCreate() {
    super.onCreate();
    SkinCompatManager.withoutActivity(this)                         // 基础控件换肤初始化
            .addInflater(new SkinMaterialViewInflater())            // material design 控件换肤初始化[可选]
            .addInflater(new SkinConstraintViewInflater())          // ConstraintLayout 控件换肤初始化[可选]
            .addInflater(new SkinCardViewInflater())                // CardView v7 控件换肤初始化[可选]
            .setSkinStatusBarColorEnable(false)                     // 关闭状态栏换肤,默认打开[可选]
            .setSkinWindowBackgroundEnable(false)                   // 关闭windowBackground换肤,默认打开[可选]
            .loadSkin();
}

如果项目中使用的Activity继承自AppCompatActivity,需要重载getDelegate()方法

@NonNull
@Override
public AppCompatDelegate getDelegate() {
    return SkinAppCompatDelegateImpl.get(this, this);
}

AndroidX support:

如果项目中使用了AndroidX, 添加以下依赖

implementation 'skin.support:skin-support:4.0.5'                   // skin-support
implementation 'skin.support:skin-support-appcompat:4.0.5'         // skin-support 基础控件支持
implementation 'skin.support:skin-support-design:4.0.5'            // skin-support-design material design 控件支持[可选]
implementation 'skin.support:skin-support-cardview:4.0.5'          // skin-support-cardview CardView 控件支持[可选]
implementation 'skin.support:skin-support-constraint-layout:4.0.5' // skin-support-constraint-layout ConstraintLayout 控件支持[可选]

⚠️ 从3.x.x迁移至4.0.5+, 解耦了换肤库对appcompat包的依赖,需要新增以下代码

implementation 'skin.support:skin-support-appcompat:4.0.5'         // skin-support 基础控件支持

在Application的onCreate中初始化

@Override
public void onCreate() {
    super.onCreate();
    SkinCompatManager.withoutActivity(this)
            .addInflater(new SkinAppCompatViewInflater())           // 基础控件换肤初始化
            .addInflater(new SkinMaterialViewInflater())            // material design 控件换肤初始化[可选]
            .addInflater(new SkinConstraintViewInflater())          // ConstraintLayout 控件换肤初始化[可选]
            .addInflater(new SkinCardViewInflater())                // CardView v7 控件换肤初始化[可选]
            .setSkinStatusBarColorEnable(false)                     // 关闭状态栏换肤,默认打开[可选]
            .setSkinWindowBackgroundEnable(false)                   // 关闭windowBackground换肤,默认打开[可选]
            .loadSkin();
}

如果项目中使用的Activity继承自AppCompatActivity,需要重载getDelegate()方法

@NonNull
@Override
public AppCompatDelegate getDelegate() {
    return SkinAppCompatDelegateImpl.get(this, this);
}

使用:

皮肤开关

如果项目中有特殊需求。例如, 股票控件: 控件颜色始终为红色或绿色, 不需要随着模式切换而换肤

那么可以使用类似的方法, 直接设置drawable

setBackgroundDrawable(redDrawable) // 不支持换肤
background="#ce3d3a"

而不是使用R.drawable.red

setBackgroundResource(R.drawable.red)
background="@drawable/red"

加载插件皮肤库

// 指定皮肤插件
SkinCompatManager.getInstance().loadSkin("new.skin"[, SkinLoaderListener], int strategy);

// 恢复应用默认皮肤
SkinCompatManager.getInstance().restoreDefaultTheme();

自定义View换肤

要点:

  1. 实现SkinCompatSupportable接口

  2. applySkin方法中实现换肤操作

  3. 在构造方法中解析出需要换肤的resId

应用内换肤:

应用内换肤,皮肤名为: night; 新增需要换肤的资源添加后缀或者前缀。

需要换肤的资源为R.color.windowBackgroundColor, 添加对应资源R.color.windowBackgroundColor_night。

加载应用内皮肤:

SkinCompatManager.getInstance().loadSkin("night", SkinCompatManager.SKIN_LOADER_STRATEGY_BUILD_IN); // 后缀加载
SkinCompatManager.getInstance().loadSkin("night", SkinCompatManager.SKIN_LOADER_STRATEGY_PREFIX_BUILD_IN); // 前缀加载

推荐将应用内换肤相关的皮肤资源放到单独的目录中

eg: res-night

注: 如果使用这种方式来增加换肤资源,记得在build.gradle 中配置一下这个资源目录 sourceSets {main {res.srcDirs = ['src/main/res', 'src/main/res-night']}}

插件式换肤:

新建Android application工程

皮肤工程包名不能和宿主应用包名相同.

例如:

宿主包名: com.ximsfei.skindemo
夜间模式: com.ximsfei.skindemo.night

将需要换肤的资源放到res目录下(同名资源)

例如 APK中窗口背景颜色为

colors.xml

<color name="background">#ffffff</color>

那么夜间模式你可以在skin-night工程中设置

colors.xml

<color name="background">#000000</color>

打包生成apk, 即为皮肤包

将打包生成的apk文件, 重命名为'xxx.skin', 防止apk结尾的文件造成混淆.

加载皮肤插件

加载插件式皮肤, 将皮肤包放到assets/skins目录下

SkinCompatManager.getInstance().loadSkin("night.skin", SkinCompatManager.SKIN_LOADER_STRATEGY_ASSETS);

自定义加载策略:

自定义sdcard路径

继承自SkinSDCardLoader,通过getSkinPath方法指定皮肤加载路径,通过getType方法指定加载器type。

public class CustomSDCardLoader extends SkinSDCardLoader {
    public static final int SKIN_LOADER_STRATEGY_SDCARD = Integer.MAX_VALUE;

    @Override
    protected String getSkinPath(Context context, String skinName) {
        return new File(SkinFileUtils.getSkinDir(context), skinName).getAbsolutePath();
    }

    @Override
    public int getType() {
        return SKIN_LOADER_STRATEGY_SDCARD;
    }
}

注: 自定义加载器type 值最好从整数最大值开始递减,框架的type值从小数开始递增,以免将来框架升级造成type 值冲突

在Application中,添加自定义加载策略:

SkinCompatManager.withoutActivity(this)
        .addStrategy(new CustomSDCardLoader());          // 自定义加载策略,指定SDCard路径

注: 自定义加载器必须在Application中注册,皮肤切换后,重启应用需要根据当前策略加载皮肤

使用自定义加载器加载皮肤:

SkinCompatManager.getInstance().loadSkin("night.skin", null, CustomSDCardLoader.SKIN_LOADER_STRATEGY_SDCARD);

zip包中加载资源

继承自SkinSDCardLoader,在loadSkinInBackground方法中解压资源,在getDrawable等方法中返回加压后的资源。

public class ZipSDCardLoader extends SkinSDCardLoader {
    public static final int SKIN_LOADER_STRATEGY_ZIP = Integer.MAX_VALUE - 1;

    @Override
    public String loadSkinInBackground(Context context, String skinName) {
        // TODO 解压zip包中的资源,同时可以根据skinName安装皮肤包(.skin)。
        return super.loadSkinInBackground(context, skinName);
    }

    @Override
    protected String getSkinPath(Context context, String skinName) {
        // TODO 返回皮肤包路径,如果自需要使用zip包,则返回""
        return new File(SkinFileUtils.getSkinDir(context), skinName).getAbsolutePath();
    }

    @Override
    public Drawable getDrawable(Context context, String skinName, int resId) {
        // TODO 根据resId来判断是否使用zip包中的资源。
        return super.getDrawable(context, skinName, resId);
    }

    @Override
    public int getType() {
        return SKIN_LOADER_STRATEGY_ZIP;
    }
}

资源加载策略更灵活,不仅仅只有皮肤包,开发者可配置任意资源获取方式(Zip/Apk/Json...)。

在Application中,添加自定义加载策略:

SkinCompatManager.withoutActivity(this)
        .addStrategy(new ZipSDCardLoader());          // 自定义加载策略,加载zip包中的资源

动态设置资源

SkinCompatUserThemeManager.get().addColorState(R.color.colorPrimary, #ffffffff);

SkinCompatUserThemeManager.get().addColorState(R.color.colorPrimary, new ColorState.ColorBuilder().addXxx().build());

// 清除所有已有颜色值。
SkinCompatUserThemeManager.get().clearColors();
SkinCompatUserThemeManager.get().addDrawablePath(R.drawable.windowBackground, "/sdcard/DCIM/Camera/xxx.jpg");

// 要换肤的资源id,图片路径,图片旋转角度(默认为0)
SkinCompatUserThemeManager.get().addDrawablePath(R.drawable.windowBackground, "/sdcard/DCIM/Camera/xxx.jpg", 90);

// 清除所有已有图片路径。
SkinCompatUserThemeManager.get().clearDrawables();

在设置完颜色及图片后,需要调用apply()方法来保存设置。

SkinCompatUserThemeManager.get().apply();

资源加载优先级: 用户自定义颜色值-加载策略中的资源-皮肤包资源-应用资源。

获取当前使用皮肤

https://github.com/ximsfei/Android-skin-support/blob/master/androidx/skin-support/src/main/java/skin/support/utils/SkinPreference.java

缺点

  • 同一个LayoutInflater只能设置一次Factory,容易和同类库产生冲突

谁在使用

如果你想提交作品,欢迎提出 PR 或联系作者

                               
搜狐新闻探索版讯飞语记qoo app多维新闻

技术交流

android-skin-support's People

Contributors

afeidaren avatar jungle68 avatar ximsfei avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-skin-support's Issues

shape 文件,里面的gradient 失效了。

<gradient
    android:startColor="@color/white"   // white 是 #ffffff 色
    android:endColor="@color/cfbf1f2"
    android:angle="0" />

然后是在 RecyclerView 里面
为LinearLayout 设置的一个 渐变色
viewholder.itemRootview.setBackgroundResource(Constant.quote_default_bg);
// quote_default_bg = R.drawable.quote_default_gradient; quote_default_gradient是上面的shape文件
发现设置的渐变背景色没有了,皮肤包里面也有对应的资源文件,不知道是什么原因

内存消耗严重

1.植入换肤模块后,刷新图片列表(RecyclerView),GC基本失效,内存基本只升不降,去掉后恢复正常,
2.控制台一直打log,影响项目调试,希望关掉多余日志。

statusBarColor颜色有错

在AndroidManifest.xml中, 如果application节点有主题, 在某个activity中又指定了主题, 同时恰好这两个主题的statusBarColor不一样, SkinCompatActivity会把application的statusBarColor读取出来, 填充到activity上

自定义控件的话,paint.setColor()的问题

自定义控件时,遇到这种
Paint.setColor(mRoundColor);
mRoundColor是通过
typedArray.getColor(R.styleable.RoundProgressBar_textColor, getResources().getColor(R.color.c4c6072));获取的,之前你说过不支持 getResources().getColor(R.color.c4c6072)方式更换颜色,想问下这种情况怎么解决?

制作皮肤后,Android7.1.1换肤正常,Android7.0及其以下无法换肤, PackageInfo=null

java.lang.NullPointerException: Attempt to read from field 'java.lang.String android.content.pm.PackageInfo.packageName' on a null object reference
05-08 01:24:49.699 9563-9602/com.xxxxx.xxxxx W/System.err: at skin.support.SkinCompatManager.initSkinPackageName(SkinCompatManager.java:196)
05-08 01:24:49.700 9563-9602/com. xxxxx.xxxxx W/System.err: at skin.support.SkinCompatManager.access$400(SkinCompatManager.java:33)

报错 生成app 直接就闪退 然后报如下的错

Didn't find class "skin.support.design.R$styleable" on path: DexPathList[[zip file "/data/app/star.liuwen.com.endskin-1/base.apk"],nativeLibraryDirectories=[/data/app/star.liuwen.com.endskin-1/lib/arm64, /vendor/lib64, /system/lib64]]

我导入了app-skin 然后依赖了
compile 'skin.support:skin-support:1.0.1'
compile 'skin.support:skin-support-design:0.0.2'
然后生成app 点击就闪退了 求解

增加功能--指定某些控件不换肤

从性能上讲,让控件不换肤最好的方式就是不受监听,这样可以减少内存和遍历查询时间

实现的思路:
在加入换肤监听的地方做判断,而判断依据最直接的方法就是解析xml
个人觉得可以参照android skin loader的做法,在xml中设置skin:enable=false这种类似的字段,在解析xml时读取出来

毕竟不设置皮肤的情况比较少见,在xml中设置一个值的情况不会太多

如何在代码中设置EditText左边的图片啊?

我的EditText是一个自定义View,继承你的SkinCompatEditText,然后代码中以下设置

Drawable username_nav_left = getResources().getDrawable(R.drawable.bg_login_user);
 username_nav_left.setBounds(0, 0, username_nav_left.getMinimumWidth() - 25, username_nav_left.getMinimumHeight() - 25);
 binding.activityLoginUsername.setCompoundDrawables(username_nav_left, null, null, null);

上面设置了R.drawable.bg_login_user图片,希望在载入其他skin的时候替换成其他的图片,但是最终的效果没有替换成功,我的手机的Nexus6p , 7.1的系统,并且在使用的时候有遇到载入皮肤后崩溃的问题,但是别人低版本的手机不会出现这个问题。
再问一个问题哈:库里支持修改text属性吗?不同skin有不同的标题.

关于ConstraintLayout背景色问题

我有几个界面用ConstraintLayout做根布局,在切换它们的android:background时,有的布局背景色偶尔会变偶尔不会,无规律,有的布局怎么都不变,我尝试过杀死应用再重启,都无法解决这个问题,请问这是什么问题?

allowBackup 相关问题

build项目的时候,使用 skin.support:skin-support:2.0.4有以下失败log:

Attribute application@allowBackup value=(false) from AndroidManifest.xml:67:9-36
is also present at [skin.support:skin-support:2.0.4] AndroidManifest.xml:12:9-35 value=(true).
Suggestion: add 'tools:replace="android:allowBackup"' to element at AndroidManifest.xml:65:5-1065:19 to override.
请参考下,能否关闭skin-support的该属性

可否不继承 SkinCompatActivity

如题。因为有一个库已经需要我继承它的 Activity,所以不能再兼容继承 SkinCompatActivity,还有别的方法解决吗

运行出现问题

Attempt to invoke virtual method 'int skin.support.content.res.SkinCompatResources.getColor(int)' on a null object reference

drawable 里面设置了颜色它是一个selector 貌似没效果

代码这样写的
<item android:drawable="@drawable/ic_button_pressed" android:state_pressed="true" android:state_window_focused="false"/> <item android:drawable="@drawable/ic_button_enable" android:state_enabled="false"/> <item android:drawable="@drawable/ic_button_normal" android:state_enabled="true"/> <item android:drawable="@drawable/ic_button_normal"/>
ic_button_pressed :
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/color10"/> <corners android:radius="@dimen/app_radius_size"/> </shape>

ic_button_enable:
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/color10"/> <corners android:radius="@dimen/app_radius_size"/>

ic_button_normal:
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/color4"/> <corners android:radius="@dimen/app_radius_size"/> </shape>
这样做之后不能进行换肤,什么原因呢

提示皮肤资源获取失败

按照步骤来的,在 assets/skins/ 目录下放入了 demo 中的 night.skin。然后运行的时候就出这样的错误信息

viewHolder.conversationMark.setImageResource(R.color.transparent); 突然项目崩溃

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.drawable.Drawable.setTintList(android.content.res.ColorStateList)' on a null object reference
at android.support.v4.graphics.drawable.DrawableCompatLollipop.setTintList(DrawableCompatLollipop.java:57)
at android.support.v4.graphics.drawable.DrawableCompat$LollipopDrawableImpl.setTintList(DrawableCompat.java:225)
at android.support.v4.graphics.drawable.DrawableCompat.setTintList(DrawableCompat.java:396)
at skin.support.widget.SkinCompatImageHelper.applySkin(SkinCompatImageHelper.java:67)
at skin.support.widget.SkinCompatImageHelper.setImageResource(SkinCompatImageHelper.java:45)
at skin.support.widget.SkinCompatImageView.setImageResource(SkinCompatImageView.java:45)

SkinCompatEditText

光标颜色不能随着夜间模式改变,皮肤包里面配置了,textCursorDrawable设置了颜色,但是更换夜间模式后,颜色没有改变。

android.support.design.widget.TabLayout 切换失败

android.support.design.widget.TabLayout 切换失败 我已经引入这个库了,重启程序久好了,帮忙一下,谢谢
又是会有这个错误
Unknown element under : meta-data at /storage/emulated/0/Android/data/com.packeage/cache/skins/night.skin Binary XML file line #11

关于应用在生产环境中的一些疑问

如果线上版本,已集成该库,用户也下载了其它颜色进行换肤。在接下的新版本中,默认主题颜色,增加了一个新的 color 值,那线上版本的用户下载的其它颜色皮肤,怎样同步新版本中的新 color 值?只能让线上用户重新下载那些皮肤吗?

请教一下自定义控件的问题

我自定义了一个BottomNavigationView extends SkinCompatLinearLayout
每个item中放了一个
`

<ImageView
    android:id="@+id/iv_bottom_icon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<TextView
    android:id="@+id/tv_bottom_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@drawable/bottom_text_selector" />

`

LayoutInflater inflater = LayoutInflater.from(this.getContext()); LinearLayout childLayout = (LinearLayout) inflater.inflate(R.layout.item_bottom, this, false);

里面使用selector设置了一个选中颜色和默认颜色
但是没有效果

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.