AndroidでJavaScriptとJavaの連携
ボタンを一列に並べて、画面からはみ出るようだったら折り返すってビューを作りたかったんですが、JavaのViewで実現するのに妙案が浮かばず、じゃあWebViewで出しちゃえばいいじゃない!ってことで、JavaScriptとどうやって連携するのか調べました。
ちなみに何を作ってるのかというとタグを並べて、それを押したらEditTextに文字列を流し込むって画面。はてブアプリをパクr…、参考にして。あれかっこいい。あれもWebViewで実現してるんでしょうか。
で、JSの連携は説明はめんどいので実際のソースコード。
まずはlayout
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:focusable="false" android:focusableInTouchMode="false" > <TextView android:id="@+id/tv" android:layout_width="fill_parent" android:layout_height="wrap_content"/> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="btn" android:onClick="clickBtn"/> <WebView android:id="@+id/wv" android:layout_width="fill_parent" android:layout_height="fill_parent" android:focusable="false" android:focusableInTouchMode="false"/> </LinearLayout>
難しいところは何一つない。
お次はActivity。importとかは端折った。
public class JsBridgeActivity extends Activity { private static final String HOGE_HTML = "file:///android_asset/hoge.html"; private TextView textView; private WebView webView; /** * WebViewの中で使うJsObj */ public class JsObj{ private JsBridgeActivity act; private Handler handler; public JsObj(JsBridgeActivity act){ this.act = act; handler = new Handler(); } /** * ボタンクリック時に呼ばれる * @param tag */ public void send(final String str){ //ここで普通にact.setTextValue(str)とかやっても怒られる。 handler.post(new Runnable() { public void run() { act.setTextValue(str); } }); } /** * Textの中の値を取得 * @return */ public String getTextValue(){ if(textView == null){ return ""; } String str = textView.getText().toString() == null ? "" : textView.getText().toString(); return str; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textView = (TextView) findViewById(R.id.tv); textView.setText("ほげー"); webView = (WebView) findViewById(R.id.wv); //js使用可能 webView.getSettings().setJavaScriptEnabled(true); //load webView.loadUrl(HOGE_HTML); JsObj js = new JsObj(this); //JavaScriptではandroidでJsObjにアクセスできる webView.addJavascriptInterface(js, "android"); } /** * ボタン押した * @param v */ public void clickBtn(View v){ webView.loadUrl("javascript:fromAndroid('androidからです')"); } /** * textに値を入れる * @param str */ public void setTextValue(String str){ if(textView == null) return; textView.setText(str); } }
ポイントはJavaScriptで使うオブジェクトを作ってあげることと、JavaScriptからActivityの値を変更したい場合は、handlerを経由してあげなきゃいけない。
handlerについて詳しくは下記。
http://www.adamrocker.com/blog/261/what-is-the-handler-in-android.html
で、最後にhoge.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=1"> <script type="text/javascript" src="js/jquery-1.4.3.min.js"></script> <script type="text/javascript"> $(document).ready(function(){ $("#sendBtn").click(function(){ android.send($("#input").val()); }); $("#getBtn").click(function(){ $("#jText").text(android.getTextValue()); }); }); function fromAndroid(str){ $("#jText").text(str); } </script> </head> <body> <input type="text" value="" id="input" /> <button id="sendBtn">send</button> <p id="jText"></p> <button id="getBtn">get</button> </body> </html>
ソース見れば分かるように、
JsObj js = new JsObj(this); webView.addJavascriptInterface(js, "android");
と指定してやるとJavaScript側からJavaのメソッドを叩ける。
そしてJava側からJSを叩く場合はloadUrlの引数にJSを書けばよいらしい。すげーかっこわるい!?
ただ、あんまりレスポンスが良くないような気がします。この程度だったらよいけど、今作ってるタグのUIはタグが押されてから文字列を流し込むまで若干もたつく印象。パクリもとの参考にしたはてブのアプリはレスポンスも素敵なんですが…。アプローチが間違ってるんだろうか。ううむ。