我正在从服务器向应用程序发送矢量绘图。 我想实现一个函数,该函数获取路径数据和颜色的列表,并从给定的String / Color对列表中创建可绘制的向量。
我使用了类似线程(Create VectorDrawable from String (path)?上的Nicolas给出的答案,但是当给定多个路径/颜色对项时,它失败了。
有人可以帮助我找到错误吗?代码对我来说似乎还可以。
这是我的代码:
private static final byte[][] BIN_XML_STRINGS = {
"height".getBytes(), "width".getBytes(), "viewportWidth".getBytes(),
"viewportHeight".getBytes(), "fillColor".getBytes(), "pathData".getBytes(),
"http://schemas.android.com/apk/res/android".getBytes(), "path".getBytes(), "vector".getBytes()
};
private static final int[] BIN_XML_ATTRS = {android.R.attr.height, android.R.attr.width, android.R.attr.viewportWidth,
android.R.attr.viewportHeight, android.R.attr.fillColor, android.R.attr.pathData};
public static Drawable getVectorDrawable(@NonNull Context context,
int width, int height,
float viewportWidth, float viewportHeight,
List<Pair<String, Integer>> pathColorList) {
List<Pair<byte[], Integer>> pathBytes = new ArrayList<>();
for(Pair<String, Integer> pathData: pathColorList){
pathBytes.add(new Pair<>(pathData.first.getBytes(), pathData.second));
}
try {
// Get the binary XML parser (XmlBlock.Parser) and use it to create the drawable
// This is the equivalent of what AssetManager#getXml() does
@SuppressLint("PrivateApi")
Class<?> xmlBlock = Class.forName("android.content.res.XmlBlock");
Constructor xmlBlockConstr = xmlBlock.getConstructor(byte[].class);
Method xmlParserNew = xmlBlock.getDeclaredMethod("newParser");
xmlBlockConstr.setAccessible(true);
xmlParserNew.setAccessible(true);
XmlPullParser parser = (XmlPullParser) xmlParserNew.invoke(
xmlBlockConstr.newInstance((Object) binXml));
KappaLogger.LogError(parser.toString());
if (Build.VERSION.SDK_INT >= 24) {
return Drawable.createFromXml(context.getResources(), parser);
} else {
// Before API 24, vector drawables aren't rendered correctly without compat lib
final AttributeSet attrs = Xml.asAttributeSet(parser);
int type = parser.next();
while (type != XmlPullParser.START_TAG) {
type = parser.next();
}
return VectorDrawableCompat.createFromXmlInner(context.getResources(), parser, attrs, null);
}
} catch (Exception e) {
KappaExceptionUtils.sendStackTraceToLog(e);
}
return null;
}
private static byte[] createBinaryDrawableXml(int width, int height,
float viewportWidth, float viewportHeight,
List<Pair<byte[], Integer>> pathBytes) {
List<byte[]> binXmlStrings = new ArrayList<>(Arrays.asList(BIN_XML_STRINGS));
for(Pair<byte[], Integer> pathItem: pathBytes){
binXmlStrings.add(pathItem.first);
}
ByteBuffer bb = ByteBuffer.allocate(8192);
bb.order(ByteOrder.LITTLE_ENDIAN);
// ==== XML header ====
bb.putShort((short) 0x0003); // Type: XML
bb.putShort((short) 8); // Header size
int xmlSizePos = bb.position();
bb.position(bb.position() + 4);
// ==== String pool chunk ====
int spStartPos = bb.position();
bb.putShort((short) 0x0001); // Type: String pool
bb.putShort((short) 28); // Header size
int spSizePos = bb.position();
bb.position(bb.position() + 4);
bb.putInt(binXmlStrings.size()); // String count
bb.putInt(0); // Style count
bb.putInt(256); // Flags set: encoding is UTF-8
bb.putInt(0x44); // Strings start
bb.putInt(0); // Styles start
// String offsets
int offset = 0;
for (byte[] str : binXmlStrings) {
bb.putInt(offset);
offset += str.length + (str.length > 127 ? 5 : 3);
}
// String pool
for (byte[] str : binXmlStrings) {
if (str.length > 127) {
byte high = (byte) ((str.length & 0xFF00 | 0x8000) >>> 8);
byte low = (byte) (str.length & 0xFF);
bb.put(high);
bb.put(low);
bb.put(high);
bb.put(low);
} else {
byte len = (byte) str.length;
bb.put(len);
bb.put(len);
}
bb.put(str);
bb.put((byte) 0);
}
if (bb.position() % 4 != 0) {
// Padding to align on 32-bit
bb.put(new byte[4 - (bb.position() % 4)]);
}
// Write string pool chunk size
int posBefore = bb.position();
bb.putInt(spSizePos, bb.position() - spStartPos);
bb.position(posBefore);
// ==== Resource map chunk ====
bb.putShort((short) 0x0180); // Type: Resource map
bb.putShort((short) 8); // Header size
bb.putInt(8 + BIN_XML_ATTRS.length * 4); // Chunk size
for (int attr : BIN_XML_ATTRS) {
bb.putInt(attr);
}
// ==== Vector start tag ====
int vstStartPos = bb.position();
bb.putShort((short) 0x0102); // Type: Start tag
bb.putShort((short) 16); // Header size
int vstSizePos = bb.position();
bb.position(bb.position() + 4);
bb.putInt(0); // Line number: None
bb.putInt(-1); // Comment: None
bb.putInt(-1); // Namespace: None
bb.putInt(8); // Name: vector (index 9)
bb.putShort((short) 0x14);
bb.putShort((short) 0x14);
bb.putShort((short) 4); // Attribute count
bb.putShort((short) 0);
bb.putShort((short) 0);
bb.putShort((short) 0);
// Attributes
bb.putInt(6); // Namespace: android
bb.putInt(0); // Name: height
bb.putInt(-1); // Raw value: none
bb.putShort((short) 0x08); // Value size
bb.putShort((short) 0x0500); // value type: dimension
bb.putInt(height * 256 + 1); // Value data: 0x01 for dp, 0x18 for 24
bb.putInt(6); // Namespace: android
bb.putInt(1); // Name: width
bb.putInt(-1); // Raw value: none
bb.putShort((short) 0x08); // Value size
bb.putShort((short) 0x0500); // value type: dimension
bb.putInt(width * 256 + 1); // Value data: 0x01 for dp, 0x18 for 24
bb.putInt(6); // Namespace: android
bb.putInt(2); // Name: viewportWidth
bb.putInt(-1); // Raw value: none
bb.putShort((short) 0x08); // Value size
bb.putShort((short) 0x0400); // value type: float
bb.putInt(Float.floatToRawIntBits(viewportWidth)); // Value data: 24.0
bb.putInt(6); // Namespace: android
bb.putInt(3); // Name: viewportHeight
bb.putInt(-1); // Raw value: none
bb.putShort((short) 0x08); // Value size
bb.putShort((short) 0x0400); // value type: float
bb.putInt(Float.floatToRawIntBits(viewportHeight)); // Value data: 24.0
// Write vector start tag chunk size
posBefore = bb.position();
bb.putInt(vstSizePos, bb.position() - vstStartPos);
bb.position(posBefore);
for(int i=0; i<pathBytes.size(); i++){
// ==== Path start tag ====
int pstStartPos = bb.position();
bb.putShort((short) 0x0102); // Type: Start tag
bb.putShort((short) 16); // Header size
int pstSizePos = bb.position();
bb.position(bb.position() + 4);
bb.putInt(0); // Line number: None
bb.putInt(-1); // Comment: None
bb.putInt(-1); // Namespace: None
bb.putInt(7); // Name: path (index 8)
bb.putShort((short) 0x14);
bb.putShort((short) 0x14);
bb.putShort((short) 2); // Attribute count
bb.putShort((short) 0);
bb.putShort((short) 0);
bb.putShort((short) 0);
bb.putInt(6); // Namespace: android
bb.putInt(4); // Name: fillColor
bb.putInt(-1); // Raw value: none
bb.putShort((short) 0x08); // Value size
bb.putShort((short) 0x1D00); // value type: color #rgb
bb.putInt(pathBytes.get(i).second); // Value data: color
bb.putInt(6); // Namespace: android
bb.putInt(5); // Name: pathData
bb.putInt(i+9); // Raw value: index 9 in string pool (path data)
bb.putShort((short) 0x08); // Value size
bb.putShort((short) 0x0300); // value type: string
bb.putInt(i+9); // Value data: same as raw data
// Write path start tag chunk size
posBefore = bb.position();
bb.putInt(pstSizePos, bb.position() - pstStartPos);
bb.position(posBefore);
// ==== Path end tag ====
bb.putShort((short) 0x0103);
bb.putShort((short) 16); // Header size
bb.putInt(24); // Chunk size
bb.putInt(0); // Line number: none
bb.putInt(-1); // Comment: none
bb.putInt(-1); // Namespace: none
bb.putInt(7); // Name: path
}
// ==== Vector end tag ====
bb.putShort((short) 0x0103);
bb.putShort((short) 16); // Header size
bb.putInt(24); // Chunk size
bb.putInt(0); // Line number: none
bb.putInt(-1); // Comment: none
bb.putInt(-1); // Namespace: none
bb.putInt(8); // Name: vector
// Write XML chunk size
posBefore = bb.position();
bb.putInt(xmlSizePos, bb.position());
bb.position(posBefore);
byte[] binXml = new byte[bb.position()];
bb.rewind();
bb.get(binXml);
StringBuilder sb = new StringBuilder();
for (byte b : binXml) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) sb.append('0');
sb.append(hex.toUpperCase());
sb.append(' ');
}
String str = sb.toString();
return binXml;
}
它适用于向量中的单个路径,但是当我使用以下配对/颜色列表时,会出现android.view.InflateException: Class not found h
错误。
输入:
[
new Pair<>("M177.230469 329.847656v167.382813c0 8.15625 6.613281 14.769531 14.769531 14.769531l9.855469-99.9375-9.855469-96.984375c-8.15625 0-14.769531 6.613281-14.769531 14.769531zm0 0",Color.BLACK),
new Pair<>("M192 315.078125v196.921875c8.15625 0 14.769531-6.613281 14.769531-14.769531v-167.382813c0-8.15625-6.613281-14.769531-14.769531-14.769531zm0 0", Color.RED)
]
我为width
和height
传递了512,并为512.0f
和viewportWidth
输入变量传递了viewportHeight
。
答案 0 :(得分:1)
我清理并修复了答案中的代码:https://stackoverflow.com/a/49920860/5288316
您现在可以传递具有不同颜色的路径列表,并更改可绘制尺寸和视口尺寸。
问题出在线路上
bb.putInt(0x44); // Strings start
字符串池块的此参数是该块的开始与字符串列表的开始之间的字节数。在这之间有一个字符串偏移量列表,当存在多个路径时,它会更长一些,因此0x44
的值不再正确。