├── app ├── .gitignore ├── src │ └── main │ │ ├── assets │ │ └── nightMode.skin │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── dimens.xml │ │ │ ├── news_attr.xml │ │ │ ├── styles.xml │ │ │ ├── colors_night.xml │ │ │ └── colors.xml │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── news_switch_setting_on_nor.png │ │ │ └── news_switch_setting_off_nor.png │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ ├── drawable │ │ │ ├── drawable_float_view.xml │ │ │ ├── drawable_float_view_night.xml │ │ │ ├── btn_bg.xml │ │ │ ├── news_item_selector.xml │ │ │ ├── btn_bg_night.xml │ │ │ └── news_item_selector_night.xml │ │ ├── drawable-night │ │ │ ├── drawable_float_view.xml │ │ │ ├── btn_bg.xml │ │ │ └── news_item_selector.xml │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ ├── values-night │ │ │ └── colors_night.xml │ │ └── layout │ │ │ ├── grid_item_view.xml │ │ │ ├── layout_popwindow.xml │ │ │ ├── list_item_view.xml │ │ │ ├── activity_recycler_view.xml │ │ │ ├── activity_grid_view.xml │ │ │ ├── activity_dynamic_add_view.xml │ │ │ ├── activity_viewpager_listview.xml │ │ │ ├── layout_dialog_custom.xml │ │ │ ├── activity_custom_attr_test.xml │ │ │ ├── activity_base_activity.xml │ │ │ ├── activity_other_scene.xml │ │ │ └── activity_main.xml │ │ ├── java │ │ └── org │ │ │ └── qcode │ │ │ └── demo │ │ │ ├── skin │ │ │ ├── SkinConstant.java │ │ │ ├── SkinConfigHelper.java │ │ │ └── SkinUtils.java │ │ │ ├── utils │ │ │ ├── UITaskRunner.java │ │ │ ├── UIUtil.java │ │ │ └── FileUtils.java │ │ │ ├── ui │ │ │ ├── otherscene │ │ │ │ ├── SpannableSkinAttr.java │ │ │ │ ├── FloatView.java │ │ │ │ ├── SpannableSkinAttrHandler.java │ │ │ │ └── CustomDialog.java │ │ │ ├── gridview │ │ │ │ └── GridViewActivity.java │ │ │ ├── viewpageandlistview │ │ │ │ ├── ViewPagerAndListViewActivity.java │ │ │ │ ├── DataListAdapter.java │ │ │ │ └── NewsPageAdapter.java │ │ │ ├── recyclerview │ │ │ │ ├── RecyclerViewActivity.java │ │ │ │ └── DataRecyclerViewAdapter.java │ │ │ ├── customattr │ │ │ │ ├── CustomTextView.java │ │ │ │ ├── CustomAttrViewActivity.java │ │ │ │ ├── DefBackgroundAttrHandler.java │ │ │ │ └── DefTextColorAttrHandler.java │ │ │ └── dynamicaddview │ │ │ │ └── DynamicAddViewActivity.java │ │ │ ├── SkinDemoApp.java │ │ │ ├── MainActivity.java │ │ │ ├── SkinChangeSwitchView.java │ │ │ └── BaseActivity.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── QSkinLoaderlib ├── .gitignore ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ ├── strings.xml │ │ │ ├── skin_ids.xml │ │ │ └── skin_attrs.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── org │ │ └── qcode │ │ └── qskinloader │ │ ├── base │ │ ├── utils │ │ │ ├── StringUtils.java │ │ │ ├── CollectionUtils.java │ │ │ ├── WeakReferenceHelper.java │ │ │ ├── HashMapCache.java │ │ │ ├── Logging.java │ │ │ └── ReflectUtils.java │ │ └── observable │ │ │ ├── INotifyUpdate.java │ │ │ ├── IObservable.java │ │ │ └── Observable.java │ │ ├── ISkinAttrHandler.java │ │ ├── entity │ │ ├── SkinConstant.java │ │ ├── SkinAttrName.java │ │ ├── SkinAttr.java │ │ ├── DynamicAttr.java │ │ └── SkinAttrSet.java │ │ ├── resourceloader │ │ ├── ILoadResourceCallback.java │ │ ├── impl │ │ │ ├── SuffixResourceLoader.java │ │ │ ├── ConfigChangeResourceLoader.java │ │ │ ├── ConfigChangeResourceManager.java │ │ │ ├── APKResourceLoader.java │ │ │ ├── APKResourceManager.java │ │ │ └── SuffixResourceManager.java │ │ └── ResourceManager.java │ │ ├── IResourceLoader.java │ │ ├── ILoadSkinListener.java │ │ ├── IViewCreateListener.java │ │ ├── ISkinAttributeParser.java │ │ ├── ISkinActivity.java │ │ ├── attrhandler │ │ ├── BackgroundAttrHandler.java │ │ ├── DividerAttrHandler.java │ │ ├── ListSelectorAttrHandler.java │ │ ├── DrawableLeftAttrHandler.java │ │ ├── TextColorHintAttrHandler.java │ │ ├── TextColorAttrHandler.java │ │ ├── ShadowAttrHandler.java │ │ ├── SrcAttrHandler.java │ │ ├── RecyclerViewClearSubAttrHandler.java │ │ ├── SkinAttrUtils.java │ │ └── SkinAttrFactory.java │ │ ├── impl │ │ ├── ViewSkinTagHelper.java │ │ ├── WindowViewManager.java │ │ └── SkinInflaterFactoryImpl.java │ │ ├── view │ │ └── ShadowImageView.java │ │ ├── IWindowViewManager.java │ │ ├── SkinManager.java │ │ ├── IResourceManager.java │ │ ├── ISkinManager.java │ │ ├── IActivitySkinEventHandler.java │ │ └── ISkinViewHelper.java ├── build.gradle └── proguard-rules.pro ├── SkinProject ├── app │ ├── .gitignore │ ├── src │ │ └── main │ │ │ ├── res │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── news_switch_setting_on_nor.png │ │ │ │ └── news_switch_setting_off_nor.png │ │ │ ├── drawable │ │ │ │ ├── drawable_float_view.xml │ │ │ │ ├── btn_bg.xml │ │ │ │ └── news_item_selector.xml │ │ │ └── values │ │ │ │ └── colors.xml │ │ │ ├── java │ │ │ └── org │ │ │ │ └── qcode │ │ │ │ └── skinproject │ │ │ │ └── MainActivity.java │ │ │ └── AndroidManifest.xml │ ├── proguard-rules.pro │ └── build.gradle ├── settings.gradle ├── .gitignore ├── build.gradle ├── gradle.properties └── gradlew.bat ├── settings.gradle ├── skin-change-demo.gif ├── skin-change-demo.mp4 ├── .idea ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── modules.xml ├── runConfigurations.xml ├── gradle.xml ├── compiler.xml └── misc.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /QSkinLoaderlib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /SkinProject/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /SkinProject/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':QSkinLoaderlib' 2 | -------------------------------------------------------------------------------- /skin-change-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/skin-change-demo.gif -------------------------------------------------------------------------------- /skin-change-demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/skin-change-demo.mp4 -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/assets/nightMode.skin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/app/src/main/assets/nightMode.skin -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | QSkinLoaderLib 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | NightModeDemo_QSkinLoader 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/news_switch_setting_on_nor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/app/src/main/res/mipmap-xxhdpi/news_switch_setting_on_nor.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/news_switch_setting_off_nor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/app/src/main/res/mipmap-xxhdpi/news_switch_setting_off_nor.png -------------------------------------------------------------------------------- /SkinProject/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SkinProject/app/src/main/res/mipmap-xxhdpi/news_switch_setting_on_nor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/SkinProject/app/src/main/res/mipmap-xxhdpi/news_switch_setting_on_nor.png -------------------------------------------------------------------------------- /SkinProject/app/src/main/res/mipmap-xxhdpi/news_switch_setting_off_nor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqliu10u/QSkinLoader/HEAD/SkinProject/app/src/main/res/mipmap-xxhdpi/news_switch_setting_off_nor.png -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/res/values/skin_ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /SkinProject/app/src/main/java/org/qcode/skinproject/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.qcode.skinproject; 2 | 3 | import android.app.Activity; 4 | 5 | /** 6 | * qqliu 7 | * 2016/11/9. 8 | */ 9 | 10 | public class MainActivity extends Activity { 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/news_attr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/drawable_float_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-night/drawable_float_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /SkinProject/app/src/main/res/drawable/drawable_float_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/drawable_float_view_night.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/base/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.base.utils; 2 | 3 | /** 4 | * qqliu 5 | * 2016/9/25. 6 | */ 7 | public class StringUtils { 8 | public static boolean isEmpty(CharSequence sequence) { 9 | return null == sequence || sequence.length() <= 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-night/btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/news_item_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /SkinProject/app/src/main/res/drawable/btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-night/news_item_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_bg_night.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SkinProject/app/src/main/res/drawable/news_item_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/news_item_selector_night.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/res/values/skin_attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /QSkinLoaderlib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 23 10 | } 11 | } 12 | 13 | dependencies { 14 | compile fileTree(include: ['*.jar'], dir: 'libs') 15 | compile 'com.android.support:recyclerview-v7:23.2.1' 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/skin/SkinConstant.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.skin; 2 | 3 | /** 4 | * qqliu 5 | * 2016/10/8. 6 | */ 7 | 8 | public class SkinConstant { 9 | 10 | public static final String PACKAGE_NAME = "org.qcode.demo"; 11 | 12 | /**皮肤标识存放*/ 13 | public static final String CUSTOM_SKIN_IDENTIFIER = 14 | PACKAGE_NAME + ".CUSTOM_SKIN_IDENTIFIER"; 15 | 16 | /**默认皮肤*/ 17 | public static final String DEFAULT_SKIN = "default"; 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/base/utils/CollectionUtils.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.base.utils; 2 | 3 | import java.util.Collection; 4 | 5 | /** 6 | * qqliu 7 | * 2016/9/25. 8 | */ 9 | public class CollectionUtils { 10 | public static boolean isEmpty(Collection collection){ 11 | return null == collection || collection.size() <= 0; 12 | } 13 | 14 | public static boolean isEmpty(T... array){ 15 | return null == array || array.length <= 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/base/observable/INotifyUpdate.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.base.observable; 2 | 3 | /** 4 | * 通知观察者更新 5 | * qqliu 6 | * 2016/9/19. 7 | */ 8 | public interface INotifyUpdate { 9 | 10 | /*** 11 | * 通知观察者发生了标识为identifier的事件,事件参数是params 12 | * @param callback 观察者 13 | * @param identifier 事件标识 14 | * @param params 事件参数 15 | * @return 返回true截断事件传播,false继续事件传播 16 | */ 17 | boolean notifyEvent(T callback, String identifier, Object... params); 18 | } 19 | -------------------------------------------------------------------------------- /SkinProject/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SkinProject/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /SkinProject/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #84878c 4 | 5 | #000000 6 | 7 | #FFFFFF 8 | #000000 9 | #000000 10 | #333333 11 | #1c1d20 12 | 13 | 14 | #00FF00 15 | 16 | #8e8e8e 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/utils/UITaskRunner.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.utils; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | /** 7 | * 在主线程中执行runnable 8 | */ 9 | public class UITaskRunner { 10 | private Handler mHandler; 11 | 12 | private UITaskRunner() { 13 | mHandler = new Handler(Looper.getMainLooper()); 14 | } 15 | 16 | private static class SingletonHolder { 17 | static UITaskRunner sRunner = new UITaskRunner(); 18 | } 19 | 20 | public static Handler getHandler() { 21 | return SingletonHolder.sRunner.mHandler; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors_night.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #84878c 4 | 5 | #000000 6 | 7 | #FFFFFF 8 | #000000 9 | #000000 10 | #333333 11 | #1c1d20 12 | 13 | 14 | #00FF00 15 | 16 | #8e8e8e 17 | 18 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors_night.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #84878c 4 | 5 | #000000 6 | 7 | #FFFFFF 8 | #000000 9 | #000000 10 | #333333 11 | #1c1d20 12 | 13 | 14 | #00FF00 15 | 16 | #8e8e8e 17 | 18 | -------------------------------------------------------------------------------- /QSkinLoaderlib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # You can edit the include path and order by changing the proguardFiles 4 | # directive in build.gradle. 5 | # 6 | # For more details, see 7 | # http://developer.android.com/guide/developing/tools/proguard.html 8 | 9 | # Add any project specific keep options here: 10 | 11 | # If your project uses WebView with JS, uncomment the following 12 | # and specify the fully qualified class name to the JavaScript interface 13 | # class: 14 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 15 | # public *; 16 | #} 17 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/base/observable/IObservable.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.base.observable; 2 | 3 | /** 4 | * 可观察对象的抽象接口,T为观察者 5 | * qqliu 6 | * 2016/9/19. 7 | */ 8 | public interface IObservable { 9 | /*** 10 | * 增加新的观察者 11 | * 12 | * @param observer 13 | */ 14 | void addObserver(T observer); 15 | 16 | /*** 17 | * 删除观察者 18 | * 19 | * @param observer 20 | */ 21 | void removeObserver(T observer); 22 | 23 | /*** 24 | * 告知观察者发生了变化 25 | * @param callback 26 | */ 27 | void notifyUpdate(INotifyUpdate callback, String identifier, Object... params); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/ui/otherscene/SpannableSkinAttr.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.ui.otherscene; 2 | 3 | import org.qcode.qskinloader.entity.DynamicAttr; 4 | 5 | /*** 6 | * author: qqliu 7 | * created at 2018/1/5 8 | */ 9 | public class SpannableSkinAttr extends DynamicAttr { 10 | 11 | public static final String HIGHLIGHT_SPANNABLE = "highlightSpannable"; 12 | public final String mText; 13 | 14 | public SpannableSkinAttr(String text, int attrValueRefId) { 15 | super(HIGHLIGHT_SPANNABLE, attrValueRefId); 16 | mText = text; 17 | 18 | //这里一定要设置,mText就丢弃了 19 | keepInstance = true; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #505050 4 | 5 | #FFFFFF 6 | 7 | #000000 8 | #FFFFFF 9 | @color/color_white 10 | #EEEEEE 11 | #f3f4f5 12 | 13 | #FF0000 14 | 15 | #00000000 16 | 17 | @color/color_transprent 18 | 19 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/ISkinAttrHandler.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader; 2 | 3 | import android.view.View; 4 | 5 | import org.qcode.qskinloader.entity.SkinAttr; 6 | 7 | /** 8 | * 皮肤属性处理器的接口 9 | * 10 | * an interface indicates how to apply special skin attributes for a view. 11 | * 12 | * qqliu 13 | * 2016/9/24. 14 | */ 15 | public interface ISkinAttrHandler { 16 | 17 | /*** 18 | * 将属性应用到View上 19 | * 20 | * apply skin attribute to view 21 | * 22 | * @param view 23 | * @param skinAttr 24 | * @param resourceManager 25 | */ 26 | void apply(View view, 27 | SkinAttr skinAttr, 28 | IResourceManager resourceManager); 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\Program Files\Android\android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/ui/gridview/GridViewActivity.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.ui.gridview; 2 | 3 | import android.os.Bundle; 4 | import android.widget.GridView; 5 | 6 | import org.qcode.demo.BaseActivity; 7 | import org.qcode.demo.ui.viewpageandlistview.DataListAdapter; 8 | import org.qcode.skintestdemo.R; 9 | 10 | public class GridViewActivity extends BaseActivity{ 11 | 12 | private GridView mGridView; 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_grid_view); 18 | mGridView = (GridView)findViewById(R.id.grid_view); 19 | mGridView.setAdapter(new DataListAdapter(this, "")); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SkinProject/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\Program Files\Android\android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/entity/SkinConstant.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.entity; 2 | 3 | /** 4 | * 皮肤框架的常量定义 5 | * qqliu 6 | * 2016/9/25. 7 | */ 8 | public class SkinConstant { 9 | /*** 10 | * 支持的命名空间 11 | */ 12 | public static final String XML_NAMESPACE = "http://schemas.android.com/android/skin"; 13 | 14 | /**属性值对应的类型是color*/ 15 | public static final String RES_TYPE_NAME_COLOR = "color"; 16 | 17 | /**属性值对应的类型是drawable*/ 18 | public static final String RES_TYPE_NAME_DRAWABLE = "drawable"; 19 | 20 | /**属性值对应的类型是mipmap*/ 21 | public static final String RES_TYPE_NAME_MIPMAP = "mipmap"; 22 | 23 | /**界面元素支持换肤的属性*/ 24 | public static final String ATTR_SKIN_ENABLE = "enable"; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/ui/viewpageandlistview/ViewPagerAndListViewActivity.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.ui.viewpageandlistview; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.view.ViewPager; 5 | 6 | import org.qcode.demo.BaseActivity; 7 | import org.qcode.skintestdemo.R; 8 | 9 | public class ViewPagerAndListViewActivity extends BaseActivity { 10 | 11 | private ViewPager mViewPager; 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_viewpager_listview); 17 | mViewPager = (ViewPager) findViewById(R.id.home_news_viewpager); 18 | mViewPager.setAdapter(new NewsPageAdapter(this)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/entity/SkinAttrName.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.entity; 2 | 3 | /** 4 | * 皮肤框架内置支持的属性 5 | * qqliu 6 | * 2016/9/27. 7 | */ 8 | public class SkinAttrName { 9 | public static final String BACKGROUND = "background"; 10 | public static final String SRC = "src"; 11 | public static final String DRAWABLE_LEFT = "drawableLeft"; 12 | public static final String TEXT_COLOR = "textColor"; 13 | public static final String TEXT_COLOR_HINT = "textColorHint"; 14 | public static final String LIST_SELECTOR = "listSelector"; 15 | public static final String DIVIDER = "divider"; 16 | public static final String DRAW_SHADOW = "drawShadow"; 17 | public static final String CLEAR_RECYCLER_VIEW = "clearRecyclerView"; 18 | } 19 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/resourceloader/ILoadResourceCallback.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.resourceloader; 2 | 3 | import org.qcode.qskinloader.IResourceManager; 4 | 5 | /** 6 | * 加载皮肤资源的回调 7 | * qqliu 8 | * 2016/9/25. 9 | */ 10 | public interface ILoadResourceCallback { 11 | /*** 12 | * 加载皮肤资源开始 13 | * 14 | * @param identifier 15 | */ 16 | void onLoadStart(String identifier); 17 | 18 | /*** 19 | * 加载皮肤资源成功 20 | * 21 | * @param identifier 22 | * @param result 23 | */ 24 | void onLoadSuccess(String identifier, IResourceManager result); 25 | 26 | /*** 27 | * 加载皮肤资源失败 28 | * 29 | * @param identifier 30 | * @param errorCode 31 | */ 32 | void onLoadFail(String identifier, int errorCode); 33 | } 34 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/IResourceLoader.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader; 2 | 3 | import org.qcode.qskinloader.resourceloader.ILoadResourceCallback; 4 | 5 | /** 6 | * 皮肤资源加载器接口 7 | * 8 | * A interface defines the load resource behaviour. 9 | * A resource loader loads resource and notify load behaviour by @ref{ILoadResourceCallback} 10 | * qqliu 11 | * 2016/9/25. 12 | */ 13 | public interface IResourceLoader { 14 | 15 | /*** 16 | * 定义资源加载的行为接口,加载的皮肤以skinIdentifier标识, 17 | * 加载结果以loadCallBack通知加载资源结果 18 | * 19 | * loads the skin identified by skinIdentifier, 20 | * notifies load behaviour by loadCallBack 21 | * @param skinIdentifier 22 | * @param loadCallBack 23 | */ 24 | void loadResource(String skinIdentifier, 25 | ILoadResourceCallback loadCallBack); 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/grid_item_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/base/utils/WeakReferenceHelper.java: -------------------------------------------------------------------------------- 1 | package com.iflytek.skin.manager.base.utils; 2 | 3 | import java.lang.ref.WeakReference; 4 | 5 | /** 6 | * qqliu 7 | * 2016/10/19. 8 | */ 9 | 10 | public class WeakReferenceHelper { 11 | 12 | private WeakReference mRef; 13 | 14 | public WeakReferenceHelper(T t) { 15 | setData(t); 16 | } 17 | 18 | public T getData() { 19 | if(null == mRef) { 20 | return null; 21 | } 22 | 23 | return mRef.get(); 24 | } 25 | 26 | public void setData(T t) { 27 | this.mRef = new WeakReference(t); 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "WeakReferenceHelper{" + 33 | "mData= " + (null == mRef ? "NULL" : mRef.get()) + 34 | '}'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_popwindow.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/ILoadSkinListener.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader; 2 | 3 | /** 4 | * 加载皮肤过程的事件回调 5 | * 6 | * a interface defines the skin loading progress. 7 | * 8 | * qqliu 9 | * 2016/9/24. 10 | */ 11 | public interface ILoadSkinListener { 12 | /*** 13 | * 加载皮肤开始 14 | * 15 | * notify skin-loading begin event 16 | * 17 | * @param skinIdentifier 18 | */ 19 | void onLoadStart(String skinIdentifier); 20 | 21 | /*** 22 | * 加载皮肤完成 23 | * 24 | * notify skin-loading success event 25 | * 26 | * @param skinIdentifier 27 | */ 28 | void onLoadSuccess(String skinIdentifier); 29 | 30 | /*** 31 | * 加载皮肤失败 32 | * 33 | * notify skin-loading fail event 34 | * 35 | * @param skinIdentifier 36 | */ 37 | void onLoadFail(String skinIdentifier); 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/skin/SkinConfigHelper.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.skin; 2 | 3 | import org.qcode.demo.base.Settings; 4 | 5 | public class SkinConfigHelper { 6 | 7 | /*** 8 | * 获取当前皮肤包的标识 9 | */ 10 | public static String getSkinIdentifier() { 11 | return Settings.getInstance().getString( 12 | SkinConstant.CUSTOM_SKIN_IDENTIFIER, 13 | SkinConstant.DEFAULT_SKIN); 14 | } 15 | 16 | /** 17 | * 保存皮肤包的标识 18 | */ 19 | public static void saveSkinIdentifier(String identifier) { 20 | Settings.getInstance().setSetting( 21 | SkinConstant.CUSTOM_SKIN_IDENTIFIER, 22 | identifier); 23 | } 24 | 25 | /** 26 | * 是否默认皮肤 27 | */ 28 | public static boolean isDefaultSkin() { 29 | return SkinConstant.DEFAULT_SKIN.equals(getSkinIdentifier()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /SkinProject/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | defaultConfig { 7 | applicationId "org.qcode.skinproject" 8 | minSdkVersion 14 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(dir: 'libs', include: ['*.jar']) 23 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 24 | exclude group: 'com.android.support', module: 'support-annotations' 25 | }) 26 | compile 'com.android.support:appcompat-v7:23.2.1' 27 | testCompile 'junit:junit:4.12' 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Sun Oct 09 14:51:38 CST 2016 16 | systemProp.http.proxyHost=127.0.0.1 17 | org.gradle.jvmargs=-Xmx1536m 18 | systemProp.http.proxyPort=1080 19 | -------------------------------------------------------------------------------- /SkinProject/gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Wed Nov 09 09:21:50 CST 2016 16 | systemProp.http.proxyHost=127.0.0.1 17 | org.gradle.jvmargs=-Xmx1536m 18 | systemProp.http.proxyPort=1080 19 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/ui/recyclerview/RecyclerViewActivity.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.ui.recyclerview; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | 7 | import org.qcode.demo.BaseActivity; 8 | import org.qcode.skintestdemo.R; 9 | 10 | public class RecyclerViewActivity extends BaseActivity { 11 | 12 | private RecyclerView mRecyclerView; 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_recycler_view); 18 | mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); 19 | mRecyclerView.setAdapter(new DataRecyclerViewAdapter(this)); 20 | LinearLayoutManager layoutManager = new LinearLayoutManager(this); 21 | mRecyclerView.setLayoutManager(layoutManager); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_recycler_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/SkinDemoApp.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import org.qcode.demo.base.Settings; 7 | import org.qcode.demo.skin.SkinChangeHelper; 8 | 9 | /** 10 | * qqliu 11 | * 2016/9/9. 12 | */ 13 | public class SkinDemoApp extends Application { 14 | 15 | private static Context mContext; 16 | 17 | public void onCreate() { 18 | super.onCreate(); 19 | mContext = this; 20 | 21 | Settings.createInstance(this); 22 | 23 | initSkinLoader(); 24 | } 25 | 26 | /** 27 | * Must call init first 28 | */ 29 | private void initSkinLoader() { 30 | // 初始化皮肤框架 31 | SkinChangeHelper.getInstance().init(this); 32 | //初始化上次缓存的皮肤 33 | SkinChangeHelper.getInstance().refreshSkin(null); 34 | } 35 | 36 | public static Context getAppContext() { 37 | return mContext; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/IViewCreateListener.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | 7 | /** 8 | * 创建View的过程中的回调 9 | * qqliu 10 | * 2016/10/17. 11 | */ 12 | 13 | public interface IViewCreateListener { 14 | /*** 15 | * 创建View之前的回调; invoked before view create, 16 | * should be used to create view outside the framework if needed 17 | * @param name 18 | * @param context 19 | * @param attrs 20 | * @return 21 | */ 22 | View beforeCreate(String name, Context context, AttributeSet attrs); 23 | 24 | /*** 25 | * 创建View之后的回调; invoked after view create, 26 | * should be used to parse view attributes outside the framework if needed 27 | * @param view 28 | * @param name 29 | * @param context 30 | * @param attrs 31 | */ 32 | void afterCreated(View view, String name, Context context, AttributeSet attrs); 33 | } 34 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | defaultConfig { 7 | applicationId "org.qcode.qskinloader" 8 | minSdkVersion 14 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:23.2.1' 28 | testCompile 'junit:junit:4.12' 29 | compile project(':QSkinLoaderlib') 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/ui/customattr/CustomTextView.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.ui.customattr; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Color; 6 | import android.util.AttributeSet; 7 | import android.widget.TextView; 8 | 9 | import org.qcode.skintestdemo.R; 10 | 11 | /** 12 | * qqliu 13 | * 2016/9/11. 14 | */ 15 | public class CustomTextView extends TextView { 16 | public CustomTextView(Context context) { 17 | this(context, null); 18 | } 19 | 20 | public CustomTextView(Context context, AttributeSet attrs) { 21 | super(context, attrs); 22 | 23 | TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.app); 24 | int textColor = array.getColor(R.styleable.app_defTextColor, Color.BLACK); 25 | int bgDrawableId = array.getResourceId(R.styleable.app_defBackground, 0); 26 | array.recycle(); 27 | 28 | setTextColor(textColor); 29 | setBackgroundResource(bgDrawableId); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/ISkinAttributeParser.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | 7 | /** 8 | * 框架解析皮肤属性的解析帮助类 9 | * 10 | * the skin attribute parser defines 11 | * how to parse attributes when view is created. 12 | * 13 | * qqliu 14 | * 2016/11/8. 15 | */ 16 | 17 | public interface ISkinAttributeParser { 18 | /*** 19 | * 是否支持换肤 20 | * 21 | * return the parse result whether the view supports skin-change 22 | * @param name 23 | * @param context 24 | * @param attrs 25 | * @return 26 | */ 27 | boolean isSupportSkin(String name, Context context, AttributeSet attrs); 28 | 29 | /*** 30 | * 解析View的皮肤属性 31 | * 32 | * parse skin attributes from view-creating process 33 | * 34 | * @param view 35 | * @param name 36 | * @param context 37 | * @param attrs 38 | */ 39 | void parseAttribute(View view, String name, Context context, AttributeSet attrs); 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/ui/customattr/CustomAttrViewActivity.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.ui.customattr; 2 | 3 | import android.os.Bundle; 4 | 5 | import org.qcode.demo.BaseActivity; 6 | import org.qcode.qskinloader.SkinManager; 7 | import org.qcode.skintestdemo.R; 8 | 9 | import static org.qcode.demo.ui.customattr.DefBackgroundAttrHandler.DEF_BACKGROUND; 10 | import static org.qcode.demo.ui.customattr.DefTextColorAttrHandler.DEF_TEXT_COLOR; 11 | 12 | public class CustomAttrViewActivity extends BaseActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | registerHandler(); 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_custom_attr_test); 19 | } 20 | 21 | private void registerHandler() { 22 | SkinManager.getInstance().registerSkinAttrHandler( 23 | DEF_TEXT_COLOR, new DefTextColorAttrHandler()); 24 | 25 | SkinManager.getInstance().registerSkinAttrHandler( 26 | DEF_BACKGROUND, new DefBackgroundAttrHandler()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/ui/otherscene/FloatView.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.ui.otherscene; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import org.qcode.qskinloader.SkinManager; 7 | import org.qcode.qskinloader.entity.SkinAttrName; 8 | import org.qcode.skintestdemo.R; 9 | 10 | /** 11 | * qqliu 12 | * 2016/10/14. 13 | */ 14 | 15 | public class FloatView extends ImageView { 16 | public FloatView(Context context) { 17 | super(context); 18 | 19 | //动态设置皮肤 20 | SkinManager 21 | .with(this) 22 | .setViewAttrs(SkinAttrName.SRC, R.drawable.drawable_float_view) 23 | .applySkin(false); 24 | } 25 | 26 | public void dismiss() { 27 | SkinManager 28 | .getWindowViewManager() 29 | .removeWindowView(this); 30 | } 31 | 32 | public void show() { 33 | //因为悬浮窗直接加载在WindowManager上,我们需要将View添加到框架内维护 34 | SkinManager 35 | .getWindowViewManager() 36 | .addWindowView(this); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_grid_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/ISkinActivity.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader; 2 | 3 | /** 4 | * 支持换肤的Activity应实现的接口 5 | * 6 | * A interface indicates that this activity supports skin change. 7 | * 8 | * qqliu 9 | * 2016/9/25. 10 | */ 11 | public interface ISkinActivity { 12 | 13 | /*** 14 | * 是否需要立刻刷新皮肤;默认不立刻换肤 15 | * tells whether refresh skin immediately, if return false, the activity will 16 | * be refreshed after focus obtained. 17 | * 18 | * @return 19 | */ 20 | boolean isSwitchSkinImmediately(); 21 | 22 | /*** 23 | * 确定是否支持换肤 24 | *tells whether the activity support skin change, 25 | * if return false, the activity will not refresh when skin change. 26 | * NOTICE: SkinManager.getInstance().applySkin() will ignore the setting 27 | * 28 | * @return 29 | */ 30 | boolean isSupportSkinChange(); 31 | 32 | /*** 33 | * 刷新皮肤; 34 | * 此处刷新的是皮肤框架管理之外的界面 35 | * 36 | * when skin changes, this method will be called, 37 | * to notify activity doing something beyond the framework's ability. 38 | */ 39 | void handleSkinChange(); 40 | } 41 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/attrhandler/BackgroundAttrHandler.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.attrhandler; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.view.View; 5 | 6 | import org.qcode.qskinloader.ISkinAttrHandler; 7 | import org.qcode.qskinloader.entity.SkinAttr; 8 | import org.qcode.qskinloader.entity.SkinAttrName; 9 | import org.qcode.qskinloader.IResourceManager; 10 | 11 | /*** 12 | * 背景属性的换肤支持(android:background) 13 | */ 14 | class BackgroundAttrHandler implements ISkinAttrHandler { 15 | 16 | @Override 17 | public void apply(View view, SkinAttr skinAttr, IResourceManager resourceManager) { 18 | if(null == view 19 | || null == skinAttr 20 | || !(SkinAttrName.BACKGROUND.equals(skinAttr.mAttrName))) { 21 | return; 22 | } 23 | 24 | Drawable drawable = SkinAttrUtils.getDrawable( 25 | resourceManager, skinAttr.mAttrValueRefId, 26 | skinAttr.mAttrValueTypeName, skinAttr.mAttrValueRefName); 27 | 28 | if(null != drawable) { 29 | view.setBackgroundDrawable(drawable); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_dynamic_add_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 20 | 21 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/attrhandler/DividerAttrHandler.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.attrhandler; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.view.View; 5 | import android.widget.ListView; 6 | 7 | import org.qcode.qskinloader.ISkinAttrHandler; 8 | import org.qcode.qskinloader.entity.SkinAttr; 9 | import org.qcode.qskinloader.entity.SkinAttrName; 10 | import org.qcode.qskinloader.IResourceManager; 11 | 12 | /*** 13 | * ListView divider属性的换肤支持(android:divider) 14 | */ 15 | class DividerAttrHandler implements ISkinAttrHandler { 16 | 17 | @Override 18 | public void apply(View view, SkinAttr skinAttr, IResourceManager resourceManager) { 19 | if (null == view 20 | || null == skinAttr 21 | || !(SkinAttrName.DIVIDER.equals(skinAttr.mAttrName))) { 22 | return; 23 | } 24 | 25 | if (!(view instanceof ListView)) { 26 | return; 27 | } 28 | 29 | ListView tv = (ListView) view; 30 | Drawable drawable = SkinAttrUtils.getDrawable( 31 | resourceManager, skinAttr.mAttrValueRefId, 32 | skinAttr.mAttrValueTypeName, skinAttr.mAttrValueRefName); 33 | 34 | if (null != drawable) { 35 | tv.setDivider(drawable); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/attrhandler/ListSelectorAttrHandler.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.attrhandler; 2 | 3 | /*** 4 | * ListView selector属性的换肤支持(android:listSelector) 5 | */ 6 | 7 | import android.graphics.drawable.Drawable; 8 | import android.view.View; 9 | import android.widget.AbsListView; 10 | 11 | import org.qcode.qskinloader.ISkinAttrHandler; 12 | import org.qcode.qskinloader.entity.SkinAttr; 13 | import org.qcode.qskinloader.entity.SkinAttrName; 14 | import org.qcode.qskinloader.IResourceManager; 15 | 16 | class ListSelectorAttrHandler implements ISkinAttrHandler { 17 | 18 | @Override 19 | public void apply(View view, SkinAttr skinAttr, IResourceManager resourceManager) { 20 | if (null == view 21 | || null == skinAttr 22 | || !(SkinAttrName.LIST_SELECTOR.equals(skinAttr.mAttrName))) { 23 | return; 24 | } 25 | 26 | if (!(view instanceof AbsListView)) { 27 | return; 28 | } 29 | 30 | Drawable drawable = SkinAttrUtils.getDrawable( 31 | resourceManager, skinAttr.mAttrValueRefId, 32 | skinAttr.mAttrValueTypeName, skinAttr.mAttrValueRefName); 33 | 34 | if (null != drawable) { 35 | ((AbsListView) view).setSelector(drawable); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_viewpager_listview.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 22 | 23 | 29 | 30 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/base/observable/Observable.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.base.observable; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 观察者通用逻辑 7 | * qqliu 8 | * 2016/9/19. 9 | */ 10 | public class Observable implements IObservable { 11 | 12 | private ArrayList mObservers; 13 | 14 | @Override 15 | public void addObserver(T observer) { 16 | if (mObservers == null) { 17 | mObservers = new ArrayList(); 18 | } 19 | 20 | if (!mObservers.contains(observer)) { 21 | mObservers.add(observer); 22 | } 23 | } 24 | 25 | @Override 26 | public void removeObserver(T observer) { 27 | if (mObservers == null) { 28 | return; 29 | } 30 | 31 | if (mObservers.contains(observer)) { 32 | mObservers.remove(observer); 33 | } 34 | } 35 | 36 | @Override 37 | public void notifyUpdate(INotifyUpdate listener, String identifier, Object... params) { 38 | if (mObservers == null || null == listener) { 39 | return; 40 | } 41 | 42 | ArrayList tmpListeners 43 | = (ArrayList) mObservers.clone(); 44 | for (T observer : tmpListeners) { 45 | listener.notifyEvent(observer, identifier, params); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/entity/SkinAttr.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.entity; 2 | 3 | /** 4 | * 皮肤指定属性及其对应的值/类型的实体类封装 5 | * qqliu 6 | * 2016/9/24. 7 | */ 8 | public class SkinAttr { 9 | /*** 10 | * 对应View的属性 11 | */ 12 | public String mAttrName; 13 | 14 | /*** 15 | * 属性值对应的reference id值,类似R.color.XX 16 | */ 17 | public int mAttrValueRefId; 18 | 19 | /*** 20 | * 属性值refrence id对应的名称,如R.color.XX,则此值为"XX" 21 | */ 22 | public String mAttrValueRefName; 23 | 24 | /*** 25 | * 属性值refrence id对应的类型,如R.color.XX,则此值为color 26 | */ 27 | public String mAttrValueTypeName; 28 | 29 | /*** 30 | * 直接存放自定义的属性 31 | */ 32 | public DynamicAttr mDynamicAttr; 33 | 34 | public SkinAttr() { 35 | //empty 36 | } 37 | 38 | public SkinAttr(String attrName) { 39 | mAttrName = attrName; 40 | //others is empty 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "SkinAttr{" + 46 | "mAttrName='" + mAttrName + '\'' + 47 | ", mAttrValueRefId=" + mAttrValueRefId + 48 | ", mAttrValueRefName='" + mAttrValueRefName + '\'' + 49 | ", mAttrValueTypeName='" + mAttrValueTypeName + '\'' + 50 | ", mDynamicAttr='" + mDynamicAttr + '\'' + 51 | '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/resourceloader/impl/SuffixResourceLoader.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.resourceloader.impl; 2 | 3 | import android.content.Context; 4 | 5 | import org.qcode.qskinloader.IResourceLoader; 6 | import org.qcode.qskinloader.base.utils.StringUtils; 7 | import org.qcode.qskinloader.resourceloader.ILoadResourceCallback; 8 | 9 | /** 10 | * 基于资源名称后缀拼接方式的资源加载器 11 | * qqliu 12 | * 2016/10/9. 13 | */ 14 | public class SuffixResourceLoader implements IResourceLoader { 15 | 16 | private static final String TAG = "SuffixResourceLoader"; 17 | 18 | private Context mContext; 19 | 20 | private String mSkinSuffix; 21 | 22 | public SuffixResourceLoader(Context context) { 23 | this.mContext = context; 24 | } 25 | 26 | @Override 27 | public void loadResource(final String skinIdentifier, 28 | final ILoadResourceCallback loadCallBack) { 29 | if (StringUtils.isEmpty(skinIdentifier)) { 30 | return; 31 | } 32 | 33 | if (loadCallBack != null) { 34 | loadCallBack.onLoadStart(skinIdentifier); 35 | } 36 | 37 | mSkinSuffix = skinIdentifier; 38 | 39 | if (loadCallBack != null) { 40 | loadCallBack.onLoadSuccess(skinIdentifier, 41 | new SuffixResourceManager(mContext, mSkinSuffix)); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/attrhandler/DrawableLeftAttrHandler.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.attrhandler; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | import org.qcode.qskinloader.ISkinAttrHandler; 8 | import org.qcode.qskinloader.entity.SkinAttr; 9 | import org.qcode.qskinloader.entity.SkinAttrName; 10 | import org.qcode.qskinloader.IResourceManager; 11 | 12 | /** 13 | * TextView的drawableLeft属性处理 14 | * qqliu 15 | * 2016/9/27. 16 | */ 17 | class DrawableLeftAttrHandler implements ISkinAttrHandler { 18 | 19 | @Override 20 | public void apply(View view, SkinAttr skinAttr, IResourceManager resourceManager) { 21 | if(null == view 22 | || null == skinAttr 23 | || !(SkinAttrName.DRAWABLE_LEFT.equals(skinAttr.mAttrName))) { 24 | return; 25 | } 26 | 27 | if (!(view instanceof TextView)) { 28 | return; 29 | } 30 | 31 | Drawable drawable = SkinAttrUtils.getDrawable(resourceManager, 32 | skinAttr.mAttrValueRefId, 33 | skinAttr.mAttrValueTypeName, 34 | skinAttr.mAttrValueRefName); 35 | 36 | if(null != drawable) { 37 | ((TextView)view).setCompoundDrawablesWithIntrinsicBounds( 38 | drawable, null, null, null); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/entity/DynamicAttr.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.entity; 2 | 3 | /** 4 | * 动态代码设置皮肤属性的实体类 5 | * 6 | * qqliu 7 | * 2016/9/25. 8 | */ 9 | public class DynamicAttr { 10 | 11 | /*** 12 | * 对应View的属性 13 | */ 14 | public String mAttrName; 15 | 16 | /*** 17 | * 属性值对应的reference id值,类似R.color.XX 18 | */ 19 | public int mAttrValueRefId = -1; 20 | 21 | /**是否设置了属性值引用*/ 22 | public boolean hasSetValueRef = false; 23 | 24 | /*** 25 | * 是否保留dynamicAttr; 26 | * 子类继承DynamicAttr时可以改变此属性来保留自定义的属性值 27 | */ 28 | public boolean keepInstance = false; 29 | 30 | public DynamicAttr(String attrName) { 31 | this.mAttrName = attrName; 32 | hasSetValueRef = false; 33 | keepInstance = false; 34 | } 35 | 36 | public DynamicAttr(String attrName, int attrValueRefId) { 37 | this.mAttrName = attrName; 38 | this.mAttrValueRefId = attrValueRefId; 39 | hasSetValueRef = true; 40 | keepInstance = false; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "DynamicAttr{" + 46 | "mAttrName='" + mAttrName + '\'' + 47 | ", mAttrValueRefId=" + mAttrValueRefId + 48 | ", hasSetValueRef=" + hasSetValueRef + 49 | ", keepInstance=" + keepInstance + 50 | '}'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/ui/customattr/DefBackgroundAttrHandler.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.ui.customattr; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.view.View; 5 | 6 | import org.qcode.qskinloader.IResourceManager; 7 | import org.qcode.qskinloader.ISkinAttrHandler; 8 | import org.qcode.qskinloader.attrhandler.SkinAttrUtils; 9 | import org.qcode.qskinloader.entity.SkinAttr; 10 | 11 | /** 12 | * qqliu 13 | * 2016/10/9. 14 | */ 15 | 16 | public class DefBackgroundAttrHandler implements ISkinAttrHandler { 17 | public static final String DEF_BACKGROUND = "defBackground"; 18 | 19 | @Override 20 | public void apply(View view, SkinAttr skinAttr, IResourceManager resourceManager) { 21 | if (null == view 22 | || null == skinAttr 23 | || !(DEF_BACKGROUND.equals(skinAttr.mAttrName))) { 24 | //自定义属性处理时,需要明确当前处理器能处理的属性,此处是DEF_BACKGROUND 25 | return; 26 | } 27 | 28 | if (!(view instanceof CustomTextView)) { 29 | //防止在错误的View上设置了此属性 30 | return; 31 | } 32 | 33 | //封装了取ColorDrawable和取普通Drawable的逻辑 34 | Drawable drawable = SkinAttrUtils.getDrawable( 35 | resourceManager, skinAttr.mAttrValueRefId, 36 | skinAttr.mAttrValueTypeName, skinAttr.mAttrValueRefName); 37 | 38 | if (null != drawable) { 39 | view.setBackgroundDrawable(drawable); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/impl/ViewSkinTagHelper.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.impl; 2 | 3 | import android.view.View; 4 | 5 | import org.qcode.qskinloader.R; 6 | import org.qcode.qskinloader.entity.SkinAttrSet; 7 | 8 | /** 9 | * 存取View的皮肤属性的帮助类 10 | * qqliu 11 | * 2016/10/8. 12 | */ 13 | 14 | class ViewSkinTagHelper { 15 | 16 | /*** 17 | * 设置View的皮肤属性 18 | * @param view 19 | * @param skinAttrSet 20 | */ 21 | public static void setSkinAttrs(View view, SkinAttrSet skinAttrSet) { 22 | if(null == view) { 23 | return; 24 | } 25 | view.setTag(R.id.tag_skin_attr, skinAttrSet); 26 | } 27 | 28 | /*** 29 | * 添加View的皮肤属性 30 | * @param view 31 | * @param skinAttrSet 32 | */ 33 | public static void addSkinAttrs(View view, SkinAttrSet skinAttrSet) { 34 | if(null == view) { 35 | return; 36 | } 37 | 38 | SkinAttrSet attrSet = getSkinAttrs(view); 39 | if (null == attrSet) { 40 | view.setTag(R.id.tag_skin_attr, skinAttrSet); 41 | } else { 42 | attrSet.addSkinAttrSet(skinAttrSet); 43 | } 44 | } 45 | 46 | /*** 47 | * 获取View的皮肤属性 48 | * @param view 49 | * @return skinAttrSet 50 | */ 51 | public static SkinAttrSet getSkinAttrs(View view) { 52 | if(null == view) { 53 | return null; 54 | } 55 | 56 | return (SkinAttrSet) view.getTag(R.id.tag_skin_attr); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/skin/SkinUtils.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo.skin; 2 | 3 | import android.content.Context; 4 | 5 | import org.qcode.demo.utils.FileUtils; 6 | import org.qcode.qskinloader.base.utils.Logging; 7 | 8 | import java.io.File; 9 | 10 | /** 11 | * qqliu 12 | * 2016/9/21. 13 | */ 14 | public class SkinUtils { 15 | private static final String TAG = "NightModeUtils"; 16 | 17 | private static final String SKIN_NAME = "nightMode.skin"; 18 | 19 | public static String getTotalSkinPath(Context context) { 20 | String SKIN_PATH = context.getCacheDir().getAbsolutePath(); 21 | String totalPath = SKIN_PATH + File.separator + SKIN_NAME; 22 | return totalPath; 23 | } 24 | 25 | public static boolean copyAssetSkin(Context context) { 26 | String totalPath = getTotalSkinPath(context); 27 | File skin = new File(totalPath); 28 | 29 | if (skin == null || !skin.exists() || needUpdateSkin()) { 30 | long currTime = System.currentTimeMillis(); 31 | 32 | boolean isSuccess = FileUtils.copyAssetFile(context, SKIN_NAME, 33 | context.getCacheDir().getAbsolutePath(), 34 | SKIN_NAME); 35 | 36 | long diff = System.currentTimeMillis() - currTime; 37 | Logging.d(TAG, "copyAssetSkin()| copy file time: " + diff); 38 | 39 | return isSuccess; 40 | } 41 | 42 | return false; 43 | } 44 | 45 | private static boolean needUpdateSkin() { 46 | //每次都拷贝皮肤包 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/base/utils/HashMapCache.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.base.utils; 2 | 3 | import java.lang.ref.WeakReference; 4 | import java.util.HashMap; 5 | 6 | /** 7 | * 维护一个缓存Map,包括强引用和弱引用两种维护方式; 8 | * 两种方式中key都是强引用; 9 | * qqliu 10 | * 2016/9/19. 11 | */ 12 | public class HashMapCache { 13 | private HashMap mCacheMap = null; 14 | private HashMap> mWeakCacheMap = null; 15 | 16 | /*** 17 | * @param isStrongReference true 强引用,false弱引用 18 | */ 19 | public HashMapCache(boolean isStrongReference) { 20 | if (isStrongReference) { 21 | mCacheMap = new HashMap(); 22 | } else { 23 | mWeakCacheMap = new HashMap>(); 24 | } 25 | } 26 | 27 | public V getCache(K key) { 28 | if(null == key) { 29 | return null; 30 | } 31 | 32 | if(null != mCacheMap) { 33 | return mCacheMap.get(key); 34 | } else { 35 | WeakReference refValue = mWeakCacheMap.get(key); 36 | if(null != refValue) { 37 | return refValue.get(); 38 | } 39 | return null; 40 | } 41 | } 42 | 43 | public void addCache(K key, V value) { 44 | if(null == key) { 45 | return; 46 | } 47 | 48 | if(null != mCacheMap) { 49 | mCacheMap.put(key, value); 50 | } else { 51 | WeakReference refValue = new WeakReference(value); 52 | mWeakCacheMap.put(key, refValue); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/attrhandler/TextColorHintAttrHandler.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.attrhandler; 2 | 3 | import android.content.res.ColorStateList; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | import org.qcode.qskinloader.IResourceManager; 8 | import org.qcode.qskinloader.ISkinAttrHandler; 9 | import org.qcode.qskinloader.entity.SkinAttr; 10 | import org.qcode.qskinloader.entity.SkinAttrName; 11 | import org.qcode.qskinloader.entity.SkinConstant; 12 | 13 | /*** 14 | * 文字提示颜色属性的换肤支持(android:textColorHint) 15 | */ 16 | class TextColorHintAttrHandler implements ISkinAttrHandler { 17 | 18 | @Override 19 | public void apply(View view, SkinAttr skinAttr, IResourceManager resourceManager) { 20 | if (null == view 21 | || null == skinAttr 22 | || !(SkinAttrName.TEXT_COLOR_HINT.equals(skinAttr.mAttrName))) { 23 | return; 24 | } 25 | 26 | if (!(view instanceof TextView)) { 27 | return; 28 | } 29 | 30 | TextView tv = (TextView) view; 31 | if (SkinConstant.RES_TYPE_NAME_COLOR.equals(skinAttr.mAttrValueTypeName)) { 32 | //按照ColorStateList引用来解析; 33 | //context.getResources().getColor()方法可以取纯颜色,也可以取ColorStateList引用内的颜色, 34 | //如果取的是ColorStateList,则取其中默认颜色; 35 | //同时,context.getResources().getColorStateList()方法也可以取纯颜色生成一个ColorStateList 36 | ColorStateList textHintColor = resourceManager.getColorStateList( 37 | skinAttr.mAttrValueRefId, skinAttr.mAttrValueRefName); 38 | tv.setHintTextColor(textHintColor); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /QSkinLoaderlib/src/main/java/org/qcode/qskinloader/attrhandler/TextColorAttrHandler.java: -------------------------------------------------------------------------------- 1 | package org.qcode.qskinloader.attrhandler; 2 | 3 | import android.content.res.ColorStateList; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | import org.qcode.qskinloader.IResourceManager; 8 | import org.qcode.qskinloader.ISkinAttrHandler; 9 | import org.qcode.qskinloader.entity.SkinAttr; 10 | import org.qcode.qskinloader.entity.SkinAttrName; 11 | import org.qcode.qskinloader.entity.SkinConstant; 12 | 13 | /*** 14 | * 文字颜色属性的换肤支持(android:textColor) 15 | */ 16 | class TextColorAttrHandler implements ISkinAttrHandler { 17 | 18 | @Override 19 | public void apply(View view, SkinAttr skinAttr, IResourceManager resourceManager) { 20 | if (null == view 21 | || null == skinAttr 22 | || !(SkinAttrName.TEXT_COLOR.equals(skinAttr.mAttrName))) { 23 | return; 24 | } 25 | 26 | if (!(view instanceof TextView)) { 27 | return; 28 | } 29 | 30 | TextView tv = (TextView) view; 31 | if (SkinConstant.RES_TYPE_NAME_COLOR.equals(skinAttr.mAttrValueTypeName) 32 | || SkinConstant.RES_TYPE_NAME_DRAWABLE.equals(skinAttr.mAttrValueTypeName)) { 33 | //按照ColorStateList引用来解析; 34 | //context.getResources().getColor()方法可以取纯颜色,也可以取ColorStateList引用内的颜色, 35 | //如果取的是ColorStateList,则取其中默认颜色; 36 | //同时,context.getResources().getColorStateList()方法也可以取纯颜色生成一个ColorStateList 37 | ColorStateList textColor = resourceManager.getColorStateList( 38 | skinAttr.mAttrValueRefId, skinAttr.mAttrValueTypeName, skinAttr.mAttrValueRefName); 39 | tv.setTextColor(textColor); 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 28 | 29 | 32 | 33 | 36 | 37 | 40 | 41 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/java/org/qcode/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.qcode.demo; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import org.qcode.demo.ui.customattr.CustomAttrViewActivity; 8 | import org.qcode.demo.ui.dynamicaddview.DynamicAddViewActivity; 9 | import org.qcode.demo.ui.gridview.GridViewActivity; 10 | import org.qcode.demo.ui.otherscene.OtherSceneActivity; 11 | import org.qcode.demo.ui.recyclerview.RecyclerViewActivity; 12 | import org.qcode.demo.ui.viewpageandlistview.ViewPagerAndListViewActivity; 13 | import org.qcode.skintestdemo.R; 14 | 15 | /** 16 | * qqliu 17 | * 2016/10/10. 18 | */ 19 | 20 | public class MainActivity extends BaseActivity { 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_main); 25 | } 26 | 27 | public void onClick(View view) { 28 | Intent intent = new Intent(); 29 | switch (view.getId()) { 30 | case R.id.btnCustomView: 31 | intent.setClass(this, CustomAttrViewActivity.class); 32 | break; 33 | case R.id.btnDynamicAddView: 34 | intent.setClass(this, DynamicAddViewActivity.class); 35 | break; 36 | case R.id.btnRecyclerView: 37 | intent.setClass(this, RecyclerViewActivity.class); 38 | break; 39 | case R.id.btnViewPagerAndListView: 40 | intent.setClass(this, ViewPagerAndListViewActivity.class); 41 | break; 42 | case R.id.btnGridView: 43 | intent.setClass(this, GridViewActivity.class); 44 | break; 45 | case R.id.btnOtherScene: 46 | intent.setClass(this, OtherSceneActivity.class); 47 | break; 48 | } 49 | startActivity(intent); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_dialog_custom.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | 26 |