内容简介:由于自己在开发中遇到的问题,所有弄了一个 Android Shape 的工具废话不多说,直接看效果:项目地址:
由于自己在开发中遇到的问题,所有弄了一个 Android Shape 的工具 Duck ,能帮助开发者直接在 xml 的任意控件上实现 Shape 效果,无需创建额外的xml文件,并且没有任何侵入性。
废话不多说,直接看效果:
项目地址: Duck
初衷
这个库的由来,是因为公司一个维护了 4 年的项目。
经历 4 年的项目,产品设计不知道改了多少版,期间产生并堆砌大量 shape.xml
文件,这些文件因为索引的问题往往还无法清理。
同时,同一个 shape.xml
文件,因为设计存在不规范的问题,在不同页面改动了一点颜色、倒角或线宽等,就无法复用,必须据此创建新的文件。
最后,大量的文件堆积,开发人员开发时,花时间找 shape.xml
,还不如自己创建新的方便,这样恶性循环, 只能 GG。
我想说,Android 设计 Shape
的初衷是好的: 一个 APP,统一的设计规范,就应该复用 Shape
。
但这种情况对于国内的生态来说并不适用。
首先,相同屏幕尺寸,中文承载信息的能力远大于英文,这就导致国外大部分 APP 界面设计简洁清爽,国内就显得非常复杂,同时国内互联网更新速度很快,界面是生命周期短,人员流动,很难做到界面统一。
所有,Android 的 Shape 并不适合国内生态。
开发时,超级羡慕对面 IOS 开发们可以直接在控件上进行花式倒角、加线框等骚操作,想不通为啥 Android 不能在这一点上借鉴IOS。哎,Android 与 IOS 的宿命之争,说多了都是泪。
基于上面种种原因,所以出现了想开发这个库。
这个库只实现最常用的 Shape 功能,selector 及 layout-list 并未实现,因为有两点考虑:
- shape 使用场景更多,并且更频繁,其他两种只在少数特定场景中使用。
- selector 及 layout-list 需要更多精细的代码控制,如全部挤在 xml 中一个控件上,会非常臃肿,难以维护。
原理
在考虑用什么技术实现时,考虑这几点:
- 任何控件都能有效,即使是自定义控件。
- 不能有侵入性,即使更换或废弃本库,也能保证稳定性。
最开始,第一个想到的是 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); } 复制代码
由于 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 库中,没有替换掉 ViewGroup 的几个常用子类,如 LinearLayout
、 RelativeLayout
、 FrameLayout
等,
所以,我们我们仿照 support 的替换方式,直接在 LayoutInflater.Factory.onCreateView
方法中注入相应的替换代码。
@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; } 复制代码
这个库的代码其实很少,我这里也只是实现了 Shape 这一个功能。
private static Injector mInjector; public static void setFactor(Injector injector) { mInjector = injector; } public static Injector getFactor() { if (mInjector == null) { mInjector = new ShapeInjector(); } return mInjector; } 复制代码
这里保留的 Duck 的扩展性,如果觉得不够,可以自行实现功能更强大的 Injector。
AOP 的能力远不止如此,还有很多事情可以做,建议大家可以发挥想象,进行更多的扩展。
优化
核心原理已经搞定,还有两点需要优化:
-
在 xml 中使用时,没有提示不方便,这一点可以通过Live Template 来解决。
-
无法预览,AOP 在编译时工作,所有无法实时预览,看到别人的库是替换成自定义 View 来查看效果,感觉这种实现方式不够完美,所有就放弃了,后续想着能否通过 AS 插件实现预览。
项目地址: Duck
小插曲
这个库的出现挺坎坷的。
在 18 年 8 月份左右,就开始写了这个库,当时核心功能的实现,xml 的代码提示问题都已经想好了解决方案。
但写到一半出现了功能相同的库,
仔细看了项目代码,对比发现实现原理不一样,xml 提示解决方案相同,预览问题通过替换解决。
额~
好吧!就瞬间没写下去的动力了,再加上公司赶工期,代码就扔在那没动了。
直到最近空闲了,突然想着不管这样,还是弄完吧,搞个烂尾实在是不好。
以上所述就是小编给大家介绍的《Android Shape工具 Duck》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- ENVI扩展工具:RandomForest分类工具
- 15大安全工具和下载黑客工具
- 版本管理工具及 Ruby 工具链环境
- [译]Go性能分析工具工具和手段
- 工具 | 一个在线生成 nginx 配置文件的开源工具
- 【工具分享】PxCook像素大厨——很好用的标记工具
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。