forus TECH

IT系技術メモ&本まとめ、ライフハック etc

【Android】WebView使用時の設定とTipsまとめ

f:id:yusukekuni:20130410180238p:plain

  • WebViewClientを設定してロード時の処理を定義する
  • WebView内ページのリダイレクトを判定する
  • ユーザーエージェントを設定する
  • URL別にWebView/標準ブラウザを判別する
  • 端末の戻るボタンで1ページ戻る
  • おまけ(goBack()で戻れない!?)


準備
WebViewを使用するため、AndroidManifestに以下のパーミッションを追加するのを忘れないように。

AndroidManifest.xml

android.permission.INTERNET


WebViewClientを設定してロード時の処理を定義する
java

//WebViewインスタンスの生成
WebView webview = new WebView(this);

// WebViewClientの設定
webview.setWebViewClient(new WebViewClient() {
	// 新しいURLが指定されたときの処理を定義
	@Override
	public boolean shouldOverrideUrlLoading(WebView view, String url) {
		// 別のActivityやアプリを起動する場合
		return true;

		// WebView内に読み込み結果を表示する場合
		return false;
	}

	// ページ読み込み開始時の処理
	@Override
	public void onPageStarted(WebView view, String url, Bitmap favicon) {
		Toast.makeText(OfferActivity.this, "読み込み開始", Toast.LENGTH_LONG).show();
	}

	// ページ読み込み完了時の処理
	@Override
	public void onPageFinished(WebView view, String url) {
		Toast.makeText(OfferActivity.this, "読み込み完了", Toast.LENGTH_LONG).show();
	}

	// ページ読み込みエラー時の処理
	@Override
	public void onReceivedError(WebView view, int errorCode, String description, String url) {
		Toast.makeText(OfferActivity.this, "通信エラー", Toast.LENGTH_LONG).show();
	}
});


WebView内ページのリダイレクトを判定する
java

webview.setWebViewClient(new WebViewClient() {
	@Override
	public boolean shouldOverrideUrlLoading(WebView view, String url) {
		if (view.getHitTestResult().getType() > 0) {
			// クリック参照ページの場合
			return true;
		} else {
			// リダイレクトページの場合
			return false;
		}
	}
});


ユーザーエージェントを設定する
java

// ユーザーエージェントの設定
webview.getSettings().setUserAgentString("YourUserAgent");

ユーザーエージェント一覧


JavaScriptを有効化し、WebView内JavaScriptJavaを連携する
JavaScriptを有効化
java

// JS有効化
webview.getSettings().setJavaScriptEnabled(true);

JavaScriptから呼び出されるクラスの定義
java

public class WebAppInterface {
	Context mContext;

	// コンストラクタ
	WebAppInterface(Context c) {
		mContext = c;
	}

	// JavaScriptから呼び出されるメソッド
	@JavascriptInterface
	public void showToast(String toast) {
		Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
	}
}

JavaScriptから参照できるよう設定
java

// 第2引数はJavaScriptでセレクタとして使用する名前
webview.addJavascriptInterface(new WebAppInterface(webview), "Android");

※追記
Android4.2 (APIレベル17)以降から@JavascriptInterfaceアノテーションを付加した
publicメソッドのみ呼び出せるように変更がありました。


■表示されるHTMLファイルの設定
html

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
	function showAndroidToast(toast) {
		Android.showToast(toast);
	}
</script>


URL別にWebView/標準ブラウザを判別する
java

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
	if (Uri.parse(url).getHost().equals("www.example.com")) {
		// WebView内で表示する
		return false;
	}
	// 標準ブラウザで表示する
	Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
	startActivity(intent);
	return true;
}


端末の戻るボタンで1ページ戻る
端末の戻るボタンが押されたときにWebView内で1ページ戻り、戻るページがなくなったらActivityを終了する。

java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
	if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
		myWebView.goBack();
		return true;
	}
	return super.onKeyDown(keyCode, event);
}


おまけ(goBack()で戻れない!?)
JavaScriptと連携してWebViewのgoBack()を呼び出すと、canGoBack()の結果はtrueが返っているにも関わらずページが戻らない事象が発生。
webview.copyBackForwardList().getCurrentIndex()で調べてみるとJSから呼び出した時点でなぜかIndexが+1されている・・・
よって以下のようにすることで1ページ戻ることができた。
runOnUiThreadを使用しているのはWarning対応です。

''Warning: A WebView method was called on thread 'WebViewCoreThread'. All WebView methods must be called on the UI thread. Future versions of WebView may not support use on other threads.''

java

public class WebAppInterface {
	@JavascriptInterface
	public void goback() {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (webview.canGoBack()) {
					//webview.goBack(); ←これだと戻れない
					webview.goBackOrForward(-2);
				}
			}
		});
	}
}


まとめ
以上の設定を踏まえたサンプル

java

public class MainActivity extends Activity {

	private final int MP = ViewGroup.LayoutParams.MATCH_PARENT;
	private static WebView webview;

	// JavaScriptから呼び出されるクラスの定義
	public class WebAppInterface {
		@JavascriptInterface
		public void goback() {
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					if (webview.canGoBack()) {
						//webview.goBack(); ←これだと戻れない
						webview.goBackOrForward(-2);
					}
				}
			});
		}
	}

	@SuppressLint("SetJavaScriptEnabled")
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// WebViewのみのページ生成
		LinearLayout layout = new LinearLayout(this);
		setContentView(layout);
		webview = new WebView(this);
		layout.addView(webview, new LinearLayout.LayoutParams(MP, MP, 1));

		// WebViewClient設定
		webview.setWebViewClient(new WebViewClient() {
			// 新しいURLが指定されたときの処理を定義
			@Override
			public boolean shouldOverrideUrlLoading(WebView view, String url) {
				// ホストによってWebView内で表示するか標準ブラウザで表示するか判定する
				if (Uri.parse(url).getHost().equals("www.example.com")) {
						// WebView内で表示する
						return false;
				}
				// 標準ブラウザで表示する
				Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
				startActivity(intent);
				return true;
			}

			// ページ読み込み開始時の処理
			@Override
			public void onPageStarted(WebView view, String url, Bitmap favicon) {
				Toast.makeText(MainActivity.this, "読み込み開始", Toast.LENGTH_LONG).show();
			}

			// ページ読み込み完了時
			@Override
			public void onPageFinished(WebView view, String url) {
				Toast.makeText(MainActivity.this, "読み込み完了", Toast.LENGTH_LONG).show();
			}

			// エラー時
			@Override
			public void onReceivedError(WebView view, int errorCode,
					String description, String url) {
				Toast.makeText(MainActivity.this, "通信エラー", Toast.LENGTH_LONG).show();
			}
		});

		// ユーザーエージェントの設定
		webview.getSettings().setUserAgentString("MyUserAgent");

		// JavaScript有効化
		webview.getSettings().setJavaScriptEnabled(true);

		// JavaScriptから参照できるよう設定(第2引数はJavaScriptでセレクタとして使用する名前)
		webview.addJavascriptInterface(new WebAppInterface(), "Android");

		// ページの読み込み
		webview.loadUrl("http://example.com");
	}

	// 戻るボタンが押されたときの処理
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
			webview.goBack();
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}
}

html

<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="utf-8">
		<title>Sample</title>
		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
		<meta name="author" content="yusukekuni">
		<meta name=apple-mobile-web-app-capable content=yes>
		<meta name=apple-mobile-web-app-status-bar-style content=black>
		<!-- アプリ側で定義したメソッドへのアクセス -->
		<script type="text/javascript">
			function goback() {
				Android.goback();
			}
		</script>
	</head>

	<body>
		<a href="#" onclick="goback()" class="btn">Back</a>
	</body>
</html>