Android Shape tools Duck

7550631-f7a5850aedbb6cc4.jpg
photo

Due to their problems in development, all to get an Android Shape tool Duck , can help developers implement the Shape effect directly on any control xml, without the need to create additional xml file, and no invasive.

Ado, directly see the effect:

7550631-af3412c582067d21
duck

]

Project Address: Duck


Original intention

The origin of this library, because the company maintained a four-year project.

4 years of project experience, product design changed version did not know how much to produce and piling up a lot during the shape.xmlfiles that are indexed often because the problem can not clean up.

At the same time, the same shape.xmlfile, because there is no standard design issues, such as the changes in different pages a little color, line width or chamfer can not be reused, it must create a new file accordingly.

Finally, the accumulation of a large number of files, while developers take the time to find shape.xml, not as their own to create a new convenience, this vicious cycle, only GG.

I want to say, Android design Shapeoriginal intention is good: a APP, unified design specifications, it should reuse Shape.

But this is for the domestic ecosystem that do not apply.

First of all, the same screen size, much larger than the Chinese ability to carry information in English, which led to most of the foreign APP simple and refreshing interface design, it is very complex country, while domestic Internet updates quickly, the interface is a short life cycle, movement of persons, difficult to achieve unified interface.

All, Android's Shape is not suitable for domestic ecology.

When developing super envious opposite IOS developers can be chamfered fancy, add frames and other Sao operate directly on the control, can not figure out why Android IOS can not learn at this point. Hey, Android and IOS fate of the dispute, saying that more tears are.

Based on the above for various reasons, so there would like to develop this library.


这个库只实现最常用的 Shape 功能,selector 及 layout-list 并未实现,因为有两点考虑:

  1. shape 使用场景更多,并且更频繁,其他两种只在少数特定场景中使用。
  2. selector 及 layout-list 需要更多精细的代码控制,如全部挤在 xml 中一个控件上,会非常臃肿,难以维护。


原理

在考虑用什么技术实现时,考虑这几点:

  1. 任何控件都能有效,即使是自定义控件。
  2. 不能有侵入性,即使更换或废弃本库,也能保证稳定性。

最开始,第一个想到的是 LayoutInflater.Factory ,xml 控件解析成 View时,必须经过它,也是换肤的解决方案,但这样得一个个替换成自己的,非常麻烦。

有没有更好的解决方案呢?

得益于 AspectJ 的 AOP(面向切面编程)能力,我们可以在编译时期,直接在 View 及其子类的构造方法中插入相关代码,解析xml 中自定义的属性,最后设置到控件上。

    @Pointcut("execution(android.view.View+.new(..))")
    public void callViewConstructor() {
    }

    @After("callViewConstructor()")
    public void inject(JoinPoint joinPoint) throws Throwable {

        Signature signature = joinPoint.getSignature();
        Object target = joinPoint.getTarget();
        Object[] args = joinPoint.getArgs();

        int length = args.length;
        if (!(target instanceof View) || length < 2 || target.hashCode() == lastHash || !(args[0] instanceof Context) || !(args[1] instanceof AttributeSet)) {
            return;
        }
        lastHash = target.hashCode();

        Context context = (Context) args[0];
        AttributeSet attrs = (AttributeSet) args[1];

        int count = attrs.getAttributeCount();

        for (int i = 0; i < count; i++) {
            Log.i(TAG, attrs.getAttributeName(i) + " = " + attrs.getAttributeValue(i));
        }

        Log.i(TAG, "inject =====> " + signature.toString());
        DuckFactor.getFactor().inject((View) target, context, attrs);
    }

AOP 相关内容,可以查看AOP 系列 包含:

1.OOP 与 AOP

2.Java 注解处理器

3.Aspect

4.Android中使用 Javassist


由于 AspectJ 能遍历项目中所有依赖包,因此,无论是 support 库,还是第三方库都能得到很好支持。

但是 AOP 也存在一定问题,我们的 apk 中是不会存在系统原生 Android SDK 的,例如 TextView 这个系统控件,在编译时是不会打包到 apk 中,因此,AOP 技术对这种原生控件无能为力。

幸好,我们绝大部分项目为了兼容性,一般都会直接依赖官方的兼容库,即 support 相关的库。

在 support· 库中,会将一些原生控件,直接替换成 support 相关控件。相关代码如下:

android/support/v7/app/AppCompatViewInflater

switch (name) {
            case "TextView":
                view = createTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageView":
                view = createImageView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "Button":
                view = createButton(context, attrs);
                verifyNotNull(view, name);
                break;
            case "EditText":
                view = createEditText(context, attrs);
                verifyNotNull(view, name);
                break;
            ......
        }

而对于这些控件,我们的 AOP 都能够生效了。

Support in the library, not replace several commonly ViewGroup subclasses, such as LinearLayout, RelativeLayout, FrameLayoutand the like,

Therefore, we would follow alternative embodiment of the support, direct LayoutInflater.Factory.onCreateViewinjection codes corresponding replacement process.

    
    @Pointcut("execution(* *..LayoutInflater.Factory+.onCreateView(..))")
    public void callLayoutInflater() {
    }

    @Around("callLayoutInflater()")
    public Object replaceView(ProceedingJoinPoint joinPoint) throws Throwable {

        ....
            
        switch (name) {
            case "RelativeLayout":
                return new DuckRelativeLayout(context, attrs);
            case "LinearLayout":
                return new DuckLinearLayout(context, attrs);
            case "FrameLayout":
                return new DuckFrameLayout(context, attrs);
            case "TableLayout":
                return new DuckTableLayout(context, attrs);
            case "ScrollView":
                return new DuckScrollView(context, attrs);
            default:
                break;
        }

        return result;
    }

The code for this library is in fact very small, I've just realized Shape this function.

    private static Injector mInjector;

    public static void setFactor(Injector injector) {
        mInjector = injector;
    }

    public static Injector getFactor() {
        if (mInjector == null) {
            mInjector = new ShapeInjector();
        }
        return mInjector;
    }

Scalability Duck reserved here, and if that is not enough, you can achieve more powerful Injector themselves.

AOP's ability is far more than that, there are many things you can do suggest that you can use your imagination, for more expansion.


optimization

The core principle has been to get, there are two things need to be optimized:

  1. When used in xml, no notification is not convenient, it can be resolved by Live Template.

    7550631-df00982682b09d58
    Show
  2. Can not preview, AOP work at compile time, not all real-time preview to see someone else's library is replaced by a custom View to see the effect, I feel this implementation is not perfect, all to give up, think about whether the follow-up by the plug-AS preview.

Project Address: Duck


Episode

The library appears quite bumpy.

In August, about 18 years, began writing this library was to achieve the core functionality of the code xml question have already had the solution.

But there have been half written the same library of functions,

BackgroundLibrary

Carefully read the program code comparison found to achieve not the same principle, xml suggesting the same solution to the problem by replacing the preview resolved.

The amount of ~

Ok! I did not write down on the moment of power, coupled with the company to catch up period, the code did not throw in that move.

Until recently idle, and suddenly thinking about this matter, or get that done right, it is not put forward failed to materialize.

Guess you like

Origin blog.csdn.net/weixin_33688840/article/details/90973337