是否可以从文件系统(* .xml文件)创建VectorDrawable

时间:2015-09-15 14:16:09

标签: android xml android-vectordrawable

我正在尝试在我的Android应用中使用VectorDrawables 我想从文件系统加载一个xml文件,并获得一个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()时,Path看起来像

/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>

3 个答案:

答案 0 :(得分:4)

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

正如前面的答案中所提到的,你确实需要XML drawable的编译版本,而不是你从Android Studio获得的版本。 AFAIK,VectorDrawable API尚不支持从原始XML加载。

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

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

注意&#39; assets /&#39;路径的一部分。这是必须的,afaik。

答案 1 :(得分:3)

不幸的是,没有。除非您以编译资源的方式以某种方式编译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调用尝试将属性集强制转换为XmlBlock.Parser,这会抛出ClassCastException。原因记录在source;它将“仅适用于已编译的XML文件”,例如aapt工具打包的资源。

答案 2 :(得分:0)

是的,有可能但可悲的是,根据某人给我看的东西,它有很多缺点:

  1. 需要反思,所以有一天可能会失效
  2. 反映在私有API上,不建议
  3. 仅适用于已编译的VectorDrawable XML文件。

话虽这么说,也许您可​​以通过查看其他库的代码来创建自己的XmlPullParser,例如here(或我的叉子,here)。有趣的类名是“ 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}")