Androidアプリ本格開発入門 webブラウザ編

66
Android アアアアアアアアア Web アアアアア 2017 年 1 年 21 年 年年年年年年年年年 B.G

Transcript of Androidアプリ本格開発入門 webブラウザ編

Page 1: Androidアプリ本格開発入門 webブラウザ編

Android アプリ本格開発入門Web ブラウザ編

2017 年 1 月 21 日わんくま勉強会東京

B.G

Page 2: Androidアプリ本格開発入門 webブラウザ編

注意

※ この発言は個人の見解であり、所属する組織の公式見解ではありません

Page 3: Androidアプリ本格開発入門 webブラウザ編

自己紹介 (1) HN: B.G TwitterID:

@miu_hiro_( メイン , 仕事だけだったはずが・・・。 )@bg1_333( 元メイン , 同人関係その他 )

Blog: 車輪の x 発明 ~B.G's Blog~

(http://bg1.hatenablog.com/)

Page 4: Androidアプリ本格開発入門 webブラウザ編

自己紹介 (2) 言語・環境など :

C++( とくに C++1x) わかりませんC# わかりません Java わかりませんWin32 マンAndroid わかりません 最近は Android も多い

Page 5: Androidアプリ本格開発入門 webブラウザ編

VizCommandhttps://github.com/bg1bgst333/VizCommand

あと、こういうのつくってる

Page 6: Androidアプリ本格開発入門 webブラウザ編

Android わからないマンがブラウザを作ることになったというお話

Page 7: Androidアプリ本格開発入門 webブラウザ編

トピック はじめに WebView URL バー ブックマーク 履歴 タブブラウザ まとめ

Page 8: Androidアプリ本格開発入門 webブラウザ編

WebView WebView

https://developer.android.com/reference/android/webkit/WebView.html

指定された URL の Web ページを読み込み、適切に表示してくれる。 単体でも簡易的な Web ページ表示には使えるが、本格的に使う場合は、後述する

WebViewClient 、 WebChromeClient 、 WebSettings などと組み合わせる。

Page 9: Androidアプリ本格開発入門 webブラウザ編

WebViewClienthttps://

developer.android.com/reference/android/webkit/WebViewClient.html

WebView のロード中のステータスに対するイベントハンドラを持つ。WebViewClient を継承した派生クラスオブジェクトを WebView にセットすることで、さまざまなイベント処理をカスタマイズできる。

読み込み開始時 読み込み終了時 読み込み URL 変更時

Page 10: Androidアプリ本格開発入門 webブラウザ編

WebChromeClienthttps://

developer.android.com/reference/android/webkit/WebChromeClient.html

これも WebView に派生クラスをセットする形で使う。 こちらは比較的 UI に関するイベントなどのカスタマイズに使う。

プログレスバーの更新 タイトルの取得 favicon の取得

Page 11: Androidアプリ本格開発入門 webブラウザ編

WebSettingshttps://

developer.android.com/reference/android/webkit/WebSettings.html

WebView のさまざまな設定を行うクラス。 JavaScript の有効化 / 無効化 UserAgent の設定 / 取得

Page 12: Androidアプリ本格開発入門 webブラウザ編

shouldOverrideUrlLoadinghttps

://developer.android.com/reference/android/webkit/WebViewClient.html#shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String)

WebViewClient のハンドラメソッド 読み込み URL が変更された時に、ここに来る。

リダイレクトで URL が切り替わった時 Web ページ内のリンクをクリックした時

オーバーライドしないと、 Chrome を起動してしまう問題 ( 後述 ) Zinc #3 一部のサイトでChrome

にリダイレクトする動作を防止

Page 13: Androidアプリ本格開発入門 webブラウザ編

オーバーライドしないと、 Chrome を起動してしまう問題

shouldOverrideUrlLoading の戻り値true  →  WebView では処理しない

false  →  WebView で処理する

Page 14: Androidアプリ本格開発入門 webブラウザ編

public class CustomWebViewClient extends WebViewClient{

public boolean shouldOverrideUrlLoading(WebView view, String url){ // 必ず WebView に表示したいので , false を返す . return false; // false を返す .

}

}

Page 15: Androidアプリ本格開発入門 webブラウザ編

こうすると、 Chrome を起動せずに WebView でロードする。

Page 16: Androidアプリ本格開発入門 webブラウザ編

URL バー EditText

https://developer.android.com/reference/android/widget/EditText.html

Android には URL バーという View は無いので、EditText を改造する。

Page 17: Androidアプリ本格開発入門 webブラウザ編

URL バーの更新URL を入力する EditText を配置しただけでは、リンクをクリックしたり、リダイレクトで URLが変わった時に URL を更新してくれない。shouldOverrideUrlLoading で URL が変わった時、更新後の URL を setText でセットする。

Zinc #8 リンク先URLをURLバーに反映

Page 18: Androidアプリ本格開発入門 webブラウザ編

// MainActivity.javaprotected void onCreate(Bundle savedInstanceState) {

//… WebView webView = (WebView)findViewById(R.id.webview); CustomWebViewClient cwvcl = new CustomWebViewClient(this); // this(MainActivity 自身 ) を渡す .    webView.setWebViewClient(cwvcl); //…

}

Page 19: Androidアプリ本格開発入門 webブラウザ編

// CustomWebViewClient.javapublic class CustomWebViewClient extends WebViewClient{ private Context mContext; public void CustomWebViewClient(Context context){

mContext = context;

} public boolean shouldOverrideUrlLoading(WebView view, String url){

if (mContext != null){ EditText urlBar = (EditText)((MainActivity)mContext).findViewById(R.id.urlBar); urlBar.setText(url); // 更新後の url をセット . }

return false;

}}

Page 20: Androidアプリ本格開発入門 webブラウザ編

これで URL が更新される

http://yahoo.co.jpからhttp://m.yahoo.co.jp/に更新

Page 21: Androidアプリ本格開発入門 webブラウザ編

HTTP の補完と省略 現在の一般的なブラウザは、 URL バーにいちいち” http://” から始まる URL を入力しなくても、 HTTP リクエストとして認識する。 loadUrlは” http://” または” https://” が無いと、正しく読み込めないので、この部分を補完する必要がある。 一方、更新時に URL バーに URL を表示する場合は、” http” の場合は省略する場合が比較的多い。” http” の除去が必要。 これらの処理も shouldOverrideUrlLoading などで行う。

Zinc #9 ロード時のhttp補完および表示時のhttp省略

Page 22: Androidアプリ本格開発入門 webブラウザ編

URL バーでリターンキー入力でロード これまでは URL バーの隣にロード用のボタンを用意し、それを押すと Web ページのロードが開始されるような形にしていた。 しかし、たいていの Web ブラウザには、ロード用ボタンは無く、リターンキー (Enter, 完了 ) 入力だけでロードが開始される。 これを実現するには

TextView.OnEditorActionListener を使う。 OnEditorAction の中に書くべき処理はいくつかのパターンがあるので注意!

TextView.OnEditorActionListener https://developer.android.com/reference/android/widge

t/TextView.OnEditorActionListener.html Zinc #13 URLバー内でのEnterキーでWebページをロード

Page 23: Androidアプリ本格開発入門 webブラウザ編

public class MainActivity extends Activity implements View.OnClickListener, TextView.OnEditorActionListener { protected void onCreate(Bundle savedInstanceState) { //… EditText urlBar = (EditText)findViewById(R.id.urlBar); urlBar.setOnEditorActionListener(this); // EditorActionに this. }

public boolean onEditorAction(TextView v, int actionId, KeyEvent event){ if (actionId == EditorInfo.IME_ACTION_DONE){ EditText urlBar = (EditText) findViewById(R.id.urlbar); String url = urlBar.getText().toString(); WebView webView = (WebView) findViewById(R.id.webview); webView.loadUrl(load); } }}

Page 24: Androidアプリ本格開発入門 webブラウザ編

これでロード用のボタンは不要。( 右の × ボタンは URL バーの入力文字列をクリアするボタン )

Page 25: Androidアプリ本格開発入門 webブラウザ編

バックキーで戻る バックキーで一つ前のページに戻るには、 onKeyDown の KEYCODE_BACK や

onBackPressed で、 webView.canGoBack がtrue の時に、 webView.goBack すればいい。 Zinc #10 ハードバックキーで前のページに戻る

Page 26: Androidアプリ本格開発入門 webブラウザ編

ブックマーク ブックマーク機能の大幅縮小 ( 事実上廃止 )

Android5.1 以前は Browser.saveBookmark で専用ダイアログを表示してくれたり、管理もAndroid 側でよろしくやってくれていた。

Android6 からは、これらの便利なメソッドが軒並み廃止になり、アプリ内でブックマークデータを管理しなければならなくなった。 https://

developer.android.com/about/versions/marshmallow/android-6.0-changes.html#behavior-bookmark-browser

Zinc #6 ブックマークDBテーブルへの登録

Page 27: Androidアプリ本格開発入門 webブラウザ編

AlertDialog.builderhttps://

developer.android.com/reference/android/app/AlertDialog.Builder.html

ダイアログクラス。登録する URL の確認用に使う。 SQLiteOpenHelper

https://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html

SQLite のヘルパークラス。これをオーバーライドして、 DB の作成やアップグレードを行う。

Page 28: Androidアプリ本格開発入門 webブラウザ編

public class DBHelper extends SQLiteOpenHelper { private static final String DB = "zinc1.db"; private static final int DB_VERSION = 1; private static final String TABLE_BOOKMARK = "bookmark"; private static final String CREATE_TABLE_BOOKMARK = "create table " + TABLE_BOOKMARK + " ( _id integer primary key autoincrement, name string, url string);"; private static final String DROP_TABLE_BOOKMARK = "drop table " + TABLE_BOOKMARK + ";"; public DBHelper(Context context){ super(context, DB, null, DB_VERSION); } public void onCreate(SQLiteDatabase db){ try{ db.execSQL(CREATE_TABLE_BOOKMARK); } catch(Exception ex){ Log.e("Zinc", ex.toString()); } } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){ db.execSQL(DROP_TABLE_BOOKMARK ); onCreate(db); }}

Page 29: Androidアプリ本格開発入門 webブラウザ編

SQLiteDatabasehttps://

developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html

ヘルパーから取得した DB 本体。これで insert やupdate を行う。

Page 30: Androidアプリ本格開発入門 webブラウザ編

DBHelper hlpr = new DBHelper(getApplicationContext());SQLiteDatabase sqlite = hlpr.getWritableDatabase();ContentValues values = new ContentValues();values.put("name", strName);values.put("url", strUrl);long id = sqlite.insertOrThrow("bookmark", null, values);

Page 31: Androidアプリ本格開発入門 webブラウザ編

ListView https://

developer.android.com/reference/android/widget/ListView.html

ブックマークや履歴などの URL リストはリストビューで表示する。 単体では使用せず、 Adapter系クラスと組み合わせて使う。

ArrayAdapter https://

developer.android.com/reference/android/widget/ArrayAdapter.html

実際のリストデータとリストビューの橋渡しをするクラス。 渡されたリストデータをアイテム一つ一つどういうレイアウトで表示するかを決定する。

Page 32: Androidアプリ本格開発入門 webブラウザ編

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/url_list_item_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" >

<TextView android:id="@+id/url_list_item_name" android:layout_width="fill_parent" android:layout_height="wrap_content" />

<TextView android:id="@+id/url_list_item_url" android:layout_width="fill_parent" android:layout_height="wrap_content" />

</LinearLayout>

Page 33: Androidアプリ本格開発入門 webブラウザ編

public class UrlListItem { public String name; // Web ページ名 . public String url; // URL}

public class UrlListAdapter extends ArrayAdapter<UrlListItem> { private LayoutInflater inflater; public UrlListAdapter(Context context, int resource, List<UrlListItem> objects){ super(context, resource, objects); inflater = (LayoutInflater)context.getSystemService(context.LAYOUT_INFLATER_SERVICE); } @Override public View getView(int position, View convertView, ViewGroup parent){ if (convertView == null){ convertView = inflater.inflate(R.layout.url_list_item, null); } TextView tvName = (TextView)convertView.findViewById(R.id.url_list_item_name); tvName.setText(getItem(position).name); TextView tvUrl = (TextView)convertView.findViewById(R.id.url_list_item_url); tvUrl.setText(getItem(position).url); return convertView; }}

Page 34: Androidアプリ本格開発入門 webブラウザ編

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" >

<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/bookmarklist" android:layout_width="match_parent" android:layout_height="wrap_content"> </ListView>

</LinearLayout>

</LinearLayout>

Page 35: Androidアプリ本格開発入門 webブラウザ編

public class BookmarkActivity extends AppCompatActivity { public List<UrlListItem> bookmarkList = null; public ListView lvBookmark = null; public UrlListAdapter adapter = null public DBHelper hlpr = null; public SQLiteDatabase sqlite = null;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bookmark);

bookmarkList = new ArrayList<UrlListItem>(); adapter = new UrlListAdapter(this, R.layout.url_list_item, bookmarkList); lvBookmark = (ListView)findViewById(R.id.bookmarklist); lvBookmark.setAdapter(adapter);

//…

Page 36: Androidアプリ本格開発入門 webブラウザ編

hlpr = new DBHelper(getApplicationContext());sqlite = hlpr.getReadableDatabase();Cursor cursor = null;cursor = sqlite.rawQuery("SELECT * FROM bookmark;", null);int c = cursor.getCount();cursor.moveToFirst();for (int i = 0; i < c; i++){ int _id = cursor.getInt(0); // 0 列目は _id. String name = cursor.getString(1); // 1 列目は name. String url = cursor.getString(2); // 2 列目は url.

UrlListItem item = new UrlListItem(); item.name = name; // item.name に name. item.url = url; // item.url に url. bookmarkList.add(item);

cursor.moveToNext();}cursor.close();

Page 37: Androidアプリ本格開発入門 webブラウザ編

メニュー (今回は割愛 ) でブックマークの登録を選択

Page 38: Androidアプリ本格開発入門 webブラウザ編

確認用ダイアログ表示

Page 39: Androidアプリ本格開発入門 webブラウザ編

ブックマークの管理を選択

Page 40: Androidアプリ本格開発入門 webブラウザ編

ブックマークに登録されている

Page 41: Androidアプリ本格開発入門 webブラウザ編

履歴 履歴も自前 DB で管理

ブックマークと同様、通算の履歴も Android6 からは、自前で管理しなければならない。 WebBackForwardList  は通算の履歴ではない

WebBackForwardList https://

developer.android.com/reference/android/webkit/WebBackForwardList.html

これは WebView がページ遷移のどの位置にいるかをスタック状に蓄積しているだけ。 ページ A→ ページ B→ ページ C→ 戻る→戻る このとき、 WebBackForwardList に残っているのはページ Aのみ

履歴の表示 ブックマークと同様に ListView で DB から読み込んで表示する。

Page 42: Androidアプリ本格開発入門 webブラウザ編

履歴の登録 どのタイミングで登録すべきかは、おそらく正解がない。 いまのところ、おそらく onPageFinished で登録するのが良いと思われる。Chrome では、ロードの途中のどこかで履歴に登録されている模様だが、詳細は不明。

Page 43: Androidアプリ本格開発入門 webブラウザ編

onPageStarted https://developer.android.com/reference/android/w

ebkit/WebViewClient.html#onPageStarted(android.webkit.WebView, java.lang.String, android.graphics.Bitmap)

WebViewClient のハンドラメソッド 読み込みが開始された時に、ここに来る。

loadUrl でロードされた時 リダイレクトやリンクを開いた時の

shouldOverrideUrlLoading の後 ここで履歴 DBへの登録をしてしまうと、リダイレクト URL をすべて登録してしまうので × ここでは読み込んだ URL をいったんメンバに保持しておくだけにしておくのが良いと思われる。

Page 44: Androidアプリ本格開発入門 webブラウザ編

onPageFinished https://developer.android.com/reference/android/we

bkit/WebViewClient.html#onPageFinished(android.webkit.WebView, java.lang.String)

WebViewClient のハンドラメソッド 読み込みが終了した時に、ここに来る。 しかし、なぜか同じ URL で 2度ここに来ることがある。

例えば Yahoo など バグ? JavaScript のせい?

そのため、最後にメンバに保持していた URL と同じ URL で、なおかつ 1回目の到着時のみ、履歴 DBに登録するのがベスト ( というかベター ) という結論に。

Page 45: Androidアプリ本格開発入門 webブラウザ編

public class CustomWebViewClient extends WebViewClient { private String mStartUrl = “”; private int count = 0; @Override public void onPageStarted(final WebView view, final String url, final Bitmap favicon) { mStartUrl = url; count = 0; } @Override public void onPageFinished(WebView view, String url) { if (count == 0){ if (mStartUrl.equals(url)){ // 履歴 DB に登録 // … } } count++; }}

Page 46: Androidアプリ本格開発入門 webブラウザ編

タブブラウザ タブブラウザにしたい

これまで一つの Activity に一つの WebView を置いてページを表示してきたが、 Chrome を始め、ほとんどのブラウザはタブブラウザが基本となっている。 タブブラウザの機能も標準として用意されているわけではなく、自前で切り替えや表示の機能を作らなければならない。 Chrome Custom Tabs というものも最近出てきたが、どこまでカスタム出来るか不明なので今後調査してみようと思う。

https://developer.chrome.com/multidevice/android/customtabs

Page 47: Androidアプリ本格開発入門 webブラウザ編

タブ系ビュー (1) TabActivity

https://developer.android.com/reference/android/app/TabActivity.html

Android3系まで使われていた。 1 つの Activity の中に子 Activity をいくつも置くような形

( 現在でいう Fragment) API レベル 13 で非推奨

TabHost https://

developer.android.com/reference/android/widget/TabHost.html

Android3系まで使われていた。 Layout の表示 /非表示を複数のタブで切り替えるような使い方。 Fragment を入れることはできない? (試してないので不明 ) 主に複数の固定画面切り替えで使う API レベル 13(? )辺りで非推奨のはずが、後述の

ActionBar.TAB が API レベル 21 で非推奨となり、こっちは非推奨が撤回された模様 (? )

Page 48: Androidアプリ本格開発入門 webブラウザ編

タブ系ビュー (2)ActionBar.Tab

https://developer.android.com/reference/android/app/ActionBar.Tab.html

API レベル 11(Android3) から追加 ActionBar の機能の一部としてタブの機能が追加された。 中は Fragment なので、画面切り替えとしても使いやすい。 しかし、 API レベル 21(Android5) から非推奨に。

Page 49: Androidアプリ本格開発入門 webブラウザ編

タブ系ビュー (3) FragmentTabHost

https://developer.android.com/reference/android/support/v4/app/FragmentTabHost.html

Support.v4 ライブラリにある Fragment 切り替えのできるTabHost

使い方は TabHost をベースとしている。 最初はこれで実装してみたが問題点がいくつも出てきた。

Tab と Fragment を一括管理しており、 Fragment だけを差し替えることができない。 (Fragment を取得して強引に削除したら落ちた。 ) 1 つの Tab/Fragment のセットを削除できない。削除するときはすべての Tab/Fragment のセットを削除してから、新たに作り直さなければならない。 View が維持されない。 Tab を押すたびに画面の生成が行われ、ロード済みの Web ページを表示していた WebView も真っ白に。 onSavedInstance で保存していても、復元時に再ロードが走ってしまう。 Tab を追加するたびに Tab 部分が小さくなり、 Tab が多いとタイトルがほとんど表示できない。

Page 50: Androidアプリ本格開発入門 webブラウザ編

タブ系ビュー (4)PagerTabStrip

https://developer.android.com/reference/android/support/v4/view/PagerTabStrip.html

Support.v4 ライブラリにあったみたいだが試してない。 『こっちの方がよかったのでは』と後悔。

TabLayout https://

developer.android.com/reference/android/support/design/widget/TabLayout.html

Design.Support ライブラリにあるので比較的新しいが試してない。 『今だとこちらがトレンドかも』とさらに後悔。

BottomNavigationView https://

developer.android.com/reference/android/support/design/widget/BottomNavigationView.html

下タブなので却下。

Page 51: Androidアプリ本格開発入門 webブラウザ編

概念上のタブ FragmentTabHost までで疲弊したので、改めてタブについて考えてみた。 そもそも、タブ系ビューでないといけないのか。 Google Chrome は、謎の 3D な UI によって切り替えられている。 サードパーティー製のブラウザや Windows10Mobile などはグリッドビューのようなタイル形式でサムネイルを選択するような形が多い。 (概念上の ) タブごとに WebView を始めとする View がの状態が維持されていれば、タブ系ビューにこだわる必要は無いのでは。

(概念上の ) タブの画面はそれぞれ Fragment にして、タブ切り替え用の一覧画面は専用の特殊なFragment にしよう。

Page 52: Androidアプリ本格開発入門 webブラウザ編

Fragment https://

developer.android.com/reference/android/app/Fragment.html

Activity の中に配置できる子 Activity のようなもの これを複数配置して画面を切り替えるのが一般的。

FragmentManager https://

developer.android.com/reference/android/app/FragmentManager.html

Activity 内の Fragment 管理。 FragmentTransaction

https://developer.android.com/reference/android/app/FragmentTransaction.html

Fragment操作などを行う。

Page 53: Androidアプリ本格開発入門 webブラウザ編

add https://developer.android.com/reference/android/a

pp/FragmentTransaction.html#add(int, android.app.Fragment, java.lang.String)

Fragment を追加する。既に追加済みの Fragment はそのまま残る。 show/hide で表示 /非表示を切り替える。 show/hide の切り替え時に Fragment ライフサイクルは発生しない

replace https://developer.android.com/reference/android/a

pp/FragmentTransaction.html#replace(int, android.app.Fragment, java.lang.String)

Fragment を置換する。既に追加済みの Fragment は消える。 置換なので Fragment ライフサイクルが発生する。

Page 54: Androidアプリ本格開発入門 webブラウザ編

起動時に表示する最初のタブ FragmentTransaction.add で追加する。

Page 55: Androidアプリ本格開発入門 webブラウザ編

private final String FRAGMENT_TAB_PREFIX_WEB = "web"; private int webFragmentNo = 0; private FragmentManager fragmentManager = null; private Map<String, Fragment> fragmentMap = null; private String currentFragmentTag = null;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

// fragmentMap の作成 . fragmentMap = new HashMap<String, Fragment>();

// 最初の Webフラグメントの追加 . fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); String fragmentTag = FRAGMENT_TAB_PREFIX_WEB + webFragmentNo; WebFragment webFragment = new WebFragment(); fragmentTransaction.add(R.id.content, webFragment, fragmentTag); fragmentTransaction.commit(); fragmentMap.put(fragmentTag, webFragment); currentFragmentTag = fragmentTag; webFragmentNo++ }

Page 56: Androidアプリ本格開発入門 webブラウザ編

新しいタブの追加 FragmentTransaction.add で追加する。

Zinc #34 新しいタブの追加

Page 57: Androidアプリ本格開発入門 webブラウザ編

// メニュー選択時 @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.menu_item_add_tab) { // 他のフラグメントを非表示にしてから , フラグメントを追加し , 表示 . FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); for (Map.Entry<String, Fragment> entry: fragmentMap.entrySet()){ Fragment fragment = entry.getValue(); fragmentTransaction.hide(fragment); } String fragmentTag = FRAGMENT_TAB_PREFIX_WEB + webFragmentNo; WebFragment webFragment = new WebFragment(); fragmentTransaction.add(R.id.content, webFragment, fragmentTag); fragmentTransaction.show(webFragment); fragmentTransaction.commit(); fragmentMap.put(fragmentTag, webFragment); currentFragmentTag = fragmentTag; webFragmentNo++; setMenuUrlBar(""); } return super.onOptionsItemSelected(item); }

Page 58: Androidアプリ本格開発入門 webブラウザ編

真っ白な新しいタブが追加された

Page 59: Androidアプリ本格開発入門 webブラウザ編

タブの切り替えボタン メニューにタブ切り替えボタンを用意し、それを押すと、タブ一覧画面の Fragment を表示する。

Page 60: Androidアプリ本格開発入門 webブラウザ編

タブ一覧画面 タブ一覧画面の Fragment に GridView でタブのサムネイルを表示する。

Page 61: Androidアプリ本格開発入門 webブラウザ編

// メニュー選択時 @Override public boolean onOptionsItemSelected(MenuItem item) { // 選択されたメニューアイテムごとに振り分ける . int id = item.getItemId(); // item.getItemId で id を取得 . if (id == R.id.menu_item_show_tabs) { // タブ一覧の表示 . // 現在表示しているタブのキャプチャを撮影 . if (currentFragmentTag.contains(FRAGMENT_TAG_PREFIX_WEB)) String path = getCacheDir() + "/" + currentFragmentTag + ".jpg WebFragment webFragment = (WebFragment)fragmentManager.findFragmentByTag(currentFragmentTag); View view = webFragment.getView(); captureView(path, view); } // tabsFragment を作成して , タグ名を決めて追加 . TabsFragment tabsFragment = new TabsFragment(); String fragmentTag = FRAGMENT_TAG_PREFIX_TABS;           addFragment(tabsFragment, fragmentTag); // フラグメントの追加 . setMenuUrlBar(""); // setMenuUrlBar で URL バーを空に . menuItemUrlBar.setVisible(false); // URL バーの非表示 . }

Page 62: Androidアプリ本格開発入門 webブラウザ編

// フラグメントのビューのキャプチャを撮る . public void captureView(String path, View view){ Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); // bitmap を作成 . Canvas canvas = new Canvas(bitmap); // bitmap から Canvas オブジェクト canvasを生成 . view.draw(canvas); // view.draw で canvas に view を描画 . FileOutputStream fos = null; try { fos = new FileOutputStream(path); if (fos != null) { bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos); fos.close(); fos = null; } } catch (Exception e) { Log.d("Zinc:e:", e.toString()); } finally { try { if (fos != null) { fos.close(); fos = null; } } catch (Exception e) { Log.d("Zinc:e:", e.toString()); } } }

Page 63: Androidアプリ本格開発入門 webブラウザ編

// フラグメントの追加 public void addFragment(Fragment addFragment, String fragmentTag){ // 他のフラグメントを非表示にしてから , フラグメントを追加し , 表示 . FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); for (Map.Entry<String, Fragment> entry: fragmentMap.entrySet()){ Fragment fragment = entry.getValue(); fragmentTransaction.hide(fragment); } fragmentTransaction.add(R.id.content, addFragment, fragmentTag); fragmentTransaction.show(addFragment); fragmentTransaction.commit(); fragmentMap.put(fragmentTag, addFragment); currentFragmentTag = fragmentTag; }

Page 64: Androidアプリ本格開発入門 webブラウザ編

public class TabsFragment extends Fragment implements AdapterView.OnItemClickListener{ // メンバフィールドの定義 private MainActivity mainActivity = null; private View fragmentView = null; private GridView tabsGridView = null; private List<TabsGridItem> tabsGridItemList = null; private TabsGridAdapter adapter = null; private Map<String, Fragment> fragmentMap = null; private final String FRAGMENT_TAG_PREFIX_WEB = "web"; private final String FRAGMENT_TAG_PREFIX_TABS = "tabs";

public TabsFragment() { // Required empty public constructor }

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // タブ一覧を取得して , GridView で表示 . mainActivity = (MainActivity)getActivity(); // Inflate the layout for this fragment fragmentView = inflater.inflate(R.layout.fragment_tabs, container, false); tabsGridView = (GridView)fragmentView.findViewById(R.id.tabsGridView); tabsGridItemList = new ArrayList<TabsGridItem>(); adapter = new TabsGridAdapter(mainActivity, R.layout.grid_item_tabs, tabsGridItemList); tabsGridView.setAdapter(adapter); fragmentMap = mainActivity.getFragmentMap(); addTabsGridItem(); adapter.notifyDataSetChanged(); tabsGridView.setOnItemClickListener(this); return fragmentView; }

Page 65: Androidアプリ本格開発入門 webブラウザ編

// グリッドアイテムが選択された時 . public void onItemClick(AdapterView<?> parent, View view, int position, long id){ // 選択されたアイテムの取得 . GridView gridView = (GridView)parent; TabsGridItem gridItem = (TabsGridItem) gridView.getItemAtPosition(position); mainActivity.changeFragment(gridItem.tabName); mainActivity.changeUrl(gridItem.tabName); mainActivity.removeFragment(FRAGMENT_TAG_PREFIX_TABS); }

// フラグメントの切り替え public void changeFragment(String tabName){ // 指定された tabName のフラグメントは表示 , それ以外は非表示 . FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); for (Map.Entry<String, Fragment> entry : fragmentMap.entrySet()) { String tab = entry.getKey(); Fragment fragment = entry.getValue(); if (tab.equals(tabName)){ fragmentTransaction.show(fragment); } else { fragmentTransaction.hide(fragment); } } fragmentTransaction.commit(); currentFragmentTag = tabName; } // フラグメントの削除 public void removeFragment(String tabName){ // tabName で fragment を探して , 削除 . Fragment fragment = fragmentManager.findFragmentByTag(tabName); if (fragment != null) { FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.remove(fragment); fragmentTransaction.commit(); fragmentMap.remove(tabName); } }

Page 66: Androidアプリ本格開発入門 webブラウザ編

まとめ 実際に Web ブラウザを作ってみると、 WebView 以外の部分が View や機能として用意されていなかったので、自分で作り込むのが大変だった。 ただ、 Android のよく使う機能について、全体的に学べたことは良かった。 今後も Web ブラウザ開発、 Android についての知見を積んでいきたいとおもった。 「もっと簡単にできるよ!」「楽にできるよ!」っていうのがあったら教えていただきたい。