Android theme change skinning

Understanding setFactory
usually use more is LayoutInflater # inflate, and the layout .xml file parsing process will call LayoutInflater # rInflate, will then be created by invoking the View LayoutInflater # createViewFromTag. It is recommended "to meet LayoutInflater & Factory"
look LayoutInflate # createViewFormTag with the following:

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}

// Apply a theme wrapper, if allowed and one is specified.
if (!ignoreThemeAttr) {
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
if (themeResId != 0) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
}

if (name.equals(TAG_1995)) {
// Let's party like it's 1995!
return new BlinkLayout(context, attrs);
}

try {
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}

if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}

IF (View == null) {
Final Object lastContext mConstructorArgs = [0];
mConstructorArgs [0] = context;
the try {
IF ( '.' name.indexOf -1 == ()) {
// Create View Android native ( android.view packet following View)
View = onCreateView (parent, name, attrs);
} the else {
// create a custom package or rely View View (XML is declared full path)
View the createView = (name, null , attrs);
}
} {the finally
mConstructorArgs [0] = lastContext;
}
}

return view;
} catch (InflateException e) {
throw e;

} catch (ClassNotFoundException e) {
final InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name, e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;

} catch (Exception e) {
final InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name, e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
}
}
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
38
39
40
41 is
42 is
43 is
44 is
45
46 is
47
48
49
50
51 is
52 is
53 is
54 is
55
56 is
57 is
58
59
60
61 is
62 is
63 is
64
65
66
67
68
From the above it can be seen the source of creation View, will first find and Factory2 # onCreateView Factory # onCreateView to create, and then take the default creation process. So, we can create Factory2 or Factory custom here to intervene in the creation View.

For example: in trying Button can be converted into TextView

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
LayoutInflater.from(this).setFactory(new LayoutInflater.Factory() {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
for (int i = 0; i < attrs.getAttributeCount(); i ++){
String attrName = attrs.getAttributeName(i);
String attrValue = attrs.getAttributeValue(i);
Log.i(TAG, String.format("name = %s, attrName = %s, attrValue= %s", name, attrName, attrValue));
}
TextView textView = null;
if (name.equals("Button")){
textView = new TextView(context, attrs);
}

the textView return;
}
});
super.onCreate (savedInstanceState);
; the setContentView (R.layout.activity_theme_change)
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
. 17
18 is
. 19
20 is
21 is
the start Activity After allowing , you will see output:

name = Button, attrName = id, attrValue= @2131230758
name = Button, attrName = background, attrValue= @2131034152
name = Button, attrName = layout_width, attrValue= -2
name = Button, attrName = layout_height, attrValue= -2
name = Button, attrName = id, attrValue= @2131230757
name = Button, attrName = background, attrValue= @2131034150
name = Button, attrName = layout_width, attrValue= -2
name = Button, attrName = layout_height, attrValue= -2
1
2
3
4
5
6
7
8
获取包外Resource
We usually get resources under the res directory by Context # getSource (), Context # getAssets () ( want to Context # getSource (). GetAssets ( )) access to resources in the asset catalog. So, to get outside of the application resource file Resource, you need to create a Resource instance file information outside of the application, and then get the resource information in the file by this example.
For example, Resource newly created instance is mResource, it can be used mResource.getColor (colorId), to obtain the corresponding color colorID examples.
The question then is divided into two steps:

How to Create Custom Resource instances
learned by the Resource constructor Resources (AssetManager assets, DisplayMetrics metrics, Configuration config), you need to get the apk file Resource objects external resources app, you first need to create the corresponding AssetManager object.

public final class AssetManager implements AutoCloseable {
/**
* Create a new AssetManager containing only the basic system assets.
* Applications will not generally use this method, instead retrieving the
* appropriate asset manager with {@link Resources#getAssets}. Not for
* use by applications.
* {@hide}
*/
public AssetManager() {
synchronized (this) {
if (DEBUG_REFS) {
mNumRefs = 0;
incRefsLocked(this.hashCode());
}
init(false);
if (localLOGV) Log.v(TAG, "New asset manager: " + this);
ensureSystemAssets();
}
}
/**
* Add an additional set of assets to the asset manager. This can be
* either a directory or ZIP file. Not for use by applications. Returns
* the cookie of the added asset, or 0 on failure.
* {@hide}
*/
//添加额外的asset路径
public final int addAssetPath(String path) {
synchronized (this) {
int res = addAssetPathNative(path);
if (mStringBlocks != null) {
makeStringBlocks(mStringBlocks);
}
return res;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
. 19
20 is
21 is
22 is
23 is
24
25
26 is
27
28
29
30
31 is
32
33 is
34 is
35
can be created by reflection corresponding AssertManager, thereby creating the Resource instances corresponding to the code is as follows:

private final static Resources loadTheme(String skinPackageName, Context context){
String skinPackagePath = Environment.getExternalStorageDirectory() + "/" + skinPackageName;
File file = new File(skinPackagePath);
Resources skinResource = null;
if (!file.exists()) {
return skinResource;
}
try {
//创建AssetManager实例
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, skinPackagePath);
//构建皮肤资源Resource实例
Resources superRes = context.getResources(http://www.my516.com);
skinResource = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());

} The catch (Exception E) {
skinResource = null;
}
return skinResource;
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
. 17
18 is
. 19
20 is
21 is
how to know the current property values id in the host Resource in
the Resource the source code can be found

Resources {class public
/ **
* by a resource name, return type, and package names id identifying a resource.
* @Param Description Name The name of the resource
* @param defType type the name of the resource
* @param defPackage package name
*
* @return returns a resource id, 0 identifies the resource was not found
* /
public int getIdentifier (String name, String DEFTYPE, String defPackage ) {
IF (name == null) {
the throw a NullPointerException new new ( "null name IS");
}
the try {
return the Integer.parseInt (name);
} the catch (Exception E) {
// the Ignore
}
return mAssets.getResourceIdentifier (name, deftype, defPackage);
}
}

. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
. 17
18 is
. 19
20 is
21 is
22 is
that is an arbitrary apk file only needs to know the package name (as specified in the manifest.xml package name for finding Java classes and resources), type the name of the resource, resource description name.
For example: There is a defType A package as "color", name of color_red_1 properties, color can be acquired in the name of the resource bundle B by Resource # getIdentifier.

//将skina重View的背景色设置为com.example.skinb中所对应的颜色
if (attrValue.startsWith("@") && attrName.contains("background")){
int resId = Integer.parseInt(attrValue.substring(1));
int originColor = mContext.getResources().getColor(resId);
if (mResource == null){
return originColor;
}
String resName = mContext.getResources().getResourceEntryName(resId);
int skinRealResId = mResource.getIdentifier(resName, "color", "com.example.skinb");
int skinColor = 0;
try{
skinColor = mResource.getColor(skinRealResId);
}catch (Exception e){
Log.e(TAG, "", e);
skinColor = originColor;
}
view.setBackgroundColor(skinColor);
}
--------------------- 

Guess you like

Origin www.cnblogs.com/ly570/p/11014116.html