diff --git a/gradle.properties b/gradle.properties index 174979a..4a664d7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -58,4 +58,4 @@ MOGO_TELEMATIC_VERSION=1.4.7.42 # v2x MOGO_V2X_VERSION=1.4.7.42 # SKIN -MOGO_SKIN_VERSION=1.4.7.49.1-debug +MOGO_SKIN_VERSION=1.4.7.49.3-debug diff --git a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinActivityLifecycle.java b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinActivityLifecycle.java index 058df63..7f3c6dd 100644 --- a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinActivityLifecycle.java +++ b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinActivityLifecycle.java @@ -32,12 +32,9 @@ public class SkinActivityLifecycle implements Application.ActivityLifecycleCallb /* 更新状态栏 */ SkinThemeUtils.updateStatusBar(activity); - /* 字体 */ - Typeface typeface = SkinThemeUtils.getSkinTypeface(activity); - //更新布局 LayoutInflater layoutInflater = LayoutInflater.from(activity); - SkinLayoutFactory skinLayoutFactory = new SkinLayoutFactory(activity, typeface); + SkinLayoutFactory skinLayoutFactory = new SkinLayoutFactory(activity); //获得Activity的布局加载器 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { //反射 diff --git a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinAttribute.java b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinAttribute.java index 3f08ebf..1d58cd8 100644 --- a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinAttribute.java +++ b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinAttribute.java @@ -1,27 +1,18 @@ package com.mogo.skin; import android.graphics.Typeface; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.Looper; import android.util.AttributeSet; -import android.util.Log; import android.view.View; -import android.widget.EditText; -import android.widget.ImageView; import android.widget.TextView; -import androidx.core.view.ViewCompat; - -import com.mogo.skin.utils.SkinResources; import com.mogo.skin.utils.SkinThemeUtils; import java.util.ArrayList; import java.util.List; /** - * donghongyu + * @author donghongyu + * @description : 属性集合 需要针对哪些属性进行替换 */ public class SkinAttribute { @@ -44,16 +35,19 @@ public class SkinAttribute { mAttributes.add("hint"); } - private Typeface typeface; - private static final String TAG = "SkinAttribute"; + /** + * 记录换肤需要操作的View + */ List mSkinViews = new ArrayList<>(); - public SkinAttribute(Typeface typeface) { - this.typeface = typeface; + public SkinAttribute() { } + /** + * 记录View中哪几个属性需要换肤,找到当前页面的所有属性 + */ public void load(View view, AttributeSet attrs) { List skinPairs = new ArrayList<>(); for (int i = 0; i < attrs.getAttributeCount(); i++) { @@ -61,13 +55,18 @@ public class SkinAttribute { String attributeName = attrs.getAttributeName(i); //是否符合 需要筛选的属性名 if (mAttributes.contains(attributeName)) { + //属性名写法 + //#ffff + //?attr/xxx + //@string/xxx String attributeValue = attrs.getAttributeValue(i); - //写死了,不管了 + //如果是写死的字符串 #fffff if (attributeValue.startsWith("#")) { continue; } //资源id int resId = 0; + //以?开头的表示用属性 if (attributeValue.startsWith("?")) { //attr Id int attrId = Integer.parseInt(attributeValue.substring(1)); @@ -75,6 +74,7 @@ public class SkinAttribute { resId = SkinThemeUtils.getResId(view.getContext(), new int[]{attrId})[0]; } else { try { + // 以@开头的 // @12343455332 String resIdStr = attributeValue.substring(1); if (isNumeric(resIdStr)) { @@ -95,8 +95,9 @@ public class SkinAttribute { //将View与之对应的可以动态替换的属性集合 放入 集合中 if (!skinPairs.isEmpty() || view instanceof TextView || view instanceof SkinViewSupport) { + //选择皮肤 更新 SkinView skinView = new SkinView(view, skinPairs); - skinView.applySkin(typeface); + skinView.applySkin(); mSkinViews.add(skinView); } } @@ -111,169 +112,9 @@ public class SkinAttribute { /** * 换皮肤 */ - public void applySkin(Typeface typeface) { + public void applySkin() { for (SkinView mSkinView : mSkinViews) { - mSkinView.applySkin(typeface); + mSkinView.applySkin(); } } - - static class SkinView { - - View view; - List skinPairs; - - public SkinView(View view, List skinPairs) { - this.view = view; - this.skinPairs = skinPairs; - } - - /** - * @param typeface 字体 - */ - public void applySkin(Typeface typeface) { - //Log.d(TAG, "applySkin() called with: typeface = [" + typeface + "] view = [" + view + "] Parent = " + view.getParent()); - //applySkinTypeface(typeface); - applySkinViewSupport(); - try { - for (SkinPair skinPair : skinPairs) { - Drawable left = null, top = null, right = null, bottom = null; - String textStr; - switch (skinPair.attributeName) { - case "background": - Object background = SkinResources.getInstance().getBackground(skinPair.resId); - //Color - if (background instanceof Integer) { - view.setBackgroundColor((Integer) background); - } else { - ViewCompat.setBackground(view, (Drawable) background); - } - break; - case "src": - try { - if (view instanceof ImageView) { - background = SkinResources.getInstance().getBackground(skinPair.resId); - if (background instanceof Integer) { - ((ImageView) view).setImageDrawable(new ColorDrawable((Integer) background)); - } else { - ((ImageView) view).setImageDrawable((Drawable) background); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - break; - case "textColor": - try { - ((TextView) view).setTextColor(SkinResources.getInstance().getColorStateList(skinPair.resId)); - } catch (Exception e) { - e.printStackTrace(); - } - break; - case "textColorHint": - try { - ((EditText) view).setHintTextColor(SkinResources.getInstance().getColorStateList(skinPair.resId)); - } catch (Exception e) { - e.printStackTrace(); - } - break; - case "drawableLeft": - left = SkinResources.getInstance().getDrawable(skinPair.resId); - break; - case "drawableTop": - top = SkinResources.getInstance().getDrawable(skinPair.resId); - break; - case "drawableRight": - right = SkinResources.getInstance().getDrawable(skinPair.resId); - break; - case "drawableBottom": - bottom = SkinResources.getInstance().getDrawable(skinPair.resId); - break; - case "skinTypeface": - Typeface typeface1 = SkinResources.getInstance().getTypeface(skinPair.resId); - applySkinTypeface(typeface1); - break; - case "text": - if (skinPair.resId > 0) { - textStr = SkinResources.getInstance().getString(skinPair.resId); - applyText(textStr); - } - break; - case "hint": - if (skinPair.resId > 0) { - textStr = SkinResources.getInstance().getString(skinPair.resId); - applyHintText(textStr); - } - break; - default: - break; - } - if (null != left || null != right || null != top || null != bottom) { - ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - private static Handler mHandler = new Handler(Looper.getMainLooper()); - - private void applySkinViewSupport() { - if (view instanceof SkinViewSupport) { - ((SkinViewSupport) view).applySkin(); - } - } - - private void applySkinTypeface(final Typeface typeface) { - if (view instanceof TextView) { - //Log.d(TAG, "applySkinTypeface() called with: view = [" + ((TextView) view).getText() + "] Parent = " + view.getParent()); - //post 防止某些控件的属性设置是在构造函数调用完成之后进行的。 - mHandler.post(new Runnable() { - @Override - public void run() { - ((TextView) view).setTypeface(typeface); - } - }); - } - } - - private void applyText(final String textStr) { - if (view instanceof TextView) { - //Log.d(TAG, "applyText() called with: view = [" + ((TextView) view).getText() + "] Parent = " + view.getParent()); - //post 防止某些控件的属性设置是在构造函数调用完成之后进行的。 - mHandler.post(new Runnable() { - @Override - public void run() { - ((TextView) view).setText(textStr); - } - }); - } - } - - private void applyHintText(final String textStr) { - if (view instanceof EditText) { - //Log.d(TAG, "applyHintText() called with: view = [" + ((TextView) view).getText() + "] Parent = " + view.getParent()); - //post 防止某些控件的属性设置是在构造函数调用完成之后进行的。 - mHandler.post(new Runnable() { - @Override - public void run() { - ((EditText) view).setHint(textStr); - } - }); - } - } - - } - - static class SkinPair { - - String attributeName; - int resId; - - public SkinPair(String attributeName, int resId) { - this.attributeName = attributeName; - this.resId = resId; - } - } - } diff --git a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinLayoutFactory.java b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinLayoutFactory.java index 392ccd5..723f60e 100644 --- a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinLayoutFactory.java +++ b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinLayoutFactory.java @@ -29,39 +29,45 @@ public class SkinLayoutFactory implements LayoutInflater.Factory2, Observer { private static final HashMap> sConstructorMap = new HashMap<>(); + //用于获取窗口的状态框的信息 private Activity activity; - // 属性处理类 + // 当选择新皮肤后需要替换View与之对应的属性 + // 页面属性管理器 SkinAttribute skinAttribute; - public SkinLayoutFactory(Activity activity, Typeface typeface) { + public SkinLayoutFactory(Activity activity) { this.activity = activity; - skinAttribute = new SkinAttribute(typeface); + skinAttribute = new SkinAttribute(); } private static final String TAG = "SkinLayoutFactory"; - private int count = 0; @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { - //反射 classloader + //换肤就是在需要时候替换 View的属性(src、background等) + //创建 View,从而修改View属性 View view = createViewFromTag(name, context, attrs); // 自定义View if (null == view) { view = createView(name, context, attrs); } - //筛选符合属性的View - skinAttribute.load(view, attrs); + if (view != null) { + //找到这个view 保存并更新视图 + //筛选符合属性的View + skinAttribute.load(view, attrs); + } return view; } private View createViewFromTag(String name, Context context, AttributeSet attrs) { - //包含了 . 自定义控件 + //包含 . 的话说明是自定义继承SkinViewSupport或者support中的视图,androidx,不是SDK中的view if (-1 != name.indexOf(".")) { return null; } View view = null; + //不包含就需要在解析的节点name前加上 android.widget 去反射 for (int i = 0; i < mClassPrefixList.length; i++) { view = createView(mClassPrefixList[i] + name, context, attrs); if (null != view) { @@ -76,6 +82,7 @@ public class SkinLayoutFactory implements LayoutInflater.Factory2, Observer { if (null == constructor) { try { Class aClass = context.getClassLoader().loadClass(name).asSubclass(View.class); + //两个参数 constructor = aClass.getConstructor(mConstructorSignature); sConstructorMap.put(name, constructor); } catch (Exception e) { @@ -97,10 +104,12 @@ public class SkinLayoutFactory implements LayoutInflater.Factory2, Observer { @Override public void update(Observable o, Object arg) { + //作为观察者被通知更新 SkinThemeUtils.updateStatusBar(activity); - Typeface typeface = SkinThemeUtils.getSkinTypeface(activity); - // 更换皮肤 - skinAttribute.applySkin(typeface); + if (skinAttribute != null) { + // 更换皮肤 + skinAttribute.applySkin(); + } } } diff --git a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinManager.java b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinManager.java index d57e573..4bfd515 100644 --- a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinManager.java +++ b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinManager.java @@ -15,13 +15,12 @@ import com.mogo.skin.net.DownloadManager; import com.mogo.skin.utils.FileUtils; import com.mogo.skin.utils.SkinPreference; import com.mogo.skin.utils.SkinResources; +import com.mogo.skin.utils.SkinUtils; import java.io.File; import java.lang.reflect.Method; import java.util.Observable; -import dalvik.system.DexClassLoader; - /** * donghongyu */ @@ -31,9 +30,17 @@ public class SkinManager extends Observable { private static Application mApplication; private static SkinManager instance; + /** + * Activity生命周期回调 + */ private SkinActivityLifecycle skinActivityLifecycle; private Application application; + /** + * 注意首次初始化时需要从配置文件读取使用哪个皮肤,然后应用 + * + * @param application + */ public static void init(Application application) { synchronized (SkinManager.class) { if (null == instance) { @@ -50,9 +57,11 @@ public class SkinManager extends Observable { private SkinManager(Application application) { this.application = application; + //记录当前使用的皮肤 SkinPreference.init(application); + //资源管理类 用于从 app/皮肤 中加载资源 SkinResources.init(application); - //注册Activity生命周期回调 + //注册Activity生命周期,并设置被观察者 skinActivityLifecycle = new SkinActivityLifecycle(); application.registerActivityLifecycleCallbacks(skinActivityLifecycle); @@ -61,23 +70,24 @@ public class SkinManager extends Observable { } /** - * 加载皮肤包 并 更新 + * 记载皮肤并应用 * - * @param skinPath 皮肤包路径 + * @param skinPath 皮肤路径 如果为空则使用默认皮肤 */ public void loadSkin(String skinPath) { Log.d(TAG, "加载皮肤路径:" + skinPath); - //还原默认皮肤包 + if (TextUtils.isEmpty(skinPath)) { + //还原默认皮肤包 SkinPreference.getInstance().setSkin(""); SkinResources.getInstance().reset(); } // 加载指定目录下的皮肤 else { try { - // 通过反射获取AssetManager + //反射创建AssetManager 与 Resource AssetManager assetManager = AssetManager.class.newInstance(); - // 获取插件APK的AssetManager + // 资源路径设置 目录或压缩包 Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.setAccessible(true); int result = (int) addAssetPath.invoke(assetManager, skinPath); @@ -87,13 +97,14 @@ public class SkinManager extends Observable { // 获取当前Resources Resources resources = application.getResources(); - // 获取皮肤插件的 Resources 横竖、语言 + //根据当前的设备显示器信息 与 配置(横竖屏、语言等) 创建Resources Resources skinResource = new Resources(assetManager, resources.getDisplayMetrics(), resources.getConfiguration()); // 获取外部Apk(皮肤包) 包名 PackageManager mPm = application.getPackageManager(); PackageInfo info = mPm.getPackageArchiveInfo(skinPath, PackageManager.GET_ACTIVITIES); String packageName = info.packageName; SkinResources.getInstance().applySkin(skinResource, packageName); + //保存当前使用的皮肤包 SkinPreference.getInstance().setSkin(skinPath); } catch (Exception e) { @@ -155,7 +166,7 @@ public class SkinManager extends Observable { if (TextUtils.equals(SkinUtils.getSkinMD5(skinFile), skin.md5)) { Log.d("SkinActivity", "校验成功,修改文件名。"); // 校验文件签名没问题后删除其他主题 - deleteFilesExcept(theme,skin.file.getName()); + deleteFilesExcept(theme, skin.file.getName()); } else { Log.e("SkinActivity", "皮肤文件校验出错,本地文件MD5 与云端文件MD5 不一致。"); Toast.makeText(mApplication, "皮肤文件校验出错,本地文件MD5 与云端文件MD5 不一致。", Toast.LENGTH_SHORT).show(); @@ -172,7 +183,6 @@ public class SkinManager extends Observable { // 下载失败后的处理 Log.e("DownloadManager", "onDownloadFailed"); e.printStackTrace(); - } @Override diff --git a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinPair.java b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinPair.java new file mode 100644 index 0000000..5efeada --- /dev/null +++ b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinPair.java @@ -0,0 +1,13 @@ +package com.mogo.skin; + +public class SkinPair { + /**属性名*/ + String attributeName; + /**对应的资源ID*/ + int resId; + + public SkinPair(String attributeName, int resId) { + this.attributeName = attributeName; + this.resId = resId; + } +} \ No newline at end of file diff --git a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinView.java b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinView.java new file mode 100644 index 0000000..69a32a9 --- /dev/null +++ b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinView.java @@ -0,0 +1,152 @@ +package com.mogo.skin; + + +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.view.View; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.core.view.ViewCompat; + +import com.mogo.skin.utils.SkinResources; + +import java.util.List; + +/** + * @author donghongyu + * 每个换肤需要操作的View以及它的属性集合 + */ +public class SkinView { + View view; + List skinPairs; + + public SkinView(View view, List skinPairs) { + this.view = view; + this.skinPairs = skinPairs; + } + + public void applySkin() { + applySkinSupport(); + try { + for (SkinPair skinPair : skinPairs) { + Drawable left = null, top = null, right = null, bottom = null; + String textStr; + + switch (skinPair.attributeName) { + case "background": + Object background = SkinResources.getInstance().getBackground(skinPair + .resId); + //背景可能是 @color 也可能是 @drawable + if (background instanceof Integer) { + view.setBackgroundColor((int) background); + } else { + ViewCompat.setBackground(view, (Drawable) background); + } + break; + case "src": + try { + if (view instanceof ImageView) { + background = SkinResources.getInstance().getBackground(skinPair + .resId); + if (background instanceof Integer) { + ((ImageView) view).setImageDrawable(new ColorDrawable((Integer) + background)); + } else { + ((ImageView) view).setImageDrawable((Drawable) background); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + break; + case "textColor": + try { + ((TextView) view).setTextColor(SkinResources.getInstance().getColorStateList(skinPair.resId)); + } catch (Exception e) { + e.printStackTrace(); + } + break; + case "textColorHint": + try { + ((EditText) view).setHintTextColor(SkinResources.getInstance().getColorStateList(skinPair.resId)); + } catch (Exception e) { + e.printStackTrace(); + } + break; + case "drawableLeft": + left = SkinResources.getInstance().getDrawable(skinPair.resId); + break; + case "drawableTop": + top = SkinResources.getInstance().getDrawable(skinPair.resId); + break; + case "drawableRight": + right = SkinResources.getInstance().getDrawable(skinPair.resId); + break; + case "drawableBottom": + bottom = SkinResources.getInstance().getDrawable(skinPair.resId); + break; + case "text": + if (skinPair.resId > 0) { + textStr = SkinResources.getInstance().getString(skinPair.resId); + applyText(textStr); + } + break; + case "hint": + if (skinPair.resId > 0) { + textStr = SkinResources.getInstance().getString(skinPair.resId); + applyHintText(textStr); + } + default: + break; + } + if (null != left || null != right || null != top || null != bottom) { + ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(left, top, right, + bottom); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 自定义实现SkinViewSupport的类 + */ + private void applySkinSupport() { + if (view instanceof SkinViewSupport) { + ((SkinViewSupport) view).applySkin(); + } + } + + private static Handler mHandler = new Handler(Looper.getMainLooper()); + + private void applyText(final String textStr) { + if (view instanceof TextView) { + //Log.d(TAG, "applyText() called with: view = [" + ((TextView) view).getText() + "] Parent = " + view.getParent()); + //post 防止某些控件的属性设置是在构造函数调用完成之后进行的。 + mHandler.post(new Runnable() { + @Override + public void run() { + ((TextView) view).setText(textStr); + } + }); + } + } + + private void applyHintText(final String textStr) { + if (view instanceof EditText) { + //Log.d(TAG, "applyHintText() called with: view = [" + ((TextView) view).getText() + "] Parent = " + view.getParent()); + //post 防止某些控件的属性设置是在构造函数调用完成之后进行的。 + mHandler.post(new Runnable() { + @Override + public void run() { + ((EditText) view).setHint(textStr); + } + }); + } + } +} \ No newline at end of file diff --git a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinViewSupport.java b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinViewSupport.java index 18236b6..c926b1d 100644 --- a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinViewSupport.java +++ b/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinViewSupport.java @@ -2,6 +2,7 @@ package com.mogo.skin; /** * donghongyu + * 主要是提供自定义View更新视图 */ public interface SkinViewSupport { diff --git a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinUtils.java b/libraries/mogo-skin/src/main/java/com/mogo/skin/utils/SkinUtils.java similarity index 97% rename from libraries/mogo-skin/src/main/java/com/mogo/skin/SkinUtils.java rename to libraries/mogo-skin/src/main/java/com/mogo/skin/utils/SkinUtils.java index dfc9e81..c48d39f 100644 --- a/libraries/mogo-skin/src/main/java/com/mogo/skin/SkinUtils.java +++ b/libraries/mogo-skin/src/main/java/com/mogo/skin/utils/SkinUtils.java @@ -1,4 +1,4 @@ -package com.mogo.skin; +package com.mogo.skin.utils; import java.io.File; import java.io.FileInputStream;