Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發之WebView知識和常見問題

Android開發之WebView知識和常見問題

編輯:關於Android編程

一、前言

最近在學習HTML5相關的知識,發現前端技術的功能越來越強大了,很多功能如果我們通過原生代碼的形式進行實現的話相對於H5會花費數倍的時間,在最求快速迭代的時候是不可取的,再一個就是現在App的種類也越來越多了。

以前一提起Android開發,我們就會想到Java,一提起IOS我們就會想到Object-c、swift,可是技術的進步速度之快讓人難以想象,現在市面上已經有非常多的技術可以用來進行移動開發,這裡就列舉幾種我所知道的技術,對於前端開發工程師而言,我們可以使用JqueryMobile來開發一個WebApp、利用AngularJs加cordova加PhoneGap開發一個可跨平台的純WebApp,或者結合Java原生開發一個Hybird混合App。

使用H5開發App有一個巨大的優勢就是它開發的產品可以跨平台,並且開發成本低,而且我們進行開發的時候有很多的前端開源框架可以讓我們很容易的寫出許多漂亮的頁面,對於一個WebApp最重要的非WebView莫屬了,一個WebView的性能好壞,因此在當前下,同一款App往往在Ios系統上的性能強於Android就是這個原因,有的程序為了App減少平台帶來的差異還在App中加入了自己的WebView。那麼我們就來看看WebView這個核心控件。

二、WebView的介紹

A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses the WebKit rendering engine to display web pages and includes methods to navigate forward and backward through a history, zoom in and out, perform text searches and more.

上文是Google對於WebView的介紹,一句話它就是一個顯示網頁的控件,並且可以簡單的顯示一些在線的內容,並且基於WebKit內核。

在Android4.4(API Level 19)引入了一個新版本的基於Chromium的Webview,這讓我們的的WebView能支持HTML5和CSS3以及JavaScript。

注意:用於Webview的升級,對於我們的程序帶來了一些影響,如果我們的targetSdkVersion設置的是18或則更低,single and narrow column和default zoom levels不在支持。並且在Android4.4中我們可以通過設置setWebContentDebuggingEnabled()方法讓我們的程序可以進行遠程桌面調試。

三、WebView的使用及常見坑

我們就拿如何在一個Activity中顯示一個WebView來說吧,我們可以通過創建一個WebView對象:
這裡寫圖片描述
或則在布局文件中定義,再在主界面中findViewById:
這裡寫圖片描述
在或則我們還可以創建一個Fragment類繼承WebViewFragment:
這裡寫圖片描述
當然這對於我們來說也只是創建了一個WebView,接下來我們就看看如何使用它,讓它顯示我們豐富的內容。

Google為我們提供了幾種讓WebView記載數據的方法,我們就簡單的進行介紹下:

這裡寫圖片描述

1.loadData()方法:

當我們不需要加載一整個的HTML頁面而是加載一小段的類容時,此時我們就可以使用這個方法,他需要我們傳入三個參數,第一個就是我們需要Webview展示的內容,第二個是我們告訴WebView我們展示內容的類型,一般,第三個是字節碼(這貨就是一個坑,我們下面會介紹)。

說完介紹我們來看一下如何使用它,我們看一個簡單的例子:

這裡寫圖片描述

我們讓WebView加載一個簡單的富文本標簽,看看運行過後的效果:

這裡寫圖片描述

What?怎麼是亂碼,我們明明指定了編碼格式為UTF-8啊,可是現實是殘酷的,那下面我們來看看解決辦
法:
這裡寫圖片描述

這裡寫圖片描述

這下我們終於看到了正確的內容,Google還提出了,在這種加載的方法下,我們的Data數據裡不能出現’#’, ‘%’, ‘\’ , ‘?’ 這四個字符,如果出現了我們要用%23, %25, %27, %3f,這些字符來替換,我在測試的過程中沒有遇到錯誤,當以後遇到類似情況時可以用於排查問題,下面附上網上以為為將特定字符轉義過程中遇到的現象:

   A)   %,會報找不到頁面錯誤,頁面全是亂碼。亂碼樣式見符件。
   B)   #,會讓你的goBack失效,但canGoBAck是可以使用的。於是就會產生返回按鈕生效,但不能返回的情況。
   C)   \ 和? 我在轉換時,會報錯,因為它會把\當作轉義符來使用,如果用兩級轉義,也不生效。

我們在使用loadData時,就意味著需要把所有的非法字符全部轉換掉,這樣就會給運行速度帶來很大的影響,因為在使用時,在頁面stytle中會使用很多%號。頁面的數據越多,運行的速度就會越慢。

2.laodDataWithBaseUrl():

其實這個方法的用處和我們上面的方法差不多,但是多出了兩個參數,BaseUrl和historyurl,那它們的區別在什麼地方了,我們設想一下這樣的場景,在一些第三方的資源存儲網站中,當我們需要用它來托管我們上傳的例如圖片的資源時,上傳成功後它會給我們返回一個key而不是一個完整的路徑,如果我們此時只有請求數據後手動拼接嗎?loadDataWithBaseUrl()為我們提供了解決方案,同樣的看代碼:

這裡寫圖片描述

我們從網上找了一個圖片,並將它的地址進行拆分模擬上訴的情況,然後在顯示的內容中定義一個IMG標簽顯示我們的圖片,把基地址傳遞給baseUrl,效果如下:

這裡寫圖片描述

可以看到我們的圖片正常顯示出來了,那我們就來介紹下historyUrl,這個屬性要想測試還得費不小的勁,我們假設一個這樣的需求,當我們先通過loadDataWithBaseUrl()時,頁面當中有一個A標簽會跳轉到其它的頁面,那麼我們返回頁面時應該回到前一個我們加載的頁面,可是真的如我們所想嗎?我們來看一下代碼的實現和效果:

這裡寫圖片描述

注意:如果我們直接這樣寫的話,我們是不可能看到現象的,我們還需要做額外的兩部操作,因為,當我們的在網頁中點擊一個鏈接的時候,WebView會去啟動一個外部的程序來打開我們的頁面,這是我們不願意看到的,所以我們需要處理,還有就是當我們按返回按鈕的時候,我們希望WbView當有可以後退的歷史頁時是返回上一頁而不是直接退出程序,解決代碼如下:

這裡寫圖片描述

這裡寫圖片描述

當實現了上面的步驟後,我以為成功了,可是事實又給了我一棒,當我返回的時候看到的不是我加載的頁面,而是一個大白屏,這是什麼鬼啊,查文檔才發現第二種加載數據的方式不會緩存我們的歷史記錄,所以為我們提供了historyUrl的參數就是讓我們返回時不至於看到大白屏,如果我們不指定這個參數的話,它會默認是”about:blank”,這就是為啥看到大白屏。

這裡寫圖片描述

當我們為其添加一個參數過後,我們返回時就可以看到一個我們指定的頁面了,所以,你可以將之前的加載頁面進行存儲,具體怎麼操作你可以自己發揮。

3.LoadUrl():

這個方法太簡單了,我們就不介紹了,參數傳遞一個我們想加載的合法url就行了,本地的也行,注意路徑,比如assets下的文件路徑就是:

file:///android_asset/  開頭

4.loadUrl (String url, Map additionalHttpHeaders)

這個方法和前一個方法類似,只是而外給我們提供了一種可以設置請求頭的方式,當我們設置的請求頭與默認的請求頭沖突的時候,我們會覆蓋掉默認的值。

說完了記載內容,我們再來看一看一些使用WebView時的常見設置:

1)mWebView.getSettings().setJavaScriptEnabled(false);   
設置了這個屬性後我們才能在WebView裡與我們的Js代碼進行交互,對於WebApp是非常重要的,默認是false,因此我們需要設置為true
2)mWebView.getSettings().setSupportZoom(false);
     這個是設置我們的頁面是否支持縮放,默認也是允許的。
3)mWebView.getSettings().setBuiltInZoomControls(false);
設置是否顯示縮放控件,默認是false。
4)mWebView.getSettings().setDefaultFontSize(18);
設置默認的顯示字體,默認是16,有效值是1-72之間。

在我們寫布局文件的時候,有時我們為了讓我們的WebView與屏幕的四周有一定的邊距,我們通常會設置一個padding值,但是我們會發現這樣其實並不能達到我們想要的效果,反而移動了我們滾動條的位置,此時我們一般會在Wbview的外層包裹一層布局來實現這樣的效果,或則修改本地的CSS代碼。
為了美觀,我們還會設置我們滾動條的樣式,通過設置:android:scrollbarStyle 為insideOverlay 或則outsideOverlay,或則在代碼中設置:
這裡寫圖片描述
這裡寫圖片描述
有時在我們的程序中,我們需要為WebView設置一個我們自定義的背景而不現實默認的大白屏或則大黑屏,我們肯定會想到去設置WebView的背景:
這裡寫圖片描述
可是事實是沒有任何變化,WebView的背景還是原來的樣子,那我們查詢網絡,發現有人說還需添加一個把硬件加速關閉的代碼:
這裡寫圖片描述
這樣就好了嗎?看來還是我們想多了,依舊是大白屏,難道沒有辦法了嗎?最終我們通過在代碼中寫入如下代碼:
這裡寫圖片描述

問題最終得以解決,在我的手機上只需要第一行代碼就可以達到效果了,加第二行代碼是怕遇到一些奇葩的手機。

我們再來看之前遇到過的一個問題,在一個很簡單的Activity中,這個Activity主要用來顯示從網絡請求後顯示一些詳情信息,可能返回的是富文本或者一個Html頁面,具體的內容大小是不確定的,這個頁面還有兩個標題欄,一個主標題和一個副標題,他們的顯示會根據實際的情況進行顯示或則隱藏。

那我們看下出現的問題,在大多數的手機上都能正常的顯示,而在一款性能較差的三星手機上就出現了問題,Webview的區域超過了布局文件中的區域,將標題欄的部分也進行了覆蓋,導致一種標題欄顯示不出來的現象,進過分析和定位,我們確定了問題的所在,主要有兩方面的原因:

1.整個頁面的大小不確定,需要在代碼中動態的控制控件的顯示和隱藏,導致一些性能差的手機在畫面的渲染上出現問題

2.Webview顯示的內容是網絡請求的數據,有延遲也會對界面的繪制帶來問題

那我們就來看一下解決辦法:
這裡寫圖片描述
我們通過開啟JavaScript接口進行調試,動態的設置我們的WebView的區域大小來解決View繪制時的問題,在這過程中我們還以先讓Webview不可見,等數據加載成功後再讓其顯示。

接下來看看Webview中兩種“客服端”

1.WebViewClient

它主要為我們提供了各種通知事件和請求事件,比如網頁開始加載,網頁加載結束,當然還有我們對鏈接的處理。

這裡需要注意的地方就是如果我們不覆寫shouldOverrideUrlLoading這個方法的話,我們點擊一個鏈接的時候就會默認開啟一個外部的浏覽器加載網頁,所以值得注意,還有就是當我們需要加載一個JavaScript命令的時候,我們需要在該客服端的onPageFinished方法中去執行。

2.WebChromeClient

它主要用於一些Web頁面中的彈出事件傳遞給我們Native部分,讓我們可以使用自定義的形式進行顯示
這裡寫圖片描述
上圖中就是一個處理Alert事件的回調,讓我們用一個Toast顯示,值得注意的一點是,當我們忘記設置result.confirm()的時候,我們發現Toast後我們的Web頁面無法滑動了,而且還會出現該回調函數只會執行一次等異常錯誤的出現,所以得多加留意。

WebView與JS交互

我們都知道我們的Native是可以和web頁面中的JS進行交互的,而且方法都差不多,那我們就來看看他們的差別和使用場景吧

首先我們必須設置
這裡寫圖片描述
設置後,我們會得到如下的提示,說會有危險,我們後面會介紹一種辦法
這裡寫圖片描述

Webview裡面的界面調用Native Java代碼
首先我們需要定義一個類,在這個類中定義一些方法供我們的Js調用:
這裡寫圖片描述
值得注意的是,在Android4.2開始,我們必須田間@JavascriptInterface注解,我們的代碼才能夠被Js所調用。
然後我們就需要把這個接口類傳遞給Webview,然Js可以調用:
這裡寫圖片描述
這個方法的第二個參數是任意一個String類型的值,這個字會關系到我們後面調用接口中的方法。
下面就是我在Html頁面中定義了一個Button,讓點擊的時候調用Native的Toast:
這裡寫圖片描述
然後就是按鈕,我們綁定了它的點擊事件:
這裡寫圖片描述

2.Native調用Webview中的Js
還記得我們前面講過的WebViewClient嗎?我們這裡就需要先實現它,然後在它的onPagerFinish()回調函數中進行處理,這樣是為了保證我們代碼的正常運行,因為在以往的時候,當我們沒有在這個回調當中執行操作,而是直接在LoadUrl()後就對頁面進行操作,結果發現我們的代碼沒有起作用,最後發現是頁面沒有加載完成,而我們就去執行代碼,所以造成了這種沒必要的操作。
為了測試Native調用Js,我們現在Html頁面中定義一個方法和一個Span標簽:
這裡寫圖片描述
在方法中我們讓調用show()方法後,將Span標簽裡面的內容改為一個字符串,並彈出一個alert。
在Native中我們通過如下的LoadUrl()的方法調用這個函數:
這裡寫圖片描述
就是這麼簡單,我們就實現了Native到Js的調用,但是我們用一個需求,我們向得到一個回調怎麼辦了?Google在Android4.4為我們新增加了一個新方法,這個方法比loadUrl方便,而且比loadUrl效率更高,因為每次load都會將頁面刷新一次。

由於新增加的方法是4.4才引入的,所以我們使用的時候需要添加版本的判斷:
這裡寫圖片描述
如果是4.4之前的老版本,我們才用loadUrl的方式:
這裡寫圖片描述

3.我們在開始的時候貼出了一個我們使用setJavaScriptEnable()的警告,那我們怎麼來處理這個問題了,對於大多數的應用其實是不需要的,如果需要我們可以使用一種安全的方式,例如WebChromeClient中有一個onJsPrompt的回調方法,我們可以將我們需要傳遞的js命令轉換為一種協議的模式,通過這個回調函數傳遞到我們的Native中,這樣就避免了我們直接與代碼交互,具體的過程在這裡就不介紹了,下面提出一個該框架的github地址,大家需要的時候可以去下載:

https://github.com/pedant/safe-java-js-webview-bridge

開發WebApp

上面我們介紹了一些關於WebView這個控件的一些基本的使用和一些設置,這是我們卡發一個WebApp最重要的部分,因為我們的WebApp需要運行在它之上,接下來我們就正式開始介紹WebApp。

首先我們看一下WebApp的分類:

1.混合App,頁面在服務端,只需要很少的Native交互,這種形式的App很大的優勢就是當Web部分有更改的時候,不需要進行Apk的版本升級,直接在服務端就可以完成該工作,與我們Native部分交互很少,只需要為其提供一個頁面進行顯示,或則一些狀態信息,不如賬戶信息。但是這種App有個缺點就是,這個模塊只有在有網絡的情況下才可以使用,而且非常的耗費流量。

2.混合App,頁面在本地,這種類型的App從名字上看和上一種非常的想象,卻別就是我們的頁面和Js代碼都在本地而不在服務端,這樣做的好處就是我們在沒有網絡的情況下我們也可以進行訪問,並且相率相對於前一種有所提高,我們一般會把這樣的Web部分作為一個單獨的模塊,這樣大大的降低了我們開發的速度和解決一些適配上的問題。

3.純Web,這種模式也是現在很多公司比較青睐的,它的開發周期短,並且可以跨平台,實現了所謂的一次開發導出運行的效果,並且現在有非常多的前端框架可以選擇,直接使用命令行就可以搭建起一個簡單的框架,減少了很多的重復勞動。

接下來,我們就看一下我所知道的一些Web開發方法:

1.我們可以使用原始的HTML標簽加上CSS樣式來寫我們的界面,這樣雖然可以達到效果,但是工作量太大了,寫一個簡單的界面還好,要寫一個復雜的界面的話會非常的麻煩。不推薦。
2.使用前端框架進行開發,例如我最近看的JqueryMobile,它為我們提供了許多的樣式和組件,然我們只是寫幾個簡單的屬性就可以寫出漂亮的界面來,而且還加入了Jquery。下面就附上一段頁面的代碼:
這裡寫圖片描述
我們使用meta標簽設置了頁面的寬度為設備的寬度,並且不可以縮放,讓他適合我們的手機屏幕,並且引入了三個開發JqueryMobile的資源,這下我們的前期工作就做完了,是不是覺得很方便,我們在看下具體的布局:
這裡寫圖片描述
在JqueryMobile中的布局大致分為三個部分,Title、content、footer,我們可以通過定義三個

塊將他們分為三個部分,然後在其中加入jqueryMobile給我們提供的屬性,data-role=”header”、data-role=”content”、data-role=”footer”,值得注意的是,在header中的元素是會自動變為行內元素的,而且屬性多以data開頭等。

 

下面就來看一下上面的代碼在手機中的展示效果:

這裡寫圖片描述

看起來樣子還不錯,上面的紅色部分其實是原生的兩個TextView,很難看出差別了,如果你已經將開篇的三個資源都下載下來放入本地的assets文件下的話,我們即使在無網的情況下也能訪問該頁面,感覺很厲害的樣子。

3.使用命令行的方式一鍵生成

這種方式相對於上一種方式前期可能要麻煩了一些,需要我們進行下載npm,然後還要配置一些環境變量才能使用,有時點背我們下載還總是失敗,此時我們可以下載cnpm進行嘗試,具體的下載過程和環境變量在這裡我們就不在贅述了,網上的資源很多的

這裡就提一下我們需要用到cordova和ionic,cordova是用於我們開發純WebApp的時候幫助我們跨平台的調用系統原生的功能的。
看一看我們的大致步驟如下:

1.ionic start 在dos命令下輸入如下指令創建一個我們指定的項目,其中 project-name 是我們指定的項目名,optional-template 有三個可選參數:sidemenu tabs blank 從名字上我們就能猜出大概的意思,第一個是創建一個基於側滑菜單的模版,第二個是常見的底部tab模版,第三個就是空白頁模版。

2.創建好工程後我們就cd 進入我們的工程目錄,為工程添加運行的平台ionic platform add android

3.此時我們可以通過命令行打包我們的程序 ionic build android 也可以用我們的 Android Studio打開platform –》android 文件夾用ide進行編譯打包
接下來我們就來看下工程的目錄結構:

這裡寫圖片描述

platforms 存放我們編譯後的對應平台的工程
plugins 存放一些我們項目中用到的插件,例如上傳圖片啊,地圖等
www 這個是我們開發的主要目錄,我們看下它下面的結構:

這裡寫圖片描述

css 存儲項目中用到的css樣式文件
Img 存儲項目中的圖片資源
Js 存儲項目中的js代碼
lib 存儲一些庫文件
templates 存儲項目中的頁面,模版

看完了目錄結構,我們來看一下整個WebApp的大致架構, 我們可以發現www目錄下有一個index.html文件,這個是我們程序的主頁面,而且在應用中也是唯一存在的,為什麼這麼說,我們後面會介紹,我們來看一下它裡面的內容:

這裡寫圖片描述

這是我們看到的主頁面,它定義了viewport和寬度,以及縮放比例,讓網頁適配我們的手機屏幕,接下來導入我們的ionic的資源和一些Js插件,然後我們看到26行的位置出現了一個ng-app的屬性,這個屬性是Angularjs中定義一個模塊的指令,有點類似於我們Android開發中的Aplication,然後在30行的位置定義了一個的標簽,只會讓我們每一個主頁面下的頁面都有一個默認的標題欄,並且還有一個返回按鈕。

39行的位置定義了一個的標簽,這個就是為什麼說我們的WebApp中只有一個Index.html的緣故,因為,以後的頁面其實只是一個page,都顯示在這個標簽之內。

那麼我們在隨便找一個主頁面中的子頁面的代碼來看看:

這裡寫圖片描述

可以看到,我們的一個page頁是通過ion-view標簽包裹的,這個頁面中通過設置屬性hide-nav-bar=”true”將主頁面中的Title給隱藏掉,我們可以自己定義一個樣式通過
ion-header標簽或則用div加ion提供的css class樣式都可以實現,這種頁面同樣是 三層結構,分為頭、內容、底部。

這只是一個頁面,那如果我們頁面不止一個的時候,我們需要頁面之間的跳轉怎麼辦?這是就需要用到一個組件 ui-Router,它的功能特別的強大,可以控制我們的頁面跳轉,配置頁面是否緩存數據,配置頁面跳轉過程中需要傳遞的參數,它就是js目錄下的app.js。我們接下來就看一看它裡面的內容:

這裡寫圖片描述

我們看到第8行的位置出現了一個starter,是不是很眼熟,其實我們之前見過,它就是index.html中第26行

中定義的模塊名,這就將我們的整個項目的Application建立起來了,這裡就是對模塊進行一些全局的配置,starter後面被[]包裹起來的部分就是starter模塊依賴的模塊或則控制器,或服務;我們在這裡將其注入,以後我們就可以在starter的域中使用這些模塊。這種依賴注入的方式有一個好處,只有當我們需要用到的時候它才回去加載,不會出現重復加載的情況。

接著往下:看看我們前面所說的頁面之間跳轉的部分:

我們的頁面跳轉需要依賴$stateProvider, $urlRouterProvider這兩個模塊,以$開頭的都是Angularjs為我們提供的模塊,因此以後我們自定義模塊的時候不要以$開頭。

這裡寫圖片描述

這裡定義了兩個路由路徑,在第二個中我們定義了不緩存和這個頁面可以接收跳轉到這個頁面是傳遞的一個對象,其中有兩個字符參數。templateUrl標明我們路由對應的頁面,以後在controller中我們需要跳轉時,我們可以通過注入$state實現:

這裡寫圖片描述

這裡寫圖片描述

這句就是配置我們的默認路由路徑。

說了這麼老半天,好像我們還是在頁面這一層,那麼我們是如何讓我們的頁面展示我們指定的數據並且處理邏輯了?接下來我們就說一下之前提到的新的框架AngularJS,不得不說這個框架的功能特別的強大。
它最大的特點就是數據的雙向綁定,就是如果我們model層的數據發生變化後,我們不需要對頁面去做額外的處理,框架就能通知我們的頁面,讓其隨數據的改變而改變。這樣就讓我們將更多的精力放在我們的業務層,而且它也有類似MVC的結構,將model層和Controller層分開,是項目結構清晰,易於擴展。

這裡寫圖片描述

上圖是我們創建的模版中的一個模塊,這個模塊展示了一個列表的會話信息,那麼我們就來看一看它的結構是如何實現的。

這裡寫圖片描述

可以看到頁面代碼非常的簡單,我們在中定義了一個一個列表,然後在標簽中定義列表項,一個img用於展示頭像等等,我們在頁面中只看到了一個,可是頁面中有五個,這就是AngularJs給我們提供的指令ng-reapeat的效果了,它是一個迭代器,chats是一個我們在Controller中定義的一個以
$scope.chats定義的數組。$scope是一個作用於對象,作用范圍是當前的Controller和 它的子作用域。

遍歷數據後我們就可以得到一個chat會話對象,我們在img標簽中使用ng-src指令綁定我們的圖片地址 {{}} 是Angularjs中的運算表達式,會計算當中的數據的值。並顯示。我們也可以在標簽屬性中使用ng-bind來綁定數據。例如綁定名字我們也可以寫成

 

 

這裡寫圖片描述

上圖就是頁面的控制器,名字叫ChatsCtrl,我們注入了一個AngularJs的$scope模塊和一個Chats Server模塊,將Chats模塊中all()方法的返回值賦值給chats變量,就是我們程序中的進行遍歷的chats。並定義了一個方法可以刪除對應的會話。

這裡寫圖片描述

上圖就是我們Controller中依賴的Server模塊,其中定義了一個jsonArry的東西,然後通過all()方法返回這個對象。我們Controller中就可以得到數據。

總結一下:

上面的過程其實很簡單,可能我表述的不是很明白,我們一般在Controller中對數據進行操作,然後在Server中存儲數據,和一些可以抽取出來的共通方法,例如訪問網絡,配置常量信息等數據。這樣我們值要在Controller中改變了Chats的值後,頁面中對應的選項就會對應的發生變化。這就是所說的雙向綁定的概念,當然AngularJs的功能遠不止如此,還有許多的強大功能,例如自定義指令標簽等。

上面就是全部內容了,寫這篇筆記時查詢了很多網上的博客和文檔,目的就是為了將WebView中的知識進行一個整理,以後需要的時候可以去查詢,文中有錯誤的地方還望大家包含,多多真正,謝謝!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved