用Kotlin擼一個圖片壓縮插件-實戰篇(三)

簡述: 由于個人原因,已經有很長一段時間沒有寫過文章,有句話是那么說的只要開始就不會太晚,所以我們開始《用Kotlin擼一個圖片壓縮插件》系列文章最后一篇實戰篇。實際上我已經把源碼發布到了GitHub,代碼很簡單。有了前兩篇文章的基礎,這篇文章將會使用Kotlin從零開始帶你擼個圖片壓縮插件。

一、開發前期準備工作

  • 1、訪問TinyPng官網注冊TinyPng開發者賬號,拿到TinyPng ApiKey,整個過程只需簡單注冊驗證即可。

  • 2、由于本項目圖片壓縮框架是基于TinyPng的圖片壓縮API來實現的,所以需要在TinyPng官網提供了develop開發庫,可以找到相應Java的jar,為了方便下載這里就直接貼出地址了:TinyPng依賴包下載

  • 3、由于圖片插件使用到GUI,插件GUI采用的是Java中的Swing框架搭建,具體可以去復習相關Swing的知識點,當然只需要大概了解即可,畢竟這個不是重點。

  • 4、需要去掌握插件開發的基礎知識,由于本篇文章是實戰篇就不去細講插件基礎知識,具體詳情可參照該系列的第二篇文章用Kotlin擼一個圖片壓縮插件-插件基礎篇(二)

  • 5、需要有Kotlin的基本開發知識,比如Kotlin中擴展函數的封裝,Lambda表達式,函數式API,IO流API的使用

二、圖片壓縮插件基本功能點

圖片壓縮插件主要支持如下兩大功能:

  • 1、支持指定圖片源輸入目錄批量壓縮到一個指定的輸出目錄。

  • 2、支持在AndroidStudio項目中直接選中指定的一個或多個圖片,右鍵點擊直接壓縮。

三、實現思路分析

實現的整體思路:首先我們需要找到實現關鍵點,然后從關鍵點一步步向外擴展延伸,那么實現圖片壓縮的插件的關鍵點在哪里,肯定毫無疑問是圖片壓縮API,也就是TinyPng API函數調用實現。

Tinify.fromFile(inputFile).toFile(inputFile)
復制代碼

通過以上的TinyPng API就可以找到關鍵點,一個是輸入文件另一個則是輸出文件,那么我們這個圖片壓縮插件的所有實現都是圍繞著如何通過一個簡單的方式指定一個輸入文件或目錄和一個輸出文件或目錄。

沒錯就是這么簡單,那么我們一起來分析下上面兩大功能實現思路其實也很簡單:

  • 功能點一: 就是通過Swing框架中的JFileChooser組件,打開并指定一個圖片輸入文件或目錄和一個圖片壓縮后的輸出文件或目錄即可。

  • 功能點二: 通過Intellij Idea open api中的 DataKeys.VIRTUAL_FILE_ARRAY.getData(this)拿到當前選中的Virtual Files,也就是當前選中的文件把選中的文件當做輸入文件,然后圖片壓縮后文件直接輸出到源文件中即可。

注意: 由于Tiny.fromFile().toFile()內部源碼實際上通過OkHttp發送圖片壓縮的網絡請求,而且內部采用的方式是同步請求的,但是在IDEA Plugin開發中主線程是不能執行耗時任務的,所以需要將該API方法調用放在異步任務中

四、代碼結構和實現

  • action包:主要定義插件中的兩個action,我們都知道在插件開發中Action是功能執行的入口,ImageSlimmingAction是前面說到第一個功能點批量壓縮指定輸入和輸出目錄的,RightSelectedAction是前面說過的第二個功能點在項目選中圖中文件直接右鍵壓縮的, 最后這兩個Action都需要在plugin.xml中注冊。
  <actions><action class="com.mikyou.plugins.image.slimming.action.ImageSlimmingAction" text="ImageSlimming"id="com.mikyou.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.mikyou.plugins.image.action.rightselectedaction"class="com.mikyou.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中的擴展函數,一個是Boolean的擴展可以類似鏈式調用來替代if-else判斷,另一個則是Dialog使用的擴展
//Boolean 擴展
sealed class BooleanExt<out T>object Otherwise : BooleanExt<Nothing>()//Nothing是所有類的子類,協變的類繼承關系和泛型參數類型繼承關系一致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 -> Otherwiseelse -> TransferData(block.invoke())
}inline fun <T> BooleanExt<T>.otherwise(block: () -> T): T = when (this) {is Otherwise ->block()is TransferData ->this.data
}//Dialog擴展
fun Dialog.showDialog(width: Int = 550, height: Int = 400, isInCenter: Boolean = true, isResizable: Boolean = false) {pack()this.isResizable = isResizablesetSize(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操作,由于兩個Action都存在圖片壓縮操作,為了復用就直接把圖片壓縮API調用的實現操作抽出封裝在ImageSlimmingHelper中。

  • ui包主要就是Swing框架中一些界面GUI的實現和交互。

四、實現的關鍵技術點

  • 關鍵點一: 插件開發中如何執行一個異步任務

IDEA Plugin開發和Android開發很類似,一些耗時的任務是不能直接在主線程執行的,需要在特定后臺線程執行,否則會阻塞主線程。在intellij open api中有個Task.Backgroundable抽象類就是處理異步任務的。Backgroundable繼承了Task類以及實現了PerformInBackgroundOption接口。具體使用很簡單傳入兩個參數一個是Project對象和一個執行異步中hint提示文本,有四個回調函數分別為run(progress: ProgressIndicator)、onSuccess、onThrowable、onFinished.最后通過queue方法加入到異步任務隊列中。為了方便調用將其封裝成一個擴展函數來使用。

//創建后臺異步任務的Project的擴展函數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 = {//執行圖片壓縮操作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存在異常,請重新輸入")})
復制代碼
  • 關鍵點二: 插件開發中如何獲取當前選中的文件或目錄

在插件開發中如何獲得當前選中文件,實際上open api提供了類似DataContext數據上下文環境,我們需要去拿到文件集合對象就需要先找到文件管理的窗口對象,還記得上篇博客中說到的AnActionEvent對象是插件與IDEA交互通信的一個媒介,通過AnActionEvent內部的dataContext的getData方法,傳入對應的DataKey對象獲得相應的窗口對象。在CommonDataKey中有一個DataKey<VirtualFile[]>,通過傳入當前event中的dataContext對象即可獲得當前選中的文件對象集合。

    private fun DataContext.getSelectedFiles(): Array<VirtualFile>? {return DataKeys.VIRTUAL_FILE_ARRAY.getData(this)//右鍵獲取選中多個文件,擴展函數}
復制代碼
  • 關鍵點三: Swing中JFileChooser組件的使用

關于JFileChooser組件的使用就比較簡單了,這里就不去詳細介紹,代碼也很簡單

  private void openFileAndSetPath(JComboBox<String> cBoxPath, int selectedMode, Boolean isSupportMultiSelect) {JFileChooser fileChooser = new JFileChooser();fileChooser.setFileSelectionMode(selectedMode);fileChooser.setMultiSelectionEnabled(isSupportMultiSelect);//設置文件擴展過濾器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);}}}
復制代碼
  • 關鍵點四: api key的驗證和圖片壓縮的實現

在進行圖片壓縮前就是需要去驗證一下TingPng ApiKey的合法性,如果第一次驗證合法就需要把該ApiKey存儲在本地,下次壓縮就直接使用本地的key進行壓縮,一旦本地key失效后,需要重新彈出TinyPng apikey 的驗證提示框,進行重新認證。當然需要注意的是驗證api key的合法性也是進行一次同步的網絡請求所以它也要放在異步任務執行。

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驗證失敗,請重新輸入")})
}
復制代碼

然后就是利用異步任務進行圖片壓縮操作。

fun slimImage(project: Project?,inputFiles: List<File>,model: ImageSlimmingModel = ImageSlimmingModel("", "", "", ""),successAction: (() -> Unit)? = null,outputSameFile: Boolean = false,failAction: ((String) -> Unit)? = null
) {project?.asyncTask(hintText = "正在壓縮", runAction = {//執行圖片壓縮操作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存在異常,請重新輸入")})
}
復制代碼

五、總結

到這里《用Kotlin擼一個圖片壓縮插件》系列文章就結束了,其實實現起來挺簡單的,其中主要的關鍵點就是需要更加熟悉使用Intellij open api, 然后其他就是運用好Kotlin的一些語法特性,其余的都很簡單。而且個人覺得把圖片壓縮做成一個插件會變得很高效,不然傳統的模式得需要把圖片拖到瀏覽器中然后一個一個下載下來,還有的人問我不就是一個腳本能解決的嗎?腳本個人覺得不夠靈活不能像插件一樣任意在項目中選中一張或多張圖片直接右鍵壓縮。如有什么問題歡迎下方留言,謝謝。

插件項目源碼地址

歡迎關注Kotlin開發者聯盟,這里有最新Kotlin技術文章,每周會不定期翻譯一篇Kotlin國外技術文章。如果你也喜歡Kotlin,歡迎加入我們~~~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450875.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450875.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450875.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

后端進階技術總結

1、SDK與API的區別&#xff1f; SDK是Software Development Kit的縮寫&#xff0c;即軟件開發工具包。可以把SDK想象成一個虛擬的程序包&#xff0c;在這個程序包中有一份做好的軟件功能&#xff0c;這份程序包幾乎是全封閉的&#xff0c;通過接口聯通外界&#xff0c;相應的接…

最簡容器化動手小實踐——再戰flappybird

《Flappy Bird》是一名越南開發者所開發的游戲&#xff0c;這款游戲的主要內容是幫助一只小鳥穿越水管的層層阻礙&#xff0c;玩家所需要的只是點擊屏幕從而調整小鳥的高度。而令這款游戲與眾不同的是&#xff0c;這款游戲的難度夸張的驚人&#xff0c;大多數玩家在初次上手之后…

為什么電影電視幀率不取整數?

英文名稱&#xff1a;time code時間碼概念時間碼&#xff08;time code&#xff09;是攝像機在記錄圖像信號的時候&#xff0c;針對每一幅圖像記錄的唯一的時間編碼。一種應用于流的數字信號。該信號為視頻中的每個幀都分配一個數字&#xff0c;用以表示小時、分鐘、秒鐘和幀數…

linux下用rpm 安裝jdk

1.下載jdk的rpm安裝包&#xff0c;這里以jdk-7u4-linux-i586.rpm為例進行說明 下載地址&#xff1a;http://www.oracle.com/technetwork/java/javase/downloads/index.html 2. 將jdk-7u4-linux-i586.rpm 移動到合適的安裝目錄上&#xff0c;安裝軟件不要在/home路徑下&#xf…

FFT 入門

推薦博客 &#xff1a; https://oi.men.ci/fft-notes/ 卷積的理解 &#xff1a; https://www.zhihu.com/question/22298352?rf21686447 題目鏈接 &#xff1a;http://uoj.ac/problem/34 這是一道模板題。給你兩個多項式&#xff0c;請輸出乘起來后的多項式。輸入格式第一行兩個…

MPEG4視頻壓縮編碼技術詳解

MPEG全稱是Moving Pictures Experts Group&#xff0c;它是“動態圖象專家組”的英文縮寫&#xff0c;該專家組成立于1988年&#xff0c;致力于運動圖像及其伴音的壓縮編碼標準化工作&#xff0c;原先他們打算開發MPEG1、MPEG2、MPEG3和MPEG4四個版本&#xff0c;以適用于不同帶…

oracle orion hugepages_settings.sh(支持OEL 7,4.1內核)

orion需要首先配置hugepage&#xff0c;否則會出現下列錯誤。[rootyyxxdb01 ~]# /opt/app/11.2.0/grid_home/bin/orion -run oltp -testname mytestORION: ORacle IO Numbers -- Version 11.2.0.4.0************************ Large Pages Information *******************Param…

eclipse啟動出現“An Error has Occurred. See the log file”解決方法

見&#xff1a;http://blog.csdn.net/ww130929/article/details/52652222 這段時間開發java的項目&#xff0c;剛開始啟動Eclipse的時候經常遇到這個問題&#xff0c;寫這篇博客來記錄解決方法。 1.刪除工程目錄下的&#xff1a; “.metadata/.plugins/org.eclipse.core.resour…

初識NIO之Java小Demo

Java中的IO、NIO、AIO&#xff1a; BIO&#xff1a;在Java1.4之前&#xff0c;我們建立網絡連接均使用BIO&#xff0c;屬于同步阻塞IO。默認情況下&#xff0c;當有一條請求接入就有一條線程專門接待。所以&#xff0c;在客戶端向服務端請求時&#xff0c;會詢問是否有空閑線程…

RTP協議詳解

RTP協議分析 第1章. RTP概述 1.1. RTP是什么 RTP全名是Real-time Transport Protocol&#xff08;實時傳輸協議&#xff09;。它是IETF提出的一個標準&#xff0c;對應的RFC文檔為RFC3550&#xff08;RFC1889為其過期版本&#xff09;。RFC3550不僅定義了RTP&#xff0…

線程狀態轉換

一、線程狀態轉換 新建&#xff08;New&#xff09; 創建后尚未啟動。 可運行&#xff08;Runnable&#xff09; 可能正在運行&#xff0c;也可能正在等待 CPU 時間片。 包含了操作系統線程狀態中的 Running 和 Ready。 阻塞&#xff08;Blocking&#xff09; 等待獲取一個排它…

Eclipse中啟動tomcat報錯java.lang.OutOfMemoryError: PermGen space的解決方法

見&#xff1a;http://outofmemory.cn/java/OutOfMemoryError/outofmemoryerror-permgen-space-in-tomcat-with-eclipse 有的項目引用了太多的jar包&#xff0c;或者反射生成了太多的類&#xff0c;異或有太多的常量池&#xff0c;就有可能會報java.lang.OutOfMemoryError: Per…

MPEG-4 AVC/H.264 信息

作者&#xff1a;haibara 來源&#xff1a;pcicp.com 本FAQ由&#xff08;haibara&#xff09;翻譯&#xff0c;期間受到kaito_mkid&#xff08;pcicp&#xff09;幫助&#xff0c;在此感謝&#xff0c;由于Newbie的關系&#xff0c;如有翻譯錯誤&#xff0c;還請各位指出&…

eclipse搜索關鍵字

見&#xff1a;https://jingyan.baidu.com/article/e6c8503c1a60d2e54f1a18e3.html

裝飾器語法糖運用

裝飾器語法糖運用 前言&#xff1a;函數名是一個特性的變量&#xff0c;可以作為容器的元素&#xff0c;也可以作為函數的參數&#xff0c;也可以當做返回值。閉包定義&#xff1a; 內層函數對外層函數&#xff08;非全局&#xff09;變量的引用&#xff0c;這個內層函數就可以…

fb 4.7英文版 顯示行數

窗口&#xff08;window&#xff09;首選項&#xff08;Preference&#xff09;—>常規&#xff08;General&#xff09;—>編輯器&#xff08;Editors&#xff09;—>文本編輯器&#xff08;Text Editors&#xff09;—>“顯示行號”&#xff08;Show line number…

集市中迷失的一代:FreeBSD核心開發者反思開源軟件質量

摘要&#xff1a;本文作者Poul-Henning Kamp (phkFreeBSD.org) &#xff0c;26年的計算機程序員&#xff0c;他編寫的軟件以底層構建塊的形式廣泛被開源和商業產品采用。講述作者在看完《設計原本》這本書后所引發的共鳴&#xff01; 13年前&#xff0c;新興的草根開源軟件運動…

點擊表格彈窗獲取另外一套數據之后,原表格相關數據的調用

用H5新屬性&#xff0c;data-*&#xff0c; $獲取方式&#xff1a; 待續。。。。。。。 轉載于:https://www.cnblogs.com/He-tao-yuan/p/9888316.html

谷歌瀏覽器如何如何禁用彈出窗口阻止程序

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 在工具欄上使用 Google Chrome 菜單。工具欄上的菜單位于瀏覽器右上角。 選擇“設置”。 在頁面底端找到并點擊“顯示高級設置”。 在“隱…

Python 3 入門,看這篇就夠了

文章目錄 簡介基礎語法運算符變量數據類型流程控制迭代器生成器函數 自定義函數參數傳遞 可更改與不可更改對象參數匿名函數變量作用域模塊面向對象錯誤和異常文件操作序列化命名規范參考資料簡介 Python 是一種高層次的結合了解釋性、編譯性、互動性和面向對象的腳本語言。Pyt…