版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、Android 移動開發(fā)基礎教程(慕課版)劉 剛 主 編高偉南 副主編新一代信息技術(shù)“十三五”系列規(guī)劃教材第8章 高級編程 前面幾章我們介紹了 Android 開發(fā)的一些基礎知識,本章我們將會在此基礎上做一些拓展,如網(wǎng)絡編程。對于一個 Android 設備來說,網(wǎng)絡請求是很常見的操作,所以掌握一些網(wǎng)絡編程的知識是必要的。另外,在 UI 方面,Android 系統(tǒng)提供了一些動畫的 API,可以提高用戶體驗。在與用戶的交互中,如果一個操作比較耗時,可能會造成用戶長時間等待的現(xiàn)象,甚至會因為 ANR(Application Not Responding)造成應用異常退出。本章我們會介紹如何使用線程做
2、耗時的操作,另外還會介紹 Fragment 和 RecycleView 組件。8.1 網(wǎng)絡編程 網(wǎng)絡編程是指多個設備之間通過網(wǎng)絡進行數(shù)據(jù)交換,網(wǎng)絡通信基于“請求-響應模型”,即一臺設備發(fā)送通信請求,另一臺設備進行反饋。發(fā)送請求端稱為客戶端,響應請求端稱為服務端。例如常見的 QQ 程序,用戶打開 QQ 客戶端程序之后,輸入賬號和密碼,再單擊“登錄”,即向騰訊服務端發(fā)送登錄請求,服務端把請求結(jié)果反饋到客戶端。Android 是基于 Java進行開發(fā)的,所以 JDK 中關(guān)于網(wǎng)絡編程的 API 在 Android 中均可使用。8.1.1 TCP、UDP 協(xié)議基礎 兩臺設備之間進行通信,一定要有通信協(xié)議
3、,即客戶端以一定的格式將數(shù)據(jù)發(fā)送出去,服務端接收到數(shù)據(jù)之后,可以根據(jù)同樣的協(xié)議將數(shù)據(jù)的內(nèi)容解析出來。在現(xiàn)有的網(wǎng)絡中,通信方式有兩種:TCP 和 UDP。TCP 是傳輸控制協(xié)議,提供的是面向連接、可靠的字節(jié)流服務,通信雙方必須先建立一個 TCP 連接,然后才能傳輸數(shù)據(jù)。而且 TCP 協(xié)議還提供了超時重發(fā)、數(shù)據(jù)校驗、擁塞控制等功能,保證了數(shù)據(jù)的可靠傳輸。UDP 是用戶數(shù)據(jù)報協(xié)議,只是簡單地把數(shù)據(jù)報發(fā)送出去,但是并不能保證數(shù)據(jù)能到達目的地,不提供可靠性。所以 TCP 應用于需要安全可靠傳輸數(shù)據(jù)的場景,但是開銷比較大。UDP 應用于對數(shù)據(jù)可靠性要求不是太高的傳輸,其優(yōu)點是開銷小,另外 UDP 沒有數(shù)據(jù)
4、校驗、擁塞控制等操作,故而傳輸比較快。 對于上層應用來說,無論是 TCP 還是 UDP,通信都包括客戶端和服務端,處理流程也具有一般性,具體如表 8.1 所示。表 8.1 客戶端和服務端的處理流程8.1.2 Socket 通信 對于 TCP 和 UDP 的通信,Java 均有對應的 API?,F(xiàn)在我們以 TCP 為例,通過一個實例說明網(wǎng)絡通信的方式。案例 8.1 實現(xiàn)網(wǎng)絡通信 對于TCP通信,Java 中使用 Socket 類做客戶端開發(fā),使用 ServerSocket 做服務端開發(fā)。8.1.3 下載網(wǎng)絡資源 在 Android 開發(fā)中,經(jīng)常需要請求網(wǎng)絡資源,例如播放在線音樂,或者加載顯示一張網(wǎng)
5、絡圖片。 Java 提供了 HttpURLConnection 和 HttpsURLConnection,兩者都可以基于 URL 實現(xiàn)簡單的請求響應功能,區(qū)別在于是訪問 http 鏈接還是訪問 https 鏈接。案例 8.2 下載網(wǎng)絡圖片 本例通過 HttpURLConnection 獲取網(wǎng)絡圖片。案例分析 代碼中首先根據(jù)網(wǎng)絡圖片的鏈接地址創(chuàng)建了一個 URL 對象,然后調(diào)用 openConnection()方法開啟一個連接,接著設置相關(guān)參數(shù),如超時時間等。然后調(diào)用 getInputStream()方法獲取輸入流,后續(xù)就是輸入/ 輸出流的基本處理,可以調(diào)用 BitmapFactory 的 dec
6、odeStream()方法將輸入流解析成 Bitmap 圖片。8.2 圖形圖像和動畫 對于一個應用來說,圖片是一種很豐富的表達形式。Android 中也為圖片的處理提供了大量的 API,不僅包括圖片的顯示、繪制,還包括一些簡單的動畫效果。本節(jié)我們將介紹這些 API 的使用方法。8.2.1 Bitmap 和 BitmapFactory Android 中提供了 Bitmap 類用于圖片處理,一個 Bitmap 對象代表一張位圖,存儲了圖片的寬高、顏色、像素點等信息,Bitmap 類提供了大量的方法,其常見的方法如表 8.2 所示。表 8.2 Bitmap 常見的方法續(xù)表 BitmapFactor
7、y 主要用于加載 Bitmap 對象,可以從資源文件加載,也可以根據(jù)圖片的路徑進行加載,還可以根據(jù)輸入流對 Bitmap 對象進行解析,相應的方法如表 8.3 所示。表 8.3 BitmapFactory 的方法8.2.2 Android 繪圖基礎 除了顯示已有的圖片之外,Android 還支持一些簡單的二維繪圖,其實對于 Android 的一些基本組件,如 TextView、Button 等,也都是系統(tǒng)繪制出來的,繪制的操作在 View 類的 onDraw(Canvas canvas)方法中,每個組件需要實現(xiàn) onDraw(Canvas canvas)方法進行自定義的繪制。所以,Androi
8、d 的繪圖應該定義一個類繼承自 View 組件,并重新定義 onDraw (Canvas canvas)方法。其中,參數(shù) Canvas 可以理解為畫布,繪制操作均在 Canvas 上執(zhí)行。Canvas 支持的一些方法如表 8.4 所示。表 8.4 Canvas 類支持的操作舉例續(xù)表 從表 8.4 中可以看到,每一個方法中都包含一個 Paint 類的參數(shù),Paint 類代表畫筆,它指定了畫筆的顏色和粗細等,相關(guān)的方法如表 8.5 所示。表 8.5 Paint 類相關(guān)的方法案例 8.3 使用線性布局 例說明繪制 API 的方法,運行結(jié)果如圖 8.1 所示。圖 8.1 圖形的繪制8.2.3 補間動畫
9、 在 Android 應用中,經(jīng)常會出現(xiàn)一些動畫效果,例如控件的滑入滑出、圖片的漸隱等。常見的實現(xiàn)方式有補間動畫和屬性動畫,補間動畫是指開發(fā)者指定好控件的初始狀態(tài)和結(jié)束狀態(tài),系統(tǒng)自動補齊顯示控件的中間狀態(tài)。Android 補間動畫支持的效果比較簡單,包括平移、縮放、旋轉(zhuǎn)、透明度變化,對應的類如表 8.6 所示。表 8.6 補間動畫對應的類案例 8.4 使用補間動畫 本例在 Activity 上顯示一張圖片和 4 個按鈕,4 個按鈕分別用于觸發(fā) 4 種動畫,運行結(jié)果如圖 8.2 所示。圖 8.2 補間動畫實例8.2.4 屬性動畫 屬性動畫是在 API 11 之后加入的功能,它幾乎可以作用在任何對
10、象上,而且不同于補間動畫只能支持 4 種變換,屬性動畫是在一定時間內(nèi)將對象的屬性從一個初始值改變到另一個值,因此,只要是對象存在的屬性,無論是可見還是不可見的,都可以實現(xiàn)動畫效果。屬性動畫可以通過 ObjectAnimator實現(xiàn)。下面我們通過一個實例說明一下屬性動畫的具體屬性。案例 8.5 使用屬性動畫 本例在 Activity 中定義了兩個按鈕,一個用于觸發(fā)圖片的平移,另一個用于觸發(fā)圖片的透明度的變換。案例分析 單擊按鈕后,使用 ObjectAnimator 類實現(xiàn)屬性動畫,首先調(diào)用 ofFloat()方法獲取 ObjectAnimator 對象,ofFloat()方法第 1 個參數(shù)為需要
11、變換的對象,第 2 個參數(shù)為需要改變的屬性,“translationX”表示水平方向上的平移,“alpha”為透明度的改變,其他的還有“rotationX”“rotationY”等,設置好需要控制的對象和屬性之后,調(diào)用 setDuration()設置動畫執(zhí)行的時間,最后調(diào)用 start()方法執(zhí)行動畫。8.3 多媒體應用開發(fā) 在 Android 應用中,經(jīng)常需要播放媒體資源,如音樂播放器、視頻播放器和游戲的背景音樂等。Android 提供了 MediaPlayer 類,可以很簡單地實現(xiàn)播放本地存儲或者網(wǎng)絡上的音/視頻文件。8.3.1 MediaPlayer 類介紹 MediaPlayer 提供
12、了大量的方法可以控制音/視頻的播放、暫停、定位等,常見的方法如表 8.7 所示。表 8.7 MediaPlayer 類的方法續(xù)表 使用 MediaPlayer 播放音/視頻時,首先調(diào)用 setDataSource()方法設置需要播放的視頻源,然后調(diào)用 prepare()或 prepareAsync()方法做視頻播放的準備工作。兩個方法的區(qū)別在于 prepareAsync()是異步方法,不會阻塞程序的執(zhí)行,可以通過調(diào)用 setOnPreparedListener()方法監(jiān)聽播放器是否已經(jīng)準備好,準備好之后就可以調(diào)用 start()方法進行播放。8.3.2 使用MediaPlayer和Surfac
13、eView播放視頻 播放的視頻必須顯示在View上,而且視頻的畫面一直在改變,所以View需要一直重繪。對于這種需要不斷更新View內(nèi)容的場景,Android 提供了 SurfaceView 類 ,SurfaceView 關(guān)聯(lián)了一個SurfaceHolder 對象,專門用于繪制SurfaceView 的內(nèi)容。SurfaceHolder 會有 3 個回調(diào)方法反饋SurfaceView 的狀態(tài),具體如表 8.8 所示。表 8.8 SurfaceHolder 的回調(diào)方法案例 8.6 使用 MediaPlayer 和 SurfaceView 播放視頻 本例實現(xiàn)視頻播放,單擊“暫?!卑粹o時,暫停播放視頻
14、;單擊“播放”按鈕時,重新開始播放視頻。最終可以退出播放頁面。案例分析 實例中首先獲取了 SurfaceView 對象,然后調(diào)用 getHolder()方法獲取關(guān)聯(lián)的 SurfaceHolder 對象,設置監(jiān)聽事件,當 SurfaceView 創(chuàng)建完成之后將 SurfaceHolder 設置給 MediaPlayer。然后調(diào)用setDataSource()方法設置視頻源,為 MediaPlayer 設置監(jiān)聽事件。接著調(diào)用 prepareAsync()方法做MediaPlayer 的準備工作,準備完成之后會回調(diào) onPrepared()方法,在其中開始播放視頻。單擊“暫停”按鈕時,調(diào)用 paus
15、e()方法暫停播放視頻;單擊“播放”按鈕時,調(diào)用 play()方法重新開始播放視頻。 退出播放頁面時,調(diào)用 release()方法釋放 MediaPlayer 占用的資源。8.4 線程開發(fā) 線程在程序開發(fā)中是一個很重要的概念,在 Android 中,線程分為主線程和子線程。主線程又叫作 UI 線程,主要處理和界面有關(guān)的事情,用于界面的繪制和交互。對于用戶來說,隨時都有可能操作頁面,而且對響應速度要求較高,因此,主線程中不能做太耗時的操作,否則會給用戶視覺上造成卡頓的現(xiàn)象,甚至有可能會因為執(zhí)行阻塞產(chǎn)生 ANR(Application Not Responding)而導致應用異常退出。 Andro
16、id 基于 Java 開發(fā),所以對于線程也有很好的支持,除了 JDK 中支持的一些 API 之外,Android 也針對自身的機制做了一些拓展。下面我們將介紹兩個常用的類: AsyncTask 和 ThreadPoolExecutor。8.4.1 AsyncTask 及其使用 AsyncTask 是一個執(zhí)行異步操作的類,它在主線程中創(chuàng)建和觸發(fā),但是在子線程中執(zhí)行后臺任務,然后將執(zhí)行的進度和最終結(jié)果傳遞給主線程并在主線程中更新 UI。例如有一種很常見的操作,應用中如果要在 ImageView 控件中顯示一張網(wǎng)絡圖片,首先需要從網(wǎng)絡上下載圖片,然后顯示到控件上。圖片的下載依賴于網(wǎng)絡情況,如果在主線
17、程中執(zhí)行下載操作,可能會造成用戶長時間等待,并且不能及時響應用戶的操作。這時就可以使用 AsyncTask類在子線程中執(zhí)行下載操作,然后通知主線程下載完成并顯示。AsyncTask有 3 個主要的回調(diào)方法,如表 8.9 所示。表 8.9 AsyncTask 的主要回調(diào)方法8.4.2 ThreadPoolExecutor 介紹 當一個應用中需要創(chuàng)建多個線程時,可以將其放入線程池中進行管理,Java 中使用 Executor 做線程池的管理和線程的調(diào)度。Executor 是一個接口,它的實現(xiàn)類為 ThreadPoolExecutor。創(chuàng)建 ThreadPoolExecutor 對象時,可以向構(gòu)造函
18、數(shù)傳入一系列的參數(shù)來配置線程池。常用的構(gòu)造函數(shù)有 ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue workQueue, ThreadFactory threadFactory)。其中,corePoolSize是指線程池的核心線程數(shù),maximumPoolSize指線程池可以容納的最大的線程個數(shù),keepAliveTime指非核心線程在空閑時可以存活的時間,TimeUnit是參數(shù)keepAliveTime的單位,workQueue是
19、指線程隊列,ThreadFactory指線程工廠,用于線程池創(chuàng)建線程。為了幫助讀者理解以上各個參數(shù)的含義,下面我們說明一下線程池的工作步驟。(1)當調(diào)用了 ThreadPoolExecutor 的 execute()方法要求執(zhí)行一個任務時,線程池會判斷當前的線程個數(shù)是否超過核心線程數(shù),如果沒有超過,則啟動一個核心線程來執(zhí)行任務。(2)如果當前的線程個數(shù)超過了核心線程的個數(shù),則將任務放到線程隊列中排隊等候。(3)如果線程隊列已滿,則啟動一個非核心線程來執(zhí)行任務。(4)如果線程的總個數(shù)已經(jīng)達到了線程池最大的線程個數(shù),則拒絕接收任務。(5)如果線程池有線程處于空閑狀態(tài),則在指定 keepAliveT
20、ime 后回收線程,當線程個數(shù)等于核心線程數(shù)后,停止回收線程。8.5 Fragment 為了更加動態(tài)和靈活地支持 UI 設計,Android 在 API 11 版本之后引入了 Fragment,它可以將 UI 碎片化,也可以被復用。Fragment 必須顯示在 Activity 中,可以被動態(tài)地添加、移除、替換,但是每個 Fragment 也都具有自己的生命周期方法,并且可以各自處理用戶的輸入事件。常見的微信界面就可以使用 Fragment 來實現(xiàn),如圖 8.3 所示,單擊下面的“微信”,上面就會對應顯示聊天記錄,單擊“通訊錄”,上面就會顯示聯(lián)系人列表。如果使用 Activity 實現(xiàn)比較麻煩
21、,但是如果使用 Fragment 實現(xiàn)就比較簡單,界面的整體定義為一個 Activity,中間的內(nèi)容區(qū)域設計為一個 Fragment,可以根據(jù)用戶單擊的 Item 動態(tài)地更換顯示區(qū)域的 Fragment。圖 8.3 微信界面8.5.1 Fragment 的創(chuàng)建 和 Activity 類似,創(chuàng)建自定義的 Fragment 需要繼承自 Fragment 類,并實現(xiàn)父類的相關(guān)回調(diào)方法。其中比較常見的是 onCreate()、onCreateView()、onPause()方法。onCreate()方法在創(chuàng)建 Fragment 時會回調(diào);onCreateView()在繪制 Fragment 視圖的時候
22、會回調(diào),開發(fā)者需要在該方法中加載 Fragment 需要顯示的布局文件;onPause()方法在用戶離開 Fragment 時會回調(diào)。另外,Android 通過 FragmentManager 類管理在Activity 中的 Fragment,具體的操作在 FragmentTransaction 中。首先可以通過 getFragmentManager()方法獲取 FragmentManager 對象,然后調(diào)用 FragmentManager 的 beginTransaction()開啟一個事務執(zhí)行具體的操作。FragmentTransaction 類支持的常見操作如表 8.10 所示。表 8.
23、10 FragmentTransaction 類支持的操作案例 8.7 單擊底部按鈕,上面內(nèi)容區(qū)域動態(tài)改變 本例運行結(jié)果如圖 8.4 所示,單擊下面的按鈕,上面的內(nèi)容會動態(tài)改變。圖 8.4 Fragment 實例案例分析 本例為每一種內(nèi)容都定義了一個Fragment,在Fragment的onCreateView()方法中可以加載自己想要顯示的布局文件,布局的處理并無特殊。然后在 Activity 中可以動態(tài)地控制 Fragment 的顯示,非常靈活。8.5.2 Fragment 的生命周期 Fragment 有自己的生命周期,但是其生命周期又依賴于 Activity。當 Activity 處于
24、不可見狀態(tài)時,F(xiàn)ragment 也一定處于不可見狀態(tài)。當 Activity 處于銷毀狀態(tài)時,F(xiàn)ragment 也一定處于銷毀狀態(tài)。 Fragment 在各個生命周期階段也有不同的回調(diào)方法,具體流程如圖 8.5 所示。圖 8.5 Fragment 的生命周期回調(diào)方法 從圖 8.5 中可以看出,F(xiàn)ragment 的生命周期方法有些和 Activity 一致,意義也一致。表 8.11 用于說明 Fragment 特有的生命周期方法的含義。表 8.11 Fragment 的生命周期方法說明續(xù)表案例 8.8 通過日志打印看 Fragment 生命周期方法的回調(diào)順序 本例在Activity中顯示兩個按鈕,
25、單擊其中一個按鈕,將一個Fragment 添加到 Activity 中,單擊另外一個按鈕,將 Fragment 從 Activity 中移除,界面如圖 8.6 所示。圖 8.6 Fragment 生命周期實例界面案例分析 單擊 Activity 中的“add”按鈕,將 Fragment 添加到 Activity 中,控制臺打印如圖 8.7 所示。依次回調(diào)了 onAttach()、onCreate()、onCreateView()、onActivityCreated()、onStart()、onResume()方法,F(xiàn)ragment處于前臺可見。 按“Home”鍵,使 Activity 處于后臺
26、不可見狀態(tài),F(xiàn)ragment 也隨之處于不可見狀態(tài),控制臺打印如圖 8.8 所示。依次回調(diào)了 onPause()和 onStop()方法。 單擊 Activity 中的“remove”按鈕,將 Fragment 移除,控制臺打印如圖 8.9 所示,依次回調(diào)了onPause()、onStop()、onDestroyView()、onDestroy()、onDetach()方法。圖 8.7 添加 Fragment圖 8.8 按“Home”鍵圖 8.9 Fragment 銷毀8.6 RecyclerView 在 Android 應用中,很多時候需要顯示大量的數(shù)據(jù),例如音樂播放器的歌曲列表,手機中可能
27、存在大量的歌曲文件,但是同一時刻屏幕上只顯示部分歌曲,用戶可以通過上下滑動查看更多音樂。這種應用場景可以使用 2.2 節(jié)介紹的 ListView 和 GridView 實現(xiàn)布局。 但是,Android 還提供了一種更靈活的組件RecyclerView,它可以動態(tài)地實現(xiàn)列表布局或網(wǎng)格布局,甚至每個 Item 都可以顯示不同的布局文件。而且,RecyclerView 內(nèi)部還實現(xiàn)了 View 的復用,在用戶上下滑動列表或網(wǎng)格時,RecyclerView 并不會為每一個子項創(chuàng)建一個 View,而是創(chuàng)建若干個 View 不斷復用,更新其中顯示的數(shù)據(jù)。8.6.1 RecyclerView 相關(guān)類 在 Re
28、cyclerView 視圖中,每個子項都被表示為 ViewHolder 對象,開發(fā)者必須繼承RecyclerView.ViewHolder 類實現(xiàn)自定義的 ViewHolder 類。每個 ViewHolder 都可以有一個布局文件用于顯示一個子項。例如,如果用 RecyclerView 顯示音樂列表,則每一個 ViewHolder 可以代表一首歌曲,用來顯示歌曲的歌手信息、時長信息等,還可以響應單擊和長按事件等。 RecyclerView 通過 Adapter 管理 ViewHolder,開發(fā)者必須繼承 RecyclerView.Adapter類實現(xiàn)自定義的 Adapter。Adapter中有很多回調(diào)方法,其中onCreateViewHolder()方法用于創(chuàng)建一個對應的ViewHolder,onBindViewHolder()用于將數(shù)據(jù)和 ViewHolder 進行綁定 RecyclerView使用LayoutManager對其中的數(shù)據(jù)進行排列,開發(fā)者可以使用系統(tǒng)提供的LayoutManager,也可以自定義LayoutManager類實現(xiàn)自己想要的排列方式。系統(tǒng)提供的LayoutManager有3種,分別是 LinearLayoutManager、GridLayoutMananger 和StaggeredGri
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度林產(chǎn)品加工與許可經(jīng)營合同2篇
- 二零二五年度俄語企業(yè)內(nèi)部培訓翻譯合同
- 二零二五年度房地產(chǎn)廣告經(jīng)紀服務合同3篇
- 2025年度消防工程清包及消防產(chǎn)品采購合同范本3篇
- 海南醫(yī)學院《法律社會學》2023-2024學年第一學期期末試卷
- 海南師范大學《建筑物理(熱)》2023-2024學年第一學期期末試卷
- 二零二五年度數(shù)據(jù)中心專用個人機柜租賃及云服務接入合同3篇
- 二零二五年度杭州建筑工程裝修設計與施工合同3篇
- 數(shù)據(jù)庫系統(tǒng)課程設計任務書(知識研究)
- 網(wǎng)頁設計課程設計成品
- 落實國家組織藥品集中采購使用檢測和應急預案
- 報價經(jīng)理崗位職責
- 裝飾裝修施工及擔保合同
- 《廣東省普通高中學生檔案》模板
- 公司章程范本下載
- GB/T 41120-2021無損檢測非鐵磁性金屬材料脈沖渦流檢測
- 青年心理學第五講(戀愛心理)
- ITV系列電氣比例閥英文說明書
- SL 537-2011 水工建筑物與堰槽測流規(guī)范
- 質(zhì)量管理-AQL抽樣基礎知識培訓課件
- 《普通話》教學講義課件
評論
0/150
提交評論