帖子
帖子
用户
博客
课程
显示全部楼层
20
帖子
1
勋章
7583
Y币

[插件开发] android模块开发中的styleable资源处理

[复制链接]
发表于 2016-11-3 11:41:14
本帖最后由 常山赵子云 于 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即可)
  1. public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {
  2.         super(context, attrs, defStyle);
  3.         //设定默认值
  4.         final Resources res = getResources();
  5.         final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color);
  6.         final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);
  7.         final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation);
  8.         final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color);
  9.         final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width);
  10.         final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius);
  11.         final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered);
  12.         final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap);

  13.         //Retrieve styles attributes
  14.         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0);
  15.         //获取xml属性,如果没有则使用默认值
  16.         mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered);
  17.         mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation);
  18.         mPaintPageFill.setStyle(Style.FILL);
  19.         mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor));
  20.         mPaintStroke.setStyle(Style.STROKE);
  21.         mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor));
  22.         mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth));
  23.         mPaintFill.setStyle(Style.FILL);
  24.         mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor));
  25.         mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius);
  26.         mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap);
  27.         //回收属性内存
  28.         a.recycle();
  29.     }
复制代码
我们可以看到,该构造函数中,系统通过AttributeSet将xml转换成属性类后传了进来,构造函数通过:设定默认值-》获取xml属性并赋给相应的变量,完成了对自定义View的各项属性的初始化,这个过程通过R文件进行资源获取,如果没有则使用默认值。

我们将其转换成代码,并直接使用默认值,同时将各项属性,定义get/set接口,允许灵活定义:
  1. public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {
  2. super(context, attrs, defStyle);

  3. final Resources res = getResources();
  4. final int defaultPageColor = 0xFF00FF00;//取原有xml文件中定义的默认颜色
  5. final int defaultFillColor = 0xFF00FF00;//取原有xml文件中定义的默认颜色
  6. final int defaultOrientation = 0xFF00FF00;//取原有xml文件中定义的默认颜色
  7. final int defaultStrokeColor = 0xFF00FF00;//取原有xml文件中定义的默认颜色
  8. final float defaultStrokeWidth =3;//取原有xml文件中定义的默认值
  9. final float defaultRadius = 5.0;//取原有xml文件中定义的默认值
  10. final boolean defaultCentered = false;//取原有xml文件中定义的默认值
  11. final boolean defaultSnap = false;//取原有xml文件中定义的默认值

  12. mCentered = defaultCentered;
  13. mOrientation = defaultOrientation;
  14. mPaintPageFill.setStyle(Style.FILL);
  15. mPaintPageFill.setColor(defaultPageColor);
  16. mPaintStroke.setStyle(Style.STROKE);
  17. mPaintStroke.setColor(defaultStrokeColor);
  18. mPaintStroke.setStrokeWidth(a.getDimension(defaultStrokeWidth);
  19. mPaintFill.setStyle(Style.FILL);
  20. mPaintFill.setColor(defaultFillColor);
  21. mRadius = defaultRadius;
  22. mSnap = defaultSnap;
  23. }

  24. public void setPageColor(int color){

  25. }

  26. public void setFillColor(int color){

  27. }

  28. public void setOrientation(int orientation){

  29. }

  30. ......
复制代码
这样既直接避免了对R文件的引用,从可以灵活的从代码层面控制各个属性,方便开放api给JS调用,时刻定制该UI模块的样式等,同时效率也非常高,因为少了系统读取xml文件并解析成类的操作,少了动态获取资源ID的操作,所有对象都是直接在内存中new出来的,无需通过R文件等由系统去加载转换。


1
帖子
0
勋章
3993
Y币
MARK            
0
帖子
0
勋章
23万+
Y币
麻烦您把源码发一下   急用
您需要登录后才可以回帖 登录

本版积分规则