Android 无需自定义View 解放 shape 解决方案 原理讲解

版权声明:本文为博主原创文章,转载请说明出处。 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;
    }

总结

  1. xml 在解析创建成 View 对象时,将通过 Factory/Factory2/ PhoneLayoutInflater 中的 createView 方法创建View
  2. 将系统的 Factory 创建修改为自己的,这个时候 所有View的创建过程都将经过这个类
  3. 模仿 系统Factory的写法,因为部分方法不是 public的
  4. 解析attr 也就是自定义属性,创建不同的 Drawable (或者替代对象)
  5. 对创建出来的View 赋值 Drawale 那么就可以解放繁琐的 shape.xml定义了

猜你喜欢

转载自blog.csdn.net/qq_30889373/article/details/83011637