【问题标题】:Is it possible to create a VectorDrawable from File System (*.xml File)是否可以从文件系统(*.xml 文件)创建 VectorDrawable
【发布时间】:2015-09-15 14:16:08
【问题描述】:

我正在尝试在我的 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() 时,路径看起来像

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

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

更新这是从 android 开发页面获取的 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>

【问题讨论】:

  • 你能把你的xml文件的代码贴出来吗?

标签: android xml android-vectordrawable


【解决方案1】:

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

正如上一个答案中提到的那样,您确实需要 XML drawable 的编译版本,而不是您从 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。

【讨论】:

  • 有没有办法从普通的 XML 文件中加载文件,就像我们在 IDE 中看到的那样?
  • 太棒了!我花了大约 2 天时间尝试不同的方法,你的工作! :)
  • 什么hack,它可以工作,但担心这种方法是否存在兼容性问题。
  • 我没有看到将图像移动到资产目录的结果。它仍然不能在运行时放置在那里,所以它不能从文件系统中使用:/
  • 是否也可以从文件系统中进行操作?出于某种原因,我在尝试解析已放入文件系统文件中的“ic_launcher_foreground.xml”时得到java.lang.ClassCastException: android.util.XmlPullAttributes cannot be cast to android.content.res.XmlBlock$Parser
【解决方案2】:

很遗憾,没有。除非您以编译资源的方式编译 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 工具打包的资源。

【讨论】:

  • 为此添加了请求:issuetracker.google.com/issues/62435069
  • 我尝试在 API 29 上运行此代码,在“abc_vector_test.xml”文件上运行,该文件已从 APK 提取到普通文件(这意味着它不是 XML 文件,而是二进制编译的文件) .仍然让我java.lang.ClassCastException: android.util.XmlPullAttributes cannot be cast to android.content.res.XmlBlock$Parser。怎么会?
【解决方案3】:

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

  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}")

【讨论】:

    猜你喜欢
    • 2015-05-14
    • 2016-08-10
    • 1970-01-01
    • 2016-08-03
    • 1970-01-01
    • 2015-12-22
    • 1970-01-01
    • 2017-08-29
    • 2011-05-02
    相关资源
    最近更新 更多