今天小程序平台项目中发现Android上自定义的ReactNative的Text组件样式显示不正确,具体表现如下图所示:
可以看到图中组件显示主要存在两个问题:
1、左右黑边;2、左右锯齿;
好嘛,这里自定义的组件是继承RN原有组件生成的,于是果断把同样的样式代码放到RN自己的Text组件里写个demo一看,竟然也有这两个问题。再换View组件也试一下,好吧,表现一样,demo代码如下:
render() {
return (
大幅度反对
);
}
显示结果如下:
好吧,看来rn自己没有处理好这一块儿的内容啊……只好自己解决了,emmm~~
经多次调整样式代码后发现,左右黑边原因是borderColor与borderLeftColor和borderRightColor颜色值冲突造成的,这里的表现是同时设置borderColor和四边框的颜色值后,左右两边的颜色是borderColor的颜色值、上下两边是单独设置的色值。
好吧,作为一个平台级项目,我们是没法控制对方是如何来写js代码的,因此也没法限制borderColor和四边框色值不同时存在。如此就只能通过原生端代码来控制了。
商量后决定,当两者同时存在时,就以四边框色值为准。
先来看RN自己是如何实现borderColor相关内容的吧。
Text组件对应的Android上的ViewManager是ReactTextViewManager,在ReactTextViewManager的父类ReactTextAnchorViewManager的源码里面找到了设置borderColor属性的代码,内容如下:
@ReactPropGroup(
names = {
"borderColor",
"borderLeftColor",
"borderRightColor",
"borderTopColor",
"borderBottomColor"
},
customType = "Color"
)
public void setBorderColor(ReactTextView view, int index, Integer color) {
float rgbComponent =
color == null ? YogaConstants.UNDEFINED : (float) ((int) color & 0x00FFFFFF);
float alphaComponent = color == null ? YogaConstants.UNDEFINED : (float) ((int) color >>> 24);
view.setBorderColor(SPACING_TYPES[index], rgbComponent, alphaComponent);
代码意思仔细看看就明白了,不做过多解释,其中SPACING_TYPES代码如下:
private static final int[] SPACING_TYPES = {
Spacing.ALL, Spacing.LEFT, Spacing.RIGHT, Spacing.TOP, Spacing.BOTTOM,
};}
点setBorderColor函数,一路往上寻找,最终在ReactViewBackgroundDrawable类里找到了setBorderColor的具体实现:
public void setBorderColor(int position, float rgb, float alpha) {
this.setBorderRGB(position, rgb);
this.setBorderAlpha(position, alpha);
}
private void setBorderRGB(int position, float rgb) {
// set RGB component
if (mBorderRGB == null) {
mBorderRGB = new Spacing(DEFAULT_BORDER_RGB);
}
if (!FloatUtil.floatsEqual(mBorderRGB.getRaw(position), rgb)) {
mBorderRGB.set(position, rgb);
invalidateSelf();
}
}
private void setBorderAlpha(int position, float alpha) {
// set Alpha component
if (mBorderAlpha == null) {
mBorderAlpha = new Spacing(DEFAULT_BORDER_ALPHA);
}
if (!FloatUtil.floatsEqual(mBorderAlpha.getRaw(position), alpha)) {
mBorderAlpha.set(position, alpha);
invalidateSelf();
}
}
打断点调试,发现这部分代码所有内容都会执行,其中invalidateSelf是其父类Drawable里实现的函数:
/**
* Use the current {@link Callback} implementation to have this Drawable
* redrawn. Does nothing if there is no Callback attached to the
* Drawable.
*
* @see Callback#invalidateDrawable
* @see #getCallback()
* @see #setCallback(android.graphics.drawable.Drawable.Callback)
*/
public void invalidateSelf() {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
好吧,看完这一路代码,明白设置bordercolor的过程应该是每次设置完透明度和色值后,就刷新下重新绘制。
仔细思考后,决定将整体的bordercolor属性拆分为上下左右四边,控制四边的绘制。
属性是并发调用的,顺序没法控制,就先添加后等view加载完成后,再手动控制。view加载完后执行onAfterUpdateTransaction。涉及到并发访问用ConcurrentHashMap存储数据。
/**************************修复同时bordercolor和borderRightColor、borderLeftColor,后两者不生效的问题***********************/
protected static final int[] SPACING_TYPES = {
Spacing.ALL,
Spacing.LEFT,
Spacing.RIGHT,
Spacing.TOP,
Spacing.BOTTOM,
Spacing.START,
Spacing.END,
};
@ReactPropGroup(
names = {
ViewProps.BORDER_COLOR,
ViewProps.BORDER_LEFT_COLOR,
ViewProps.BORDER_RIGHT_COLOR,
ViewProps.BORDER_TOP_COLOR,
ViewProps.BORDER_BOTTOM_COLOR,
ViewProps.BORDER_START_COLOR,
ViewProps.BORDER_END_COLOR
},
customType = "Color"
)
public void setBorderColor(ReactViewGroup view, int index, Integer color) {
//将所有的bordercolor设置进color,因为属性调用是并行的,无法在这里控制渲染顺序
SBaseViewTag sBaseViewTag = (SBaseViewTag) view.getTag();
if (sBaseViewTag != null) {
sBaseViewTag.getBorderColors().put(index, color);
}
}
@Override
protected void onAfterUpdateTransaction(T view) {
super.onAfterUpdateTransaction(view);
//属性已经设置完成,在这里控制bordercolor的显示
//如果即有BORDER_COLOR又有上下左右四边的颜色,则以四边颜色为准
//下面的0~6对应setBorderColor的属性index
SBaseViewTag sBaseViewTag = (SBaseViewTag) view.getTag();
if (sBaseViewTag != null) {
//真实去设置的颜色集合
Map realBorderColors = new HashMap<>();
Map borderColors = sBaseViewTag.getBorderColors();
for (Map.Entry entry : borderColors.entrySet()) {
//将非BORDER_COLOR添加进待设置集合里
if (entry.getKey() != 0) {
realBorderColors.put(entry.getKey(), entry.getValue());
}
}
//如果包含BORDER_COLOR,则替换待设置集合里未设置的边框的颜色
if (borderColors.containsKey(0)) {
Integer allColor = borderColors.get(0);
if (!realBorderColors.containsKey(1)) {
realBorderColors.put(1, allColor);
}
if (!realBorderColors.containsKey(2)) {
realBorderColors.put(2, allColor);
}
if (!realBorderColors.containsKey(3)) {
realBorderColors.put(3, allColor);
}
if (!realBorderColors.containsKey(4)) {
realBorderColors.put(4, allColor);
}
}
for (Map.Entry entry : realBorderColors.entrySet()) {
setRealBorderColor((ReactViewGroup) view, entry.getKey(), entry.getValue());
}
}
}
/**
* 设置border颜色
* @param view
* @param index
* @param color
*/
private void setRealBorderColor(ReactViewGroup view, int index, Integer color) {
float rgbComponent = color == null ? YogaConstants.UNDEFINED : (float) ((int)color & 0x00FFFFFF);
float alphaComponent = color == null ? YogaConstants.UNDEFINED : (float) ((int)color >>> 24);
view.setBorderColor(SPACING_TYPES[index], rgbComponent, alphaComponent);
}
tag里的borderColors:
//map的key为border的index,value为color
//需并发访问
private Map borderColors = new ConcurrentHashMap<>();
public Map getBorderColors() {
return borderColors;
}
跑代码后,表现如下:
左右两边黑框消失了,达成目标。
(逐一去设置自定义的viewmanager,同时对比继承的父类,发现rn自己的borderColor、borderColor等属性也是在各自的viewmanager里都实现了一遍,并没有什么统一的复用方式。)
圆角边框的实现,同样是在ReactViewBackgroundDrawable类里,该类有1300+行,其中后面1000+行是在说绘制圆角的事情,看得头大,也看不出什么问题。
最后到GitHub上查看issue,发现也有其他人遇见锯齿的问题。
看搜索到的第一个issue中,有个官方团队的回答:
说是0.57版本已经修复了此问题。
好吧,与团队里其他成员商量后,考虑到还有其他问题也需要通过升级新版本来解决,最终决定升级版本。一通操作升级到新版本,再解决掉若干兼容问题,终于修复此问题。