1. 切换自定义Theme
优点: 实现简单 ,无侵入,易于理解 ,兼容性能好
缺点: 不能实时生效,静态更新,要在setContentView之前切换theme ,
自定义属性 :
1 2 3 4 5
| <resources> <attr name="textColor" format="reference|color"/> <attr name="background" format="reference|color"/> </resources>
|
自定义样式:
1 2 3 4 5 6 7 8 9 10
| <style name="AppTheme_Light" > <item name="textColor">@android:color/black</ item> <item name="android:background">@android:color/white</ item> </style> <style name="AppTheme_Night"> <item name="textColor">@android:color/white</ item> <item name="android:background">@android:color/black</ item> </style>
|
应用:
1 2 3 4 5 6 7
| <TextView android :layout_width="wrap_content" android :layout_height="wrap_content" android :text="Hello World!" android :background="?attr/background" android :textColor="?attr/textColor"/>
|
1 2 3 4 5 6 7 8 9
| protected void skinChange() { TypedValue background = new TypedValue(); TypedValue textColor = new TypedValue(); Resources.Theme theme = getTheme(); theme.resolveAttribute(R.attr.background, background, true); theme.resolveAttribute(R.attr.textColor, textColor, true); mTextView .setTextColor(textColor.data); mTextView.setBackgroundResource(background.resourceId); }
|
2. 侵入式换肤框架
主要介绍一种侵入式方案的实现
优点: 使用简单,可以动态替换网络上下载的皮肤包
缺点: 侵入式 可能存在兼容性问题
2.1 LayoutInflater加载
首先是view的加载流程
- activity.java
1 2 3
| public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); }
|
- PhoneWindow.java
1 2 3 4 5 6 7 8
| @Override public void setContentView(int layoutResID) { ... mLayoutInflater.inflate(layoutResID, mContentParent); ... }
|
- LayoutInflater.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public View inflate(int resource, ViewGroup root, boolean attachToRoot) { ... XmlResourceParser parser = getContext().getResources().getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } } public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { ... View temp = createViewFromTag(name, attrs); ...
}
View createViewFromTag(String name, AttributeSet attrs) { ... View view = (mFactory == null) ? null : mFactory.onCreateView(name, mContext, attrs);
if (view == null) { if (-1 == name.indexOf('.')) { view = onCreateView(name, attrs); } else { view = createView(name, null, attrs); } }
if (DEBUG) System.out.println("Created view is: " + view); return view; ... }
|
我们重点看 createViewFromTag , 经过上面一系列步骤,我们看到了view的创建,首先会通过 mFactory ,
1
| public void setFactory(Factory factory)
|
通过 设置 factory,我们可以接管view的创建流程
2.2 加载外部资源皮肤包
皮肤包就是一个空的apk文件,只有 drawable,color 等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Resources skinResource = null; try { File file = new File(skinPath); if(file == null || !file.exists()){ return null; }
PackageManager mPm = mContext.getPackageManager(); PackageInfo mInfo =mPm.getPackageArchiveInfo(skinPath, PackageManager.GET_ACTIVITIES); mSkinPackageName = mInfo.packageName;
AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, skinPath);
Resources superRes = mContext.getResources(); skinResource = new Resources(assetManager,superRes.getDisplayMetrics(),superRes.getConfiguration()); } catch (Exception e) { e.printStackTrace(); }
|
代码托管: https://github.com/kongxs/PluginProject