13

我正在尝试在我的 Android 应用程序中使用 VectorDrawables。
我想从文件系统加载一个 xml 文件并获取一个 Instance 以android.graphics.Drawable将其显示在ImageView. 如果我将 xml 文件添加到 Resources 目录,它就可以工作。但是当我尝试从文件系统加载它时,我总是得到一个 NullPointer。

我目前正在尝试通过 Drawable.createFromPath(*Path to File*)或加载文件VectorDrawable.createFromPath(*Path to File*),但我不断收到 NullPointer。该文件存在并且是一个有效的 xml 文件(见下文)。

在 adb 日志中,我总是得到:

SkImageDecoder::Factory returned null

当我使用 mContext.getFilesDir()路径看起来像

/data/data/*packagename*/files/*filename*.xml

我还尝试了一些公共文件夹,例如“下载”。当我使用 File.io Api 检查文件时exists()canRead()canWrite()

更新这是从 android Dev Pages 获取的 XML 代码

 <vector xmlns:android="http://schemas.android.com/apk/res/android"
 android:height="64dp"
 android:width="64dp"
 android:viewportHeight="600"
 android:viewportWidth="600" >
 <group
     android:name="rotationGroup"
     android:pivotX="300.0"
     android:pivotY="300.0"
     android:rotation="45.0" >
     <path
         android:name="v"
         android:fillColor="#000000"
         android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
 </group>

4

3 回答 3

10

实际上,是的,你可以,而且我已经在我自己的项目中做到了。

正如上一个答案中提到的那样,您确实需要 XML 可绘制的编译版本,而不是您从 Android Studio 中获得的版本。AFAIK,VectorDrawable API 还不支持从原始 XML 加载。

为此,只需将您的图像临时放在res目录下,以便在构建过程中将它们编译为普通资源,然后找到您生成的 APK 并从那里提取它们(您会注意到它们不再是文本文件,而是二进制文件文件)。现在将它们再次放在您的资产目录下您想要的任何位置,并使用与此类似的代码将它们加载为可绘制对象:

XmlResourceParser parser = context.getAssets()
    .openXmlResourceParser("assets/folder/image.xml");
drawable = VectorDrawableCompat.createFromXml(context.getResources(), parser);

注意路径的“assets/”部分。这是必需的,afaik。

于 2017-02-16T17:15:15.553 回答
7

抱歉不行。除非您以编译资源的方式编译 XML 源文件。 VectorDrawable当前假定已编译的图像源。

否则你可能会写这样的东西:

VectorDrawable d = new VectorDrawable();
try( InputStream in = new FileInputStream( *Path to File* ); )
{
    XmlPullParser p = Xml.newPullParser();
    p.setInput( in, /*encoding, self detect*/null );
    d.inflate( getResources(), p, Xml.asAttributeSet(p) ); // FAILS
}

当前失败(API 级别 23,Marshmallow 6.0),因为 inflate 调用尝试将属性集强制转换为 an XmlBlock.Parser,这会引发ClassCastException. 原因记录在文件中;它将“仅适用于已编译的 XML 文件”,例如该aapt工具打包的资源。

于 2016-02-17T18:21:50.687 回答
0

是的,这是可能的,但遗憾的是,根据有人向我展示的内容,它有多个缺点:

  1. 需要反思,所以有一天可能无法工作
  2. 反射在私有API上,不推荐
  3. 仅适用于已编译的 VectorDrawable XML 文件。

话虽如此,也许您可​​以通过查看其他库的代码来创建自己的 XmlPullParser,例如此处(或我的分支,此处)。有趣的类名是“BinaryXmlParser”,我认为这可能会有所帮助。我尝试使用其他方法,但它导致了 的异常java.lang.ClassCastException: android.util.XmlPullAttributes cannot be cast to android.content.res.XmlBlock$Parser,所以它可能只想使用反射的 XmlBlock 类,这意味着即使你实现了它,我认为它很有可能不起作用。

这是代码(基于here):

object VectorDrawableParser {
    /**
     * Create a vector drawable from a binary XML byte array.
     *
     * @param context Any context.
     * @param binXml  Byte array containing the binary XML.
     * @return The vector drawable or null it couldn't be created.
     */
    @SuppressLint("PrivateApi", "DiscouragedPrivateApi")
    fun getVectorDrawable(context: Context, binXml: ByteArray): Drawable? {
        try {
            // Get the binary XML parser (XmlBlock.Parser) and use it to create the drawable
            // This is the equivalent of what AssetManager#getXml() does
            val xmlBlock = Class.forName("android.content.res.XmlBlock")
            val xmlBlockConstr = xmlBlock.getConstructor(ByteArray::class.java)
            val xmlParserNew = xmlBlock.getDeclaredMethod("newParser")
            xmlBlockConstr.isAccessible = true
            xmlParserNew.isAccessible = true
            val parser = xmlParserNew.invoke(xmlBlockConstr.newInstance(binXml)) as XmlPullParser
            return if (Build.VERSION.SDK_INT >= 24) {
                Drawable.createFromXml(context.resources, parser)
            } else {
                // Before API 24, vector drawables aren't rendered correctly without compat lib
                val attrs = Xml.asAttributeSet(parser)
                var type = parser.next()
                while (type != XmlPullParser.START_TAG) {
                    type = parser.next()
                }
                VectorDrawableCompat.createFromXmlInner(context.resources, parser, attrs, null)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }
}

以及如何使用:

val filePath = "/storage/emulated/0/abc_vector_test.xml"
val readBytes = FileInputStream(File(filePath)).readBytes()
val vectorDrawable = VectorDrawableParser.getVectorDrawable(this, readBytes)
Log.d("AppLog", "got it?${vectorDrawable != null}")
于 2020-05-31T10:23:42.437 回答