用Kotlin撸一个图片压缩插件-实战篇(三)
簡述: 由于個人原因,已經(jīng)有很長一段時間沒有寫過文章,有句話是那么說的只要開始就不會太晚,所以我們開始《用Kotlin擼一個圖片壓縮插件》系列文章最后一篇實戰(zhàn)篇。實際上我已經(jīng)把源碼發(fā)布到了GitHub,代碼很簡單。有了前兩篇文章的基礎(chǔ),這篇文章將會使用Kotlin從零開始帶你擼個圖片壓縮插件。
一、開發(fā)前期準備工作
- 1、訪問TinyPng官網(wǎng)注冊TinyPng開發(fā)者賬號,拿到TinyPng ApiKey,整個過程只需簡單注冊驗證即可。
- 2、由于本項目圖片壓縮框架是基于TinyPng的圖片壓縮API來實現(xiàn)的,所以需要在TinyPng官網(wǎng)提供了develop開發(fā)庫,可以找到相應(yīng)Java的jar,為了方便下載這里就直接貼出地址了:TinyPng依賴包下載
-
3、由于圖片插件使用到GUI,插件GUI采用的是Java中的Swing框架搭建,具體可以去復(fù)習(xí)相關(guān)Swing的知識點,當然只需要大概了解即可,畢竟這個不是重點。
-
4、需要去掌握插件開發(fā)的基礎(chǔ)知識,由于本篇文章是實戰(zhàn)篇就不去細講插件基礎(chǔ)知識,具體詳情可參照該系列的第二篇文章用Kotlin擼一個圖片壓縮插件-插件基礎(chǔ)篇(二)
-
5、需要有Kotlin的基本開發(fā)知識,比如Kotlin中擴展函數(shù)的封裝,Lambda表達式,函數(shù)式API,IO流API的使用
二、圖片壓縮插件基本功能點
圖片壓縮插件主要支持如下兩大功能:
- 1、支持指定圖片源輸入目錄批量壓縮到一個指定的輸出目錄。
- 2、支持在AndroidStudio項目中直接選中指定的一個或多個圖片,右鍵點擊直接壓縮。
三、實現(xiàn)思路分析
實現(xiàn)的整體思路:首先我們需要找到實現(xiàn)關(guān)鍵點,然后從關(guān)鍵點一步步向外擴展延伸,那么實現(xiàn)圖片壓縮的插件的關(guān)鍵點在哪里,肯定毫無疑問是圖片壓縮API,也就是TinyPng API函數(shù)調(diào)用實現(xiàn)。
Tinify.fromFile(inputFile).toFile(inputFile) 復(fù)制代碼通過以上的TinyPng API就可以找到關(guān)鍵點,一個是輸入文件另一個則是輸出文件,那么我們這個圖片壓縮插件的所有實現(xiàn)都是圍繞著如何通過一個簡單的方式指定一個輸入文件或目錄和一個輸出文件或目錄。
沒錯就是這么簡單,那么我們一起來分析下上面兩大功能實現(xiàn)思路其實也很簡單:
-
功能點一: 就是通過Swing框架中的JFileChooser組件,打開并指定一個圖片輸入文件或目錄和一個圖片壓縮后的輸出文件或目錄即可。
-
功能點二: 通過Intellij Idea open api中的 DataKeys.VIRTUAL_FILE_ARRAY.getData(this)拿到當前選中的Virtual Files,也就是當前選中的文件把選中的文件當做輸入文件,然后圖片壓縮后文件直接輸出到源文件中即可。
注意: 由于Tiny.fromFile().toFile()內(nèi)部源碼實際上通過OkHttp發(fā)送圖片壓縮的網(wǎng)絡(luò)請求,而且內(nèi)部采用的方式是同步請求的,但是在IDEA Plugin開發(fā)中主線程是不能執(zhí)行耗時任務(wù)的,所以需要將該API方法調(diào)用放在異步任務(wù)中
四、代碼結(jié)構(gòu)和實現(xiàn)
- action包:主要定義插件中的兩個action,我們都知道在插件開發(fā)中Action是功能執(zhí)行的入口,ImageSlimmingAction是前面說到第一個功能點批量壓縮指定輸入和輸出目錄的,RightSelectedAction是前面說過的第二個功能點在項目選中圖中文件直接右鍵壓縮的, 最后這兩個Action都需要在plugin.xml中注冊。
- extension包: 主要是定義了Kotlin中的擴展函數(shù),一個是Boolean的擴展可以類似鏈式調(diào)用來替代if-else判斷,另一個則是Dialog使用的擴展
-
helper包主要是用文件IO操作,由于兩個Action都存在圖片壓縮操作,為了復(fù)用就直接把圖片壓縮API調(diào)用的實現(xiàn)操作抽出封裝在ImageSlimmingHelper中。
-
ui包主要就是Swing框架中一些界面GUI的實現(xiàn)和交互。
四、實現(xiàn)的關(guān)鍵技術(shù)點
- 關(guān)鍵點一: 插件開發(fā)中如何執(zhí)行一個異步任務(wù)
IDEA Plugin開發(fā)和Android開發(fā)很類似,一些耗時的任務(wù)是不能直接在主線程執(zhí)行的,需要在特定后臺線程執(zhí)行,否則會阻塞主線程。在intellij open api中有個Task.Backgroundable抽象類就是處理異步任務(wù)的。Backgroundable繼承了Task類以及實現(xiàn)了PerformInBackgroundOption接口。具體使用很簡單傳入兩個參數(shù)一個是Project對象和一個執(zhí)行異步中hint提示文本,有四個回調(diào)函數(shù)分別為run(progress: ProgressIndicator)、onSuccess、onThrowable、onFinished.最后通過queue方法加入到異步任務(wù)隊列中。為了方便調(diào)用將其封裝成一個擴展函數(shù)來使用。
//創(chuàng)建后臺異步任務(wù)的Project的擴展函數(shù)asyncTask private fun Project.asyncTask(hintText: String,runAction: (ProgressIndicator) -> Unit,successAction: (() -> Unit)? = null,failAction: ((Throwable) -> Unit)? = null,finishAction: (() -> Unit)? = null ) {object : Task.Backgroundable(this, hintText) {override fun run(p0: ProgressIndicator) {runAction.invoke(p0)}override fun onSuccess() {successAction?.invoke()}override fun onThrowable(error: Throwable) {failAction?.invoke(error)}override fun onFinished() {finishAction?.invoke()}}.queue() } //asyncTask的使用project?.asyncTask(hintText = "正在壓縮", runAction = {//執(zhí)行圖片壓縮操作outputSameFile.yes {//針對右鍵選定圖片情況,直接壓縮當前目錄選中圖片,輸出目錄包括文件也是原來的inputFiles.forEach { inputFile -> Tinify.fromFile(inputFile.absolutePath).toFile(inputFile.absolutePath) }}.otherwise {inputFiles.forEach { inputFile -> Tinify.fromFile(inputFile.absolutePath).toFile(getDestFilePath(model, inputFile.name)) }}}, successAction = {successAction?.invoke()}, failAction = {failAction?.invoke("TinyPng key存在異常,請重新輸入")}) 復(fù)制代碼- 關(guān)鍵點二: 插件開發(fā)中如何獲取當前選中的文件或目錄
在插件開發(fā)中如何獲得當前選中文件,實際上open api提供了類似DataContext數(shù)據(jù)上下文環(huán)境,我們需要去拿到文件集合對象就需要先找到文件管理的窗口對象,還記得上篇博客中說到的AnActionEvent對象是插件與IDEA交互通信的一個媒介,通過AnActionEvent內(nèi)部的dataContext的getData方法,傳入對應(yīng)的DataKey對象獲得相應(yīng)的窗口對象。在CommonDataKey中有一個DataKey<VirtualFile[]>,通過傳入當前event中的dataContext對象即可獲得當前選中的文件對象集合。
private fun DataContext.getSelectedFiles(): Array<VirtualFile>? {return DataKeys.VIRTUAL_FILE_ARRAY.getData(this)//右鍵獲取選中多個文件,擴展函數(shù)} 復(fù)制代碼- 關(guān)鍵點三: Swing中JFileChooser組件的使用
關(guān)于JFileChooser組件的使用就比較簡單了,這里就不去詳細介紹,代碼也很簡單
private void openFileAndSetPath(JComboBox<String> cBoxPath, int selectedMode, Boolean isSupportMultiSelect) {JFileChooser fileChooser = new JFileChooser();fileChooser.setFileSelectionMode(selectedMode);fileChooser.setMultiSelectionEnabled(isSupportMultiSelect);//設(shè)置文件擴展過濾器if (selectedMode != JFileChooser.DIRECTORIES_ONLY) {fileChooser.addChoosableFileFilter(new FileNameExtensionFilter(".png", "png"));fileChooser.addChoosableFileFilter(new FileNameExtensionFilter(".jpg", "jpg"));fileChooser.addChoosableFileFilter(new FileNameExtensionFilter(".jpeg", "jpeg"));}fileChooser.showOpenDialog(null);if (selectedMode == JFileChooser.DIRECTORIES_ONLY) {//僅僅選擇目錄情況,不存在多文件選中File selectedDir = fileChooser.getSelectedFile();if (selectedDir != null) {cBoxPath.insertItemAt(selectedDir.getAbsolutePath(), 0);cBoxPath.setSelectedIndex(0);}} else {//選擇含有文件情況,包括僅僅 選擇文件 和 同時選擇文件和目錄,File[] selectedFiles = fileChooser.getSelectedFiles();if (selectedFiles != null && selectedFiles.length > 0) {cBoxPath.insertItemAt(getSelectedFilePath(selectedFiles), 0);cBoxPath.setSelectedIndex(0);}}} 復(fù)制代碼- 關(guān)鍵點四: api key的驗證和圖片壓縮的實現(xiàn)
在進行圖片壓縮前就是需要去驗證一下TingPng ApiKey的合法性,如果第一次驗證合法就需要把該ApiKey存儲在本地,下次壓縮就直接使用本地的key進行壓縮,一旦本地key失效后,需要重新彈出TinyPng apikey 的驗證提示框,進行重新認證。當然需要注意的是驗證api key的合法性也是進行一次同步的網(wǎng)絡(luò)請求所以它也要放在異步任務(wù)執(zhí)行。
fun checkApiKeyValid(project: Project?,apiKey: String,validAction: (() -> Unit)? = null,invalidAction: ((String) -> Unit)? = null ) {if (apiKey.isBlank()) {invalidAction?.invoke("TinyPng key為空,請重新輸入")}project?.asyncTask(hintText = "正在檢查key是否合法", runAction = {try {Tinify.setKey(apiKey)Tinify.validate()} catch (exception: Exception) {throw exception}}, successAction = {validAction?.invoke()}, failAction = {println("驗證Key失敗!!${it.message}")invalidAction?.invoke("TinyPng key驗證失敗,請重新輸入")}) } 復(fù)制代碼然后就是利用異步任務(wù)進行圖片壓縮操作。
fun slimImage(project: Project?,inputFiles: List<File>,model: ImageSlimmingModel = ImageSlimmingModel("", "", "", ""),successAction: (() -> Unit)? = null,outputSameFile: Boolean = false,failAction: ((String) -> Unit)? = null ) {project?.asyncTask(hintText = "正在壓縮", runAction = {//執(zhí)行圖片壓縮操作outputSameFile.yes {//針對右鍵選定圖片情況,直接壓縮當前目錄選中圖片,輸出目錄包括文件也是原來的inputFiles.forEach { inputFile -> Tinify.fromFile(inputFile.absolutePath).toFile(inputFile.absolutePath) }}.otherwise {inputFiles.forEach { inputFile -> Tinify.fromFile(inputFile.absolutePath).toFile(getDestFilePath(model, inputFile.name)) }}}, successAction = {successAction?.invoke()}, failAction = {failAction?.invoke("TinyPng key存在異常,請重新輸入")}) } 復(fù)制代碼五、總結(jié)
到這里《用Kotlin擼一個圖片壓縮插件》系列文章就結(jié)束了,其實實現(xiàn)起來挺簡單的,其中主要的關(guān)鍵點就是需要更加熟悉使用Intellij open api, 然后其他就是運用好Kotlin的一些語法特性,其余的都很簡單。而且個人覺得把圖片壓縮做成一個插件會變得很高效,不然傳統(tǒng)的模式得需要把圖片拖到瀏覽器中然后一個一個下載下來,還有的人問我不就是一個腳本能解決的嗎?腳本個人覺得不夠靈活不能像插件一樣任意在項目中選中一張或多張圖片直接右鍵壓縮。如有什么問題歡迎下方留言,謝謝。
插件項目源碼地址
歡迎關(guān)注Kotlin開發(fā)者聯(lián)盟,這里有最新Kotlin技術(shù)文章,每周會不定期翻譯一篇Kotlin國外技術(shù)文章。如果你也喜歡Kotlin,歡迎加入我們~~~
總結(jié)
以上是生活随笔為你收集整理的用Kotlin撸一个图片压缩插件-实战篇(三)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql约束与外键_MySQL 外键与
- 下一篇: Haproxy+Percona-Xtra