版权声明:本文为博主原创文章,转载请说明出处。 https://blog.csdn.net/qq_30889373/article/details/83011637
预备知识点
https://blog.csdn.net/qq_30889373/article/details/82823013
讲解
这个库还没有正式发布,后期会发布
github地址
本期讲解demo地址
https://github.com/2745329043/WidgetSkin
开始
阅读上面预备知识点的文章,我们大概明白了一个事情,我们的View创建是通过
LayoutInflater.class
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
...
try {
// 如果有 Factory 优先使用这个方式创建
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;
try {
if (-1 == name.indexOf('.')) {
// PhoneLayoutInflater Activity的情况下
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
} catch (InflateException e) {
....
}
那么思路就来了,我们能不能使用自定义 Factory来使创建View
这样我们就可以使用做一些自定义的操作了,比如shape
下面我们来自己创建一下
WidgetSkinFactory.java
public class WidgetSkinFactory implements LayoutInflater.Factory2 {
// Factory
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return onCreateView(null, name, context, attrs);
}
// Factory2
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// 2. 通过原方式创建
if (mFactory2 != null) {
delegateView = mFactory2.onCreateView(name, context, attrs);
if (delegateView == null) {
delegateView = mFactory2.onCreateView(null, name, context, attrs);
}
} else if (mFactory != null) {
delegateView = mFactory.onCreateView(name, context, attrs);
}
TypedArray backgroundTypes = context.obtainStyledAttributes(attrs, R.styleable.background);
TypedArray selectorTypes = context.obtainStyledAttributes(attrs, R.styleable.background_selector);
TypedArray textSelectorTypes = context.obtainStyledAttributes(attrs, R.styleable.text_selector);
try {
// 如果资源没有自定义情况,不进行处理
if (backgroundTypes.length() == 0 && selectorTypes.length() == 0) {
return delegateView;
} else {
// 3. 这里代码是系统源码里抄出来的,通过 名称 创建 view
if (delegateView == null) {
delegateView = createViewFromTag(context, name, attrs);
}
// 4. 检测自定义属性
if (delegateView != null) {
// 先挑这个最简单的来说
CompatBackgroundCreator.create(delegateView, backgroundTypes);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
backgroundTypes.recycle();
selectorTypes.recycle();
}
return delegateView;
}
}
修改系统默认的 Factory为自己定义的
WidgetSkin.java
/**
* 注入皮肤
* @param activity
*/
public static void inject(Activity activity) {
// 系统默认的 factory
LayoutInflater inflater = activity.getLayoutInflater();
WidgetSkinFactory skinFactory = new WidgetSkinFactory(inflater.getFactory(),inflater.getFactory2());
// 通过反射修改,因为默认系统只允许设置 Factory 一次
Field field ;
try {
field = LayoutInflater.class.getDeclaredField("mFactorySet");
field.setAccessible(true);
field.setBoolean(inflater, false);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
inflater.setFactory2(skinFactory);
}
看到上面的代码了吧,这个时候 你只需要在
public class MainActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 调整为自己的Factory
WidgetSkin.inject(this);
setContentView(R.layout.activity_main);
}
}
那么现在所有的创建 View过程 都将经过 WidgetSkinFactory$$onCreateView
开始解析,并进行Drawable创建
CompatBackgroundCreator.java
public static boolean create(View view, TypedArray backgroundTypes) throws XmlPullParserException {
// 需要进行背景处理
if (CheckStyleCreatorHelper.validBackground(backgroundTypes)) {
// 创建drawable
GradientDrawable normalDrawable = createDrawable(backgroundTypes);
// 为自己的View 设置
view.setBackground(normalDrawable);
}
}
/**
* 创建Drawable
*/
public static GradientDrawable createDrawable(TypedArray typedArray) throws XmlPullParserException {
// corners
float[] cornerRadius = new float[8];
// 渐变
float[] gradientCenters = new float[2];
Arrays.fill(gradientCenters, 0.5f);
// 颜色
int[] gradientColors = new int[3];
// padding:用来定义内部边距
float[] paddings = new float[4];
// size:是用来定义图形的大小的。
int[] sizes = new int[2];
Arrays.fill(sizes, -1);
// 画笔相关
float strokeWidth = -1f;
float strokeDashWidth = 0f;
int strokeColor = 1;
float strokeGap = 0f;
// 创建 drawable
GradientDrawable normalDrawable = new GradientDrawable();
for (int i = 0; i < typedArray.getIndexCount(); i++) {
int attr = typedArray.getIndex(i);
if (attr == -1) {
continue;
}
int typeIndex = typedArray.getIndex(i);
/**
* 基本操作
*/
if (attr == R.styleable.background_shape) {
int shape = typedArray.getInt(typeIndex, 0);
normalDrawable.setShape(shape);
}else if (attr == R.styleable.background_solid_color) {
int typedArrayColor = typedArray.getColor(typeIndex, 0);
normalDrawable.setColor(typedArrayColor);
}else if (attr == R.styleable.background_corners_radius) {
// 角标
float radius = typedArray.getDimension(typeIndex, 0f);
Arrays.fill(cornerRadius, radius);
}else if (attr == R.styleable.background_corners_topLeftRadius) {
cornerRadius[0] = typedArray.getDimension(typeIndex, 0f);
cornerRadius[1] = typedArray.getDimension(typeIndex, 0f);
}else if (attr == R.styleable.background_corners_topRightRadius) {
cornerRadius[2] = typedArray.getDimension(typeIndex, 0f);
cornerRadius[3] = typedArray.getDimension(typeIndex, 0f);
}else if (attr == R.styleable.background_corners_bottomRightRadius) {
cornerRadius[4] = typedArray.getDimension(typeIndex, 0f);
cornerRadius[5] = typedArray.getDimension(typeIndex, 0f);
}else if (attr == R.styleable.background_corners_bottomLeftRadius) {
cornerRadius[6] = typedArray.getDimension(typeIndex, 0f);
cornerRadius[7] = typedArray.getDimension(typeIndex, 0f);
/**
* 渐变相关
*/
}else if (attr == R.styleable.background_gradient_type) {
int type = typedArray.getInt(typeIndex, GradientDrawable.LINEAR_GRADIENT);
normalDrawable.setGradientType(type);
}else if (attr == R.styleable.background_gradient_angle) {
int angle = typedArray.getInteger(typeIndex, 0);
GradientDrawable.Orientation orientation = getOrientation(angle, typedArray);
normalDrawable.setOrientation(orientation);
}else if (attr == R.styleable.background_gradient_centerX) {
gradientCenters[0] = typedArray.getFloat(typeIndex, 0.5f);
}else if (attr == R.styleable.background_gradient_centerY) {
gradientCenters[1] = typedArray.getFloat(typeIndex, 0.5f);
}else if (attr == R.styleable.background_gradient_startColor) {
gradientColors[0] = typedArray.getColor(typeIndex, 0);
}else if (attr == R.styleable.background_gradient_centerColor) {
gradientColors[1] = typedArray.getColor(typeIndex, 0);
}else if (attr == R.styleable.background_gradient_endColor) {
gradientColors[2] = typedArray.getColor(typeIndex, 0);
}else if (attr == R.styleable.background_gradient_gradientRadius) {
float raidus = typedArray.getDimension(typeIndex, 0f);
normalDrawable.setGradientRadius(raidus);
}else if (attr == R.styleable.background_gradient_useLevel) {
boolean useLevel = typedArray.getBoolean(typeIndex, false);
normalDrawable.setUseLevel(useLevel);
// size
}else if (attr == R.styleable.background_size_width) {
sizes[0] = (int) typedArray.getDimension(typeIndex, -1f);
}else if (attr == R.styleable.background_size_height) {
sizes[1] = (int) typedArray.getDimension(typeIndex, -1f);
// padding
}else if (attr == R.styleable.background_padding) {
float padding = typedArray.getDimension(typeIndex, 0f);
Arrays.fill(paddings, padding);
}else if (attr == R.styleable.background_padding_left) {
paddings[0] = typedArray.getDimension(typeIndex, 0f);
}else if (attr == R.styleable.background_padding_top) {
paddings[1] = typedArray.getDimension(typeIndex, 0f);
}else if (attr == R.styleable.background_padding_right) {
paddings[2] = typedArray.getDimension(typeIndex, 0f);
}else if (attr == R.styleable.background_padding_bottom) {
paddings[3] = typedArray.getDimension(typeIndex, 0f);
// stroke
}else if (attr == R.styleable.background_stroke_width) {
strokeWidth = typedArray.getDimension(typeIndex, 0f);
}else if (attr == R.styleable.background_stroke_color) {
strokeColor = typedArray.getColor(typeIndex, 0);
}else if (attr == R.styleable.background_stroke_dashWidth) {
strokeDashWidth = typedArray.getDimension(typeIndex, 0f);
}else if (attr == R.styleable.background_stroke_dashGap) {
strokeGap = typedArray.getDimension(typeIndex, 0f);
}
}
// 四个方向的 Radius
if (isValidArray(cornerRadius)) {
normalDrawable.setCornerRadii(cornerRadius) ;
}
// 渐变的中心位置
invalidCenter(gradientCenters, typedArray);
normalDrawable.setGradientCenter(gradientCenters[0], gradientCenters[1]);
// 渐变色
if (isValidArray(gradientColors)) {
int[] colors = clearInvalid(gradientColors);
normalDrawable.setColors(colors);
}
// size
normalDrawable.setSize(sizes[0], sizes[1]);
// padding
if (isValidArray(paddings)) {
changePadding(normalDrawable, paddings);
}
// stroke
if (typedArray.hasValue(R.styleable.background_stroke_width) && typedArray.hasValue(R.styleable.background_stroke_color)) {
normalDrawable.setStroke((int) strokeWidth, strokeColor, strokeDashWidth, strokeGap);
}
// 创建完毕
return normalDrawable;
}
总结
- xml 在解析创建成 View 对象时,将通过 Factory/Factory2/ PhoneLayoutInflater 中的 createView 方法创建View
- 将系统的 Factory 创建修改为自己的,这个时候 所有View的创建过程都将经过这个类
- 模仿 系统Factory的写法,因为部分方法不是 public的
- 解析attr 也就是自定义属性,创建不同的 Drawable (或者替代对象)
- 对创建出来的View 赋值 Drawale 那么就可以解放繁琐的 shape.xml定义了