SVG圖片在移動端的應(yīng)用解決方案

近幾年來SVG使用得越來越多,就連Android的官方庫也加入VectorDrawable的支持。這個類就是用來支持向量圖的。SVG圖片在web端使用非常廣泛,我第一次接觸這個也是在做react-native的項目中使用的。當(dāng)時我們要做一些動畫,需要從一個形狀變換成另一個形狀,這種一般都是用矢量圖來做的。當(dāng)時設(shè)計師就給了我一些矢量圖,于是我就開始研究這個東西。

在react-native中,有專門一個庫叫react-native-svg來處理這個。不過當(dāng)時要做兩個SVG形狀的動畫變化,并不是任何一個形狀都可以的,需要遵循一定的標準。設(shè)計師給我的兩個SVG文件并不能轉(zhuǎn)換。后來是我自己根據(jù)文件里的一些關(guān)鍵參數(shù)自己在代碼里直接畫出來后,再做轉(zhuǎn)換動畫。

最近我在做的native項目中,也遇到了要用SVG的圖片。我們的項目里要從服務(wù)器下載SVG圖片來展示。我們要實現(xiàn)的這些文件是需要服務(wù)器動態(tài)配置的,也就是說我們不能預(yù)先打包進我們的APP里。所以我們這里的要提供一個解決方案,跟圖片JPG圖片一樣顯示,緩存。

這個需求跟之前我遇到的那個需求是很不一樣的,之前的是設(shè)計師已經(jīng)定義好圖片,我們工程師直接拿到文件在程序里展示,不需要考慮下載和緩存之類的。這種需求其實很簡單,我們實際上大部分的需求就是這種需求,網(wǎng)上有很多庫可以完成這種需求。把SVG圖片跟JPG等普通圖片一樣使用,網(wǎng)上的方案還真不多,特別是iOS方面。。。。

要像普通圖片一樣使用,就要考慮下載,本地緩存,內(nèi)存緩存。像這種需求,我們移動端都會使用專門的圖片框架,像安卓端的glide,UIL等,iOS端的SDWebImage等。但是這種庫它是默認都不會考慮SVG圖片。但是我們最好還是像使用這種框架來處理SVG圖片。最好的方式就是把SVG的支持集成到這些庫中。好在這些庫的優(yōu)點就是容易擴展。

安卓端的解決方案

由于我們的項目是采用glide框架來處理圖片,所以這里就只講在這個框架集成SVG圖片的展示。

實際上glide真的是一個很強大的庫,怪不得那么多人用它(早幾年我們都是用UIL),它在它的sample例子里就提供了SVG的展示支持svg。在這個例子里,采用的SVG解碼方式是使用外部的解碼庫。它采用的解碼庫是androidsvg。這個庫是web端移植過來的,所以它有很好的兼容性,是很不錯的庫,雖然它的star不是很多。Android就是好,有強大的Java社區(qū),受益于這些社區(qū),很多庫都不錯。而這方面iOS就那么好了,這個等一下再說。

按照它提供的sample來集成SVG的支持,不是很難。但是我遇到了其他問題。因為我們項目里的glide使用的是3.7版本,sample是基于4.8版本的。這兩個版本在API上有很大區(qū)別,變化很大。所以我必須要先升級到4.8版本。等我升級完后,接入SVG的支持,然而SVG圖片死活顯示不出來。最終發(fā)現(xiàn)是我的AppModule無法生成。一直在文檔,查資料,還是找不到問題,我是完全按照官方文檔升級和集成的。最終我發(fā)現(xiàn)可能是跟AndroidX相關(guān)(還不知道AndroidX是什么的自己查)。我們項目升級到了AndroidX,它會影響一些annotation生成方式。我們的項目采用插件式框架。我們很多通用庫是放在一個commomlibrary的Module中,glide也是。APP module就只是一個殼。但是一些annotation的聲明一定要放在APP module中才行。所以我們把

<pre style="box-sizing: border-box; font-family: monospace, serif; font-size: 1em; white-space: pre; margin: 1em 0px; padding: 1em; direction: ltr; word-wrap: normal; border: 1px solid rgb(204, 204, 204); background: rgb(7, 54, 66); overflow: auto;">annotationProcessor 'androidx.annotation:annotation:1.0.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
</pre>

放在了APP module中,最終那些自動生成的代碼才會真正的生成。這樣就可以很愉快的用glide來顯示SVG圖片了。

<pre style="box-sizing: border-box; font-family: monospace, serif; font-size: 1em; white-space: pre; margin: 1em 0px; padding: 1em; direction: ltr; word-wrap: normal; border: 1px solid rgb(204, 204, 204); background: rgb(7, 54, 66); overflow: auto;">public class GlideSvgUtil {

//顯示網(wǎng)絡(luò)中的svg文件
public static void showSvg(ImageView imageView, String url) {
    Glide.with(imageView.getContext()).as(PictureDrawable.class).listener(new SvgSoftwareLayerSetter()).load(url).into(imageView);
}

//把svg放入到raw中,通過rawid來顯示
public static void showSvgRes(ImageView imageView, int rawId) {
    Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + imageView.getContext().getPackageName() + "/"
            + rawId);
    Glide.with(imageView.getContext()).as(PictureDrawable.class).listener(new SvgSoftwareLayerSetter()).load(uri).into(imageView);
}

//把svg的XML加載到字符串中來顯示
public static void showSvgContent(ImageView imageView, String svgContent) {
    Glide.with(imageView.getContext()).as(PictureDrawable.class).listener(new SvgSoftwareLayerSetter()).load(svgContent.getBytes()).into(imageView);
}

}
</pre>

iOS端的解決方案

iOS的方案還不是很好解決,我沒有發(fā)現(xiàn)有哪一個圖片框架是集成了SVG或者提供了集成的例子的。而且我們的同事還發(fā)現(xiàn)了一個問題。我們服務(wù)器提供的SVG圖片在很多庫解碼出來后沒有了顏色,是黑白的。很詭異的問題,然而安卓端沒有這問題。我們找很多庫,像SVGKit,SwiftSVG,PocketSVG,Macaw,這些庫都是超過1000star的,都無法正常顯示。我基本確定是我們SVG文件的兼容性問題,我問我們的設(shè)計師他是怎么生成SVG文件的。他說是用sketch導(dǎo)出來的。這些文件在web和安卓的庫,還有甚至xcode里都是能正常顯示的。這里我提供一個不能正常顯示的圖片。

<pre style="box-sizing: border-box; font-family: monospace, serif; font-size: 1em; white-space: pre; margin: 1em 0px; padding: 1em; direction: ltr; word-wrap: normal; border: 1px solid rgb(204, 204, 204); background: rgb(7, 54, 66); overflow: auto;"><svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80">
<defs><style>.cls-1{fill:#ff9595;}.cls-2{fill:#ffcc80;}</style></defs>
<title>彩色圖標</title>
<path class="cls-1" d="M43,77A27.14,27.14,0,0,0,58.64,27.67a24.55,24.55,0,0,1-6.4,8A24.44,24.44,0,0,0,34.55,3a24.48,24.48,0,0,1,.87,6.49c0,21.19-25.57,21.72-25.57,42.37C9.85,64.9,20.47,77,43,77Z"/><path class="cls-2" d="M51,66c0-9.28-11.79-11.85-11.79-21.74A12.3,12.3,0,0,1,40,39.95,20.79,20.79,0,0,0,34.77,76.4,54.34,54.34,0,0,0,43,77c.86,0,1.7,0,2.53-.12A13.47,13.47,0,0,0,51,66Z"/>
</svg>
</pre>

因為是顏色不能正常顯示,我猜肯定是defs標簽里的內(nèi)容不能正常解析。我稍微改了下文件,就能正常顯示了。我還發(fā)現(xiàn)對SVG顯示支持的比較好的是Macaw,其他的貌似都有點小問題。所以我把這個問題在Macaw上提交了。我不知道他們會什么時候修改,所以我就自己去研究他們的源代碼,準備自己來解決了。很快我就發(fā)現(xiàn)他們在預(yù)解析階段,解析defs標簽的時候,就沒有考慮style這種子標簽。所以我就加上去了,就兩三行代碼(他們的代碼架構(gòu)挺好),然后就正常顯示了。后來我又到Macaw網(wǎng)站上看,他們已經(jīng)回復(fù)我了,并且已經(jīng)支持!前后也就一個多小時!他說defs標簽里一般不會放style標簽,不過很多其他庫支持,所以他們也就支持了。我去看他改的代碼,幾乎跟我改一樣。所以我就放棄自己的改動,采用cocoapods直接拉取他們的master上的最新代碼。

解碼的問題解決了,但是怎么集成到圖片框架里呢?我們項目是采用swift開發(fā)的,我們采用的圖片處理框架是Kingfisher。我去Kingfisher的網(wǎng)站上看,喵神很厲害,已經(jīng)有很好的文檔教我們怎么擴展圖片解碼器。我一看,另一個問題來了,解碼器是在工作線程中進行的,并且要返回一個UIImage。Macaw這個庫并沒有提供一個可以在子線程中將SVG文件轉(zhuǎn)為UIImage的方法。他們的方法是將SVG顯示在一個UIView里然后截圖。。。也有人將這個問題提了。我看他們源碼發(fā)現(xiàn)有支持的,但是沒有放出來,無法使用。

我最終也只能采用曲線救國的方法了。還是用他們的方法,我在解碼線程里同步切到主線程中生成UIImage,然后再在子線程中返回這個UIImage。好消息是,最近兩天,他們終于支持將一個文件直接轉(zhuǎn)換為UIImage了具體方案看這里,不過我還沒測試,他們也還沒發(fā)新版。

SVG解碼器

<pre style="box-sizing: border-box; font-family: monospace, serif; font-size: 1em; white-space: pre; margin: 1em 0px; padding: 1em; direction: ltr; word-wrap: normal; border: 1px solid rgb(204, 204, 204); background: rgb(7, 54, 66); overflow: auto;">import Foundation
import UIKit
import Kingfisher
import Macaw

struct SVGProcessor: ImageProcessor {
var size = CGSize(width: 32, height: 32)
init(_ size: CGSize) {
if size.width == 0 || size.height == 0 {
print("不支持size為0的情況m,將采用默認值32")
} else {
self.size = size
}
}

let identifier: String = "com.wegene.future"

func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> UIImage? {
    switch item {
    case .image(let image):
        print("already an image")
        return image
    case .data(let data):
        let svgContent = String.init(data: data, encoding: String.Encoding.utf8)
        var img: UIImage?
        DispatchQueue.main.sync {//現(xiàn)在Macaw庫暫時只支持這種方式生成UIImage,下一版他們支持后臺線程生成UIImage的方式,以后再做修改
            let rootNode = try! SVGParser.parse(text: svgContent!)
            let macawView = MacawView(node: rootNode, frame:CGRect(origin: CGPoint.zero, size: size))
            UIGraphicsBeginImageContextWithOptions(size, true, UIScreen.main.scale)
            macawView.layer.render(in: UIGraphicsGetCurrentContext()!)
            img =  UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
        }
        return img
    }
}

}
</pre>

UIImageView的擴展

<pre style="box-sizing: border-box; font-family: monospace, serif; font-size: 1em; white-space: pre; margin: 1em 0px; padding: 1em; direction: ltr; word-wrap: normal; border: 1px solid rgb(204, 204, 204); background: rgb(7, 54, 66); overflow: auto;">extension UIImageView {
func setSvgImage(_ url: String) {
let processor = SVGProcessor(self.frame.size)
let _url = URL(string: url)
self.kf.setImage(with: _url, options:[.processor(processor)])
}
}</pre>

小編有給大家準備高級安卓進階學(xué)習(xí)資料,NDK,架構(gòu),UI,性能優(yōu)化等等
有需要的加群領(lǐng)取
群號:4112676

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,153評論 4 61
  • 7.1 壓縮圖片 一、基礎(chǔ)知識 1、圖片的格式 jpg:最常見的圖片格式。色彩還原度比較好,可以支持適當(dāng)壓縮后保持...
    AndroidMaster閱讀 2,694評論 0 13
  • 一、簡介 在泰國舉行的谷歌開發(fā)者論壇上,谷歌為我們介紹了一個名叫Glide的圖片加載庫,作者是bumptech。這...
    天天大保建閱讀 7,754評論 2 28
  • 文 / 瀟 萱 一千個母親便有一千種愛,一千種愛,都是一樣的情懷。 ——題記 情感學(xué)家說,人類是最有感情的動物,他...
    瀟萱閱讀 941評論 0 3
  • 我沒有咒自己的意思,可如影隨形的頭痛一直在讓我感到害怕,這不是杞人憂天,半年前的一次事故讓我現(xiàn)在都心有余悸,...
    海碗咖啡閱讀 4,009評論 33 8

友情鏈接更多精彩內(nèi)容