Kotlin 如何開發(fā) IDEA 圖片壓縮插件
這篇文章將會(huì)帶大家使用 Kotlin 開發(fā)一個(gè)非常實(shí)用的工具,一款基于 Intellij IDEA 并且可以適用于所有 jetBrains 全家桶 IDE 的圖片壓縮插件,可以直接用于平時(shí)開發(fā)中。
我們經(jīng)常會(huì)遇到一些圖片需要壓縮的需求,特別是 Android 開發(fā)一些打入包內(nèi)的圖片,為了不增加包體積大小需要手動(dòng)壓縮一遍圖片,這時(shí)候一般會(huì)把圖片拖到具體壓縮網(wǎng)站上,在線壓縮然后下載。
如果說(shuō)只需要在 AndroidStudio 或 IDEA 中直接選中要壓縮的圖片即可,是不是很方便呢。開發(fā)該插件目的有兩個(gè):一個(gè)是學(xué)習(xí) Intellij IDEA 插件的開發(fā)流程,另一個(gè)是練習(xí) Kotlin 的開發(fā)實(shí)戰(zhàn)技能。
1. 插件開發(fā)導(dǎo)學(xué)篇
1.1 為什么需要開發(fā)一款圖片壓縮插件
我們?cè)陧?xiàng)目開發(fā)過(guò)程中常會(huì)使用圖片,一般開發(fā)者都不會(huì)直接把設(shè)計(jì)切的圖片放入到項(xiàng)目中,而是會(huì)去壓縮一下,那么一般會(huì)去 TinyPng 網(wǎng)頁(yè)端壓縮一遍,你一般會(huì)先把要壓縮的圖片拖進(jìn)去,然后又一張張把圖片點(diǎn)擊下載下來(lái),是不是感覺(jué)特別的浪費(fèi)時(shí)間,是不是需要把浪費(fèi)的時(shí)間省下來(lái),然后就愉快地早點(diǎn)下班啦。如果你還沒(méi)有使用過(guò) TinyPng,那么這個(gè)插件也許適合你。
然后這段時(shí)間正研究插件,所以決定試試,其實(shí)很簡(jiǎn)單的。因?yàn)?TinyPng 提供 develop api,可以方便實(shí)現(xiàn)圖片壓縮。這次插件也就是利用了它的 API 開發(fā)的。
1.2 插件基本介紹
本插件是一款基于 TinyPng API 開發(fā)的圖片壓縮的 IDEA 工具插件,采用的是 Kotlin 語(yǔ)言開發(fā)以及 Java Swing 框架設(shè)計(jì) UI 界面??蛇\(yùn)行在 AndroidStudio,Intellij IDEA,WebStorm 等 JetBrains 全家桶系列 IDE 中。主要支持以下功能:
- 1、支持整個(gè)目錄中的圖片批量壓縮,只需要指定圖片源目錄和壓縮的輸出目錄即可;
- 2、支持單張或者選定多張圖片文件進(jìn)行壓縮;
- 3、支持 png,jpg 格式圖片;
- 4、支持輸入目錄和輸出目錄二次選擇功能,減少繁瑣指定相同的目錄;
- 5、支持指定輸入文件的前綴,也即是批量文件添加前綴名,以及前綴名二次選擇功能;
- 6、圖片壓縮過(guò)程中,仍然繼續(xù) coding, 工作并行執(zhí)行。
1.3 需要使用的技術(shù)點(diǎn)
- Intellij Idea 插件開發(fā)基礎(chǔ)知識(shí);
- 插件開發(fā)中執(zhí)行一個(gè)后臺(tái)線程任務(wù) Task.Backgroundable 的使用;
- Intellij Idea open api 的使用;
- Kotlin 開發(fā)基礎(chǔ)知識(shí);
- Kotlin 中擴(kuò)展函數(shù)的封裝;
- Kotlin 中 Lambda 表達(dá)式的使用;
- Kotlin 中函數(shù)式 API 的使用;
- Kotlin 中 IO 操作 API 的使用;
- Java 中 Swing UI 框架的基本使用;
- TinyPng API 基本使用。
1.4 實(shí)現(xiàn)后基本效果
2. IntelliJ IDEA 插件開發(fā)基礎(chǔ)篇
2.1 什么是 IntelliJ IDEA 插件
IDE 插件利用 jetBrains 公司開源的 IntelliJ Platform SDK (java 語(yǔ)言) 來(lái)開發(fā)一個(gè)獨(dú)立功能可以安裝在 IDEA 之類的編輯器的功能組件。 IDE 插件是基于 IntelliJ IDEA 開發(fā)工具開發(fā),里面集成了插件的項(xiàng)目的構(gòu)建。采用的是 Java 語(yǔ)言開發(fā)和 IntelliJ 的 SDK 相結(jié)合開發(fā)。
并且在開發(fā)出來(lái)的插件不僅在 AndroidStudio 上可以使用,可以通用于 jetBrains 的編輯器的全家桶工具。通過(guò)源碼可以發(fā)現(xiàn) Intellij Idea 內(nèi)置了大量的插件,可以這么說(shuō) Intellij Idea 開發(fā)工具大部分功能是由插件組合而成的。
2.2 開始編寫第一個(gè)插件
構(gòu)建插件項(xiàng)目的方式主要分為兩種:一種是直接創(chuàng)建 IDEA 內(nèi)置的插件項(xiàng)目。
另一種則是先通過(guò)構(gòu)建一個(gè) gradle 項(xiàng)目,然后加入 plugin.xml 配置以及 加入 IDEA ERP 的依賴,然后來(lái)構(gòu)建一個(gè)插件項(xiàng)目 (整個(gè)開發(fā)過(guò)程就和開發(fā)一個(gè) Android 項(xiàng)目一樣),當(dāng)然這個(gè)構(gòu)建過(guò)程可參考官方給出的 gradle-intellij-plugin 項(xiàng)目來(lái)實(shí)現(xiàn)。
(這里我們以第一種為例) 打開已經(jīng)安裝好的 IntelliJ IDEA,然后 create New Project. 選擇一個(gè) IntelliJ Platform Plugin 項(xiàng)目。注意需要引入 IntelliJ IDEA 的 SDK
選擇好 SDK 后,然后只需要一步一步把項(xiàng)目創(chuàng)建完畢即可,創(chuàng)建好的項(xiàng)目結(jié)構(gòu)如下:
正如你所看到,生成了一個(gè) plugin.xml,這個(gè)文件是插件項(xiàng)目的配置文件,它記錄了插件相關(guān)的版本擴(kuò)展等基本信息,還記錄了插件事件與具體實(shí)現(xiàn)類綁定過(guò)程,下面就一一介紹每個(gè)標(biāo)簽的含義。
<idea-plugin>
<id>com.your.company.unique.plugin.id</id>
<name>Plugin display name here</name>
<version>1.0</version>
<vendor email="support@yourcompany.com" url="http://www.yourcompany.com">YourCompany</vendor>
<description><![CDATA[
Enter short description for your plugin here.<br>
<em>most HTML tags may be used</em>
]]></description>
<change-notes><![CDATA[
Add change notes here.<br>
<em>most HTML tags may be used</em>
]]>
</change-notes>
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
<idea-version since-build="173.0"/>
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products -->
<!-- uncomment to enable plugin in all products
<depends>com.intellij.modules.lang</depends>
-->
<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
</extensions>
<actions>
<!-- Add your actions here -->
</actions>
</idea-plugin>
標(biāo)簽 | 含義 | 解釋說(shuō)明 |
---|---|---|
plugin 插件項(xiàng)目的標(biāo)識(shí) | 和 Android 項(xiàng)目中的 package 功能類似,唯一標(biāo)識(shí)一個(gè)插件項(xiàng)目 | |
插件名字 | 發(fā)布到 jetBrains plugin 倉(cāng)庫(kù)中會(huì)用這個(gè) | |
插件版本號(hào) | 這個(gè)用于標(biāo)識(shí)插件版本,一般用于更新 jetbrains plugins 倉(cāng)庫(kù)中插件版本標(biāo)識(shí) | |
開發(fā)者信息,郵箱和個(gè)人主頁(yè),公司名字或個(gè)人開發(fā)者姓名 | 用于插件倉(cāng)庫(kù)中插件信息介紹顯示 | |
<description> | 插件的描述信息 | 主要是描述插件有什么功能,支持標(biāo)簽內(nèi)部?jī)?nèi)嵌 HTML 標(biāo)簽 |
<changNote> | 插件版本變更信息 | 一般用于插件版本變更的信息,支持標(biāo)簽內(nèi)部?jī)?nèi)嵌 HTML 標(biāo)簽 |
<idea-version> | 插件支持的 idea 版本 | 這個(gè)版本標(biāo)簽需要注意下,它決定了該插件能夠運(yùn)行在最低版本的 IDEA 中,一旦配置不當(dāng),會(huì)導(dǎo)致插件安裝不成功,有點(diǎn)類似 Android 中 AndroidManifest.xml 中配置最低兼容 Android 版本意思 |
<depends> | 當(dāng)前的插件項(xiàng)目依賴哪些內(nèi)置或者外部的插件庫(kù)依賴 | 例如你需要實(shí)現(xiàn)類似 git 功能插件,你就可以通過(guò) depends 標(biāo)簽引入 Git4Idea 即可,Git4Idea, 如果看過(guò) IDEA 源碼的話,實(shí)際上內(nèi)置 GitHub 插件就是通過(guò) depends 依賴內(nèi)部 Git4Idea 插件實(shí)現(xiàn)的,還有現(xiàn)在的碼云 git 工具插件也是通過(guò)依賴 Git4Idea 內(nèi)置插件來(lái)實(shí)現(xiàn)的 |
<extension> | 插件與其他插件或與 IDE 本身交互 | (默認(rèn)是 IDEA) 如果您希望插件擴(kuò)展其他插件或 IntelliJ Platform 的功能,則必須聲明一個(gè)或多個(gè)擴(kuò)展名 |
<action> | 決定了你的插件在 IDE 上顯示的位置和順序 | 這個(gè)標(biāo)簽非常重要,它決定了你的插件在 IDE 上顯示的位置和順序,以及這個(gè)插件的點(diǎn)擊事件和插件項(xiàng)目 Action 實(shí)現(xiàn)類的綁定。 |
創(chuàng)建一個(gè) Action 類,在 IDEA 插件項(xiàng)目中,IDEA 點(diǎn)擊 Item 或者按鈕或者一個(gè)圖標(biāo)對(duì)應(yīng)是觸發(fā)了插件中一個(gè) Action,創(chuàng)建 Action 主要有兩種方式:
第 1 種:通過(guò) IDEA 提供的一個(gè)入口,直接去創(chuàng)建 Action,然后它自動(dòng)幫你實(shí)現(xiàn) plugin.xml 中的事件綁定的注冊(cè):
第 2 種: 手動(dòng)創(chuàng)建一個(gè) Action 類,然后繼承 AnAction 類或者 DumbAwareAction 類,然后在 plugin.xml 中的 action 標(biāo)簽去注冊(cè) action 類與事件綁定:
//創(chuàng)建Action類
package com.imooc.plugins.demo
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.ui.Messages//注意import,是com.intellij.openapi包下
class DemoAction: AnAction() {
override fun actionPerformed(p0: AnActionEvent?) {
Messages.showInfoMessage("Just a Test ", "來(lái)自DemoAction提示")
}
}
在 plugin.xml 中注冊(cè) action 類的綁定:
<actions>
<!-- Add your actions here -->
<action id="com.imooc.plugins.demo.DemoAction" class="com.imooc.plugins.demo.DemoAction" text="DemoAction"
description="just a test demo">
<add-to-group group-id="ToolbarRunGroup" anchor="last"/><!--加入到ToolbarRunGroup內(nèi)置組-->
</action>
</actions>
在 plugin.xml 中配置插件圖標(biāo),先在插件項(xiàng)目中 resource 目錄下創(chuàng)建一個(gè) image 目錄或者直接把圖標(biāo)拷貝目錄下即可 然后 action 標(biāo)簽中指定 icon 屬性:
<actions>
<!-- Add your actions here -->
<action id="com.imooc.plugins.demo.DemoAction" class="com.imooc.plugins.demo.DemoAction" text="DemoAction"
description="just a test demo" icon="/image/icon_pic_demo.png"><!--指定圖標(biāo)-->
<add-to-group group-id="ToolbarRunGroup" anchor="last"/><!--加入到ToolbarRunGroup內(nèi)置組-->
</action>
</actions>
在 plugin.xml 中配置自定義組,并把自定義的組加入內(nèi)置的組中:
<group id="com.imooc.plugins.group.demo" text="Demo" description="just a demo group"><!--group標(biāo)簽實(shí)現(xiàn)自定義組,id:組的唯一標(biāo)識(shí),text:組顯示名稱,description:組的描述名-->
<add-to-group group-id="MainMenu" anchor="last"/><!--把組加入到內(nèi)置的組中-->
<action id="com.imooc.plugins.demo.DemoAction" class="com.imooc.plugins.demo.DemoAction" text="DemoAction" description="just a test demo" icon="/image/icon_pic_demo.png"><!--指定圖標(biāo)-->
<add-to-group group-id="ToolbarRunGroup" anchor="last"/><!--加入到ToolbarRunGroup內(nèi)置組-->
</action>
</group>
配置 OK 后,現(xiàn)在就可以運(yùn)行插件了,運(yùn)行成功后會(huì)新啟動(dòng)一個(gè) Intellij IDEA,這個(gè) IDE 就是安裝了開發(fā)的插件,然后就可以在里面去調(diào)試你的插件功能:
點(diǎn)擊運(yùn)行,進(jìn)行測(cè)試,此外還支持?jǐn)帱c(diǎn)調(diào)試:
最后一步,打包插件,并發(fā)布。選擇頂部工具欄 Build, 點(diǎn)擊 "Prepare Plugin Module ‘Demo’ For Deployment", 就會(huì)在當(dāng)前工作目錄下生成一個(gè) jar 或 zip 的包。然后發(fā)布插件,只需要在 jetBrains Plugins Repository 上傳你的包,等待 jetBrains 官方的審核通過(guò)了,就能通過(guò) ide 中的 plugins 倉(cāng)庫(kù)中搜索找到。
3. 圖片插件開發(fā)篇
3.1 開發(fā)的前期準(zhǔn)備
訪問(wèn) TinyPng 官網(wǎng)注冊(cè) TinyPng 開發(fā)者賬號(hào),拿到 TinyPng ApiKey, 整個(gè)過(guò)程只需簡(jiǎn)單注冊(cè)驗(yàn)證即可:
由于本項(xiàng)目圖片壓縮框架是基于 TinyPng 的圖片壓縮 API 來(lái)實(shí)現(xiàn)的,所以需要在 TinyPng 官網(wǎng)提供了 develop 開發(fā)庫(kù),可以找到相應(yīng) Java 的 jar,為了方便下載這里就直接貼出地址了:TinyPng 依賴包下載
由于圖片插件使用到 GUI,插件 GUI 采用的是 Java 中的 Swing 框架搭建,具體可以去復(fù)習(xí)相關(guān) Swing 的知識(shí)點(diǎn),當(dāng)然只需要大概了解即可。此外你還需要掌握插件開發(fā)的基礎(chǔ)知識(shí),Kotlin 的基本開發(fā)知識(shí),比如 Kotlin 中擴(kuò)展函數(shù)的封裝,Lambda 表達(dá)式,函數(shù)式 API,IO 流 API 的使用。
3.2 插件實(shí)現(xiàn)原理分析
實(shí)現(xiàn)的整體思路:首先我們需要找到實(shí)現(xiàn)關(guān)鍵點(diǎn),然后從關(guān)鍵點(diǎn)一步步向外擴(kuò)展延伸,那么實(shí)現(xiàn)圖片壓縮的插件的關(guān)鍵點(diǎn)在哪里,肯定毫無(wú)疑問(wèn)是圖片壓縮 API,也就是 TinyPng API 函數(shù)調(diào)用實(shí)現(xiàn)。
Tinify.fromFile(inputFile).toFile(inputFile)
通過(guò)以上的 TinyPng API 就可以找到關(guān)鍵點(diǎn),一個(gè)是輸入文件另一個(gè)則是輸出文件,那么我們這個(gè)圖片壓縮插件的所有實(shí)現(xiàn)都是圍繞著如何通過(guò)一個(gè)簡(jiǎn)單的方式指定一個(gè)輸入文件或目錄和一個(gè)輸出文件或目錄。
沒(méi)錯(cuò)就是這么簡(jiǎn)單,那么我們一起來(lái)分析下上面兩大功能實(shí)現(xiàn)思路其實(shí)也很簡(jiǎn)單:
-
功能點(diǎn)一:就是通過(guò) Swing 框架中的 JFileChooser 組件,打開并指定一個(gè)圖片輸入文件或目錄和一個(gè)圖片壓縮后的輸出文件或目錄即可。
-
功能點(diǎn)二:通過(guò) Intellij Idea open api 中的
DataKeys.VIRTUAL_FILE_ARRAY.getData(this)
拿到當(dāng)前選中的 Virtual Files,也就是當(dāng)前選中的文件把選中的文件當(dāng)做輸入文件,然后圖片壓縮后文件直接輸出到源文件中即可。
注意:由于 Tiny.fromFile ().toFile () 內(nèi)部源碼實(shí)際上通過(guò) OkHttp 發(fā)送圖片壓縮的網(wǎng)絡(luò)請(qǐng)求,而且內(nèi)部采用的方式是同步請(qǐng)求的,但是在 IDEA Plugin 開發(fā)中主線程是不能執(zhí)行耗時(shí)任務(wù)的,所以需要將該 API 方法調(diào)用放在異步任務(wù)中。
3.3 項(xiàng)目代碼結(jié)構(gòu)
action 包主要定義插件中的兩個(gè) action,我們都知道在插件開發(fā)中 Action 是功能執(zhí)行的入口,ImageSlimmingAction 是前面說(shuō)到第一個(gè)功能點(diǎn)批量壓縮指定輸入和輸出目錄的,RightSelectedAction 是前面說(shuō)過(guò)的第二個(gè)功能點(diǎn)在項(xiàng)目選中圖中文件直接右鍵壓縮的,最后這兩個(gè) Action 都需要在 plugin.xml 中注冊(cè)。
<actions>
<action class="com.imooc.plugins.image.slimming.action.ImageSlimmingAction" text="ImageSlimming"
id="com.imooc.plugins.image.slimming.action.ImageSlimmingAction"
description="compress picture plugin" icon="/img/icon_image_slimming.png">
<add-to-group group-id="MainToolBar" anchor="after" relative-to-action="Android.MainToolBarSdkGroup"/>
</action>
<action id="com.imooc.plugins.image.action.rightselectedaction"
class="com.imooc.plugins.image.slimming.action.RightSelectedAction" text="Quick Slim Images"
description="Quick Slim Images">
<add-to-group group-id="ProjectViewPopupMenu" anchor="after" relative-to-action="ReplaceInPath"/>
</action>
</actions>
extension 包主要是定義了 Kotlin 中的擴(kuò)展函數(shù),一個(gè)是 Boolean 的擴(kuò)展可以類似鏈?zhǔn)秸{(diào)用來(lái)替代 if-else 判斷,另一個(gè)則是 Dialog 使用的擴(kuò)展:
//Boolean 擴(kuò)展
sealed class BooleanExt<out T>
object Otherwise : BooleanExt<Nothing>()//Nothing是所有類的子類,協(xié)變的類繼承關(guān)系和泛型參數(shù)類型繼承關(guān)系一致
class TransferData<T>(val data: T) : BooleanExt<T>()
inline fun <T> Boolean.yes(block: () -> T): BooleanExt<T> = when {
this -> TransferData(block.invoke())
else -> Otherwise
}
inline fun <T> Boolean.no(block: () -> T): BooleanExt<T> = when {
this -> Otherwise
else -> TransferData(block.invoke())
}
inline fun <T> BooleanExt<T>.otherwise(block: () -> T): T = when (this) {
is Otherwise ->
block()
is TransferData ->
this.data
}
//Dialog擴(kuò)展
fun Dialog.showDialog(width: Int = 550, height: Int = 400, isInCenter: Boolean = true, isResizable: Boolean = false) {
pack()
this.isResizable = isResizable
setSize(width, height)
if (isInCenter) {
setLocation(Toolkit.getDefaultToolkit().screenSize.width / 2 - width / 2, Toolkit.getDefaultToolkit().screenSize.height / 2 - height / 2)
}
isVisible = true
}
fun Project.showWarnDialog(icon: Icon = UIUtil.getWarningIcon(), title: String, msg: String, positiveText: String = "確定", negativeText: String = "取消", positiveAction: (() -> Unit)? = null, negativeAction: (() -> Unit)? = null) {
Messages.showDialog(this, msg, title, arrayOf(positiveText, negativeText), 0, icon, object : DialogWrapper.DoNotAskOption.Adapter() {
override fun rememberChoice(p0: Boolean, p1: Int) {
if (p1 == 0) {
positiveAction?.invoke()
} else if (p1 == 1) {
negativeAction?.invoke()
}
}
})
}
helper 包主要是用文件 IO 操作,由于兩個(gè) Action 都存在圖片壓縮操作,為了復(fù)用就直接把圖片壓縮 API 調(diào)用的實(shí)現(xiàn)操作抽出封裝在 ImageSlimmingHelper 中,ui 包主要就是 Swing 框架中一些界面 GUI 的實(shí)現(xiàn)和交互。
3.4 插件實(shí)現(xiàn)核心點(diǎn)分析
插件開發(fā)中如何執(zhí)行一個(gè)異步任務(wù):
IDEA Plugin 開發(fā)和 Android 開發(fā)很類似,一些耗時(shí)的任務(wù)是不能直接在主線程執(zhí)行的,需要在特定后臺(tái)線程執(zhí)行,否則會(huì)阻塞主線程。在 intellij open api 中有個(gè) Task.Backgroundable 抽象類就是處理異步任務(wù)的。Backgroundable 繼承了 Task 類以及實(shí)現(xiàn)了 PerformInBackgroundOption 接口。具體使用很簡(jiǎn)單傳入兩個(gè)參數(shù)一個(gè)是 Project 對(duì)象和一個(gè)執(zhí)行異步中 hint 提示文本,有四個(gè)回調(diào)函數(shù)分別為 run (progress: ProgressIndicator)、onSuccess、onThrowable、onFinished. 最后通過(guò) queue 方法加入到異步任務(wù)隊(duì)列中。為了方便調(diào)用將其封裝成一個(gè)擴(kuò)展函數(shù)來(lái)使用。
//創(chuàng)建后臺(tái)異步任務(wù)的Project的擴(kuò)展函數(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 {
//針對(duì)右鍵選定圖片情況,直接壓縮當(dāng)前目錄選中圖片,輸出目錄包括文件也是原來(lái)的
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存在異常,請(qǐng)重新輸入")
})
插件開發(fā)中如何獲取當(dāng)前選中的文件或目錄
在插件開發(fā)中如何獲得當(dāng)前選中文件,實(shí)際上 open api 提供了類似 DataContext 數(shù)據(jù)上下文環(huán)境,我們需要去拿到文件集合對(duì)象就需要先找到文件管理的窗口對(duì)象,還記得上篇博客中說(shuō)到的 AnActionEvent 對(duì)象是插件與 IDEA 交互通信的一個(gè)媒介,通過(guò) AnActionEvent 內(nèi)部的 dataContext 的 getData 方法,傳入對(duì)應(yīng)的 DataKey 對(duì)象獲得相應(yīng)的窗口對(duì)象。在 CommonDataKey 中有一個(gè) DataKey<VirtualFile []>,通過(guò)傳入當(dāng)前 event 中的 dataContext 對(duì)象即可獲得當(dāng)前選中的文件對(duì)象集合。
private fun DataContext.getSelectedFiles(): Array<VirtualFile>? {
return DataKeys.VIRTUAL_FILE_ARRAY.getData(this)//右鍵獲取選中多個(gè)文件,擴(kuò)展函數(shù)
}
api key 的驗(yàn)證和圖片壓縮的實(shí)現(xiàn)
在進(jìn)行圖片壓縮前就是需要去驗(yàn)證一下 TingPng ApiKey 的合法性,如果第一次驗(yàn)證合法就需要把該 ApiKey 存儲(chǔ)在本地,下次壓縮就直接使用本地的 key 進(jìn)行壓縮,一旦本地 key 失效后,需要重新彈出 TinyPng apikey 的驗(yàn)證提示框,進(jìn)行重新認(rèn)證。當(dāng)然需要注意的是驗(yàn)證 api key 的合法性也是進(jìn)行一次同步的網(wǎng)絡(luò)請(qǐng)求所以它也要放在異步任務(wù)執(zhí)行。
fun checkApiKeyValid(
project: Project?,
apiKey: String,
validAction: (() -> Unit)? = null,
invalidAction: ((String) -> Unit)? = null
) {
if (apiKey.isBlank()) {
invalidAction?.invoke("TinyPng key為空,請(qǐng)重新輸入")
}
project?.asyncTask(hintText = "正在檢查key是否合法", runAction = {
try {
Tinify.setKey(apiKey)
Tinify.validate()
} catch (exception: Exception) {
throw exception
}
}, successAction = {
validAction?.invoke()
}, failAction = {
println("驗(yàn)證Key失敗!!${it.message}")
invalidAction?.invoke("TinyPng key驗(yàn)證失敗,請(qǐng)重新輸入")
})
}
然后就是利用異步任務(wù)進(jìn)行圖片壓縮操作。
4. 總結(jié)
到這里有關(guān) Kotlin 實(shí)現(xiàn)圖片插件實(shí)戰(zhàn)篇就結(jié)束了,本篇文章篇幅比較多,主要介紹了如何去開發(fā)一個(gè) IDEA 插件以及使用 Kotlin 去開發(fā)一個(gè)圖片壓縮插件具體實(shí)現(xiàn)方案和技術(shù)重點(diǎn)和難點(diǎn)。下篇文章我們將繼續(xù) Kotlin 的實(shí)戰(zhàn)篇系列。