|
[插件开发]
android模块开发中的styleable资源处理
[复制链接]
本帖最后由 常山赵子云 于 2018-8-28 10:42 编辑
以下内容仅针对Eclipse版本Android模块开发,如果使用Android Studio版模块开发方式,可以忽略以下全文。
---------------------------------------------------------------------------------------------------------------------------
1、先说说R文件:
对于原生开发人员而言,在开发过程中,代码中引用一个资源文件,最方便的方式是使用R文件进行引用,系统会自动将id对应到实际的资源。实际上,这个过程是开发工具(eclipse、android studio等)帮你做的,在编码过程中,开发工具将会在项目包名对应的路径下,自动生成一个临时的名为R的class文件,并将项目res目录下的资源根据规则自动生成对应的静态int型id,保存于该class中,开发者在开发过程中引用到资源时,通过R文件进行id引用即可,非常方便。当你的项目release时,实际上这个R文件就没用了,在编译过程中,R文件中的所有id都会被编译工具处理到一个叫做public的xml文件中(反编译一个apk的时候可以看到),所以,R文件只是一个临时文件,便于开发过程中的项目资源管理,它与release后的apk包中资源并没有直接关系。
2、APICloud模块开发中为什么不能引用R文件:
上面已经说到,R文件是与项目包名对应的,当你在开发完模块后,导出jar包时,如果你的代码中引用到了R文件,那么,该R文件的class路径是不是就已经定死为当时你做开发时所创建的android项目包名路径(例如:com.api.abc.R)?然而,模块与开发者是一对多的关系,同一个模块,会提供给N个开发者的项目用,并且APICloud开发者所创建的项目,他的app包名,肯定不可能与你的R文件所在包名相同(比如此时项目包名为:com.api.efg)。因此,当某项目用到该模块时,执行到使用了R文件的地方时,肯定因找不到com.api.abc.R,报类似can not find class xxx.xxx.R而导致app崩溃。
所以我们约定,在模块开发中,不允许直接使用R文件对资源进行引用,应该进行动态获取,并且提供了UZResourcesIDFinder工具类进行使用。该类的内部实现,实际上就是对android系统的Resources.getIdentifier(String name, String defType, String defPackage)函数的一个封装。
(细心的开发者应该也可以发现,像百度,腾讯这样一些大厂提供的SDK中,也是没有R文件引用的,他们通常的做法也是动态获取,或者放到asset中,并打包到SDK的jar里)
3、模块开发过程中的自定义属性(styleable)如何处理
很多开发者在开发UI类的模块时,通常都会用到一些开源的代码,而这些开源的代码实现中,通常都会有自定义属性,这些自定义属性对应到attr.xml文件中的字段名为styleable。对于styleable,在动态获取id上非常复杂,如果原生开发功底不足的开发者,不建议去操作。另外,styleable最终会被系统解析为对应的AttributeSet实例,并通过View的构造函数传给view。
下面提供的解决方式,可直接将其转换成代码类的成员属性,通过开放接口去定制,会更简单,也更符合模块开放接口的设计模式,同时效率也更高:
将开源的东西,凡是自定义View的自定义属性(即astyleable),直接在其构造函数中使用默认值即可,然后将attrs中定义的字段对应的开放set接口,因为通过xml定义的attrs之类的东西,最终系统framework都会将其转化为Java代码中View对象的set/get属性,如果你在代码中直接使用set/get接口,实际上更有利,对于不允许引用R文件的模块开发来说尤其便利。最简单的例子:
某自定义View构造函数有如下代码:
(CirclePageIndicator实际上是官方scrollPicture模块中用到的类,取自开源项目,可在github搜索CirclePageIndicator即可)
- public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- //设定默认值
- final Resources res = getResources();
- final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color);
- final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);
- final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation);
- final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color);
- final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width);
- final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius);
- final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered);
- final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap);
- //Retrieve styles attributes
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0);
- //获取xml属性,如果没有则使用默认值
- mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered);
- mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation);
- mPaintPageFill.setStyle(Style.FILL);
- mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor));
- mPaintStroke.setStyle(Style.STROKE);
- mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor));
- mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth));
- mPaintFill.setStyle(Style.FILL);
- mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor));
- mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius);
- mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap);
- //回收属性内存
- a.recycle();
- }
复制代码 我们可以看到,该构造函数中,系统通过AttributeSet将xml转换成属性类后传了进来,构造函数通过:设定默认值-》获取xml属性并赋给相应的变量,完成了对自定义View的各项属性的初始化,这个过程通过R文件进行资源获取,如果没有则使用默认值。
我们将其转换成代码,并直接使用默认值,同时将各项属性,定义get/set接口,允许灵活定义:
- public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- final Resources res = getResources();
- final int defaultPageColor = 0xFF00FF00;//取原有xml文件中定义的默认颜色
- final int defaultFillColor = 0xFF00FF00;//取原有xml文件中定义的默认颜色
- final int defaultOrientation = 0xFF00FF00;//取原有xml文件中定义的默认颜色
- final int defaultStrokeColor = 0xFF00FF00;//取原有xml文件中定义的默认颜色
- final float defaultStrokeWidth =3;//取原有xml文件中定义的默认值
- final float defaultRadius = 5.0;//取原有xml文件中定义的默认值
- final boolean defaultCentered = false;//取原有xml文件中定义的默认值
- final boolean defaultSnap = false;//取原有xml文件中定义的默认值
- mCentered = defaultCentered;
- mOrientation = defaultOrientation;
- mPaintPageFill.setStyle(Style.FILL);
- mPaintPageFill.setColor(defaultPageColor);
- mPaintStroke.setStyle(Style.STROKE);
- mPaintStroke.setColor(defaultStrokeColor);
- mPaintStroke.setStrokeWidth(a.getDimension(defaultStrokeWidth);
- mPaintFill.setStyle(Style.FILL);
- mPaintFill.setColor(defaultFillColor);
- mRadius = defaultRadius;
- mSnap = defaultSnap;
- }
- public void setPageColor(int color){
- }
- public void setFillColor(int color){
- }
- public void setOrientation(int orientation){
- }
- ......
复制代码 这样既直接避免了对R文件的引用,从可以灵活的从代码层面控制各个属性,方便开放api给JS调用,时刻定制该UI模块的样式等,同时效率也非常高,因为少了系统读取xml文件并解析成类的操作,少了动态获取资源ID的操作,所有对象都是直接在内存中new出来的,无需通过R文件等由系统去加载转换。
|
|