Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

2,374 views

Published on

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

Published in: Software
  • Be the first to comment

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

  1. 1. 2017年1月21日 わんくま勉強会東京 B.G
  2. 2. ※この発言は個人の見解であり、所属する組織 の公式見解ではありません
  3. 3.  HN: B.G  TwitterID:  @miu_hiro_(メイン, 仕事だけだったはず が・・・。)  @bg1_333(元メイン, 同人関係その他)  Blog:  車輪のx発明 ~B.G's Blog~  (http://bg1.hatenablog.com/)
  4. 4.  言語・環境など:  C++(とくにC++1x)わかりません  C#わかりません  Javaわかりません  Win32マン  Androidわかりません  最近はAndroidも多い
  5. 5. Androidわからないマンが ブラウザを作ることになった というお話
  6. 6.  はじめに  WebView  URLバー  ブックマーク  履歴  タブブラウザ  まとめ
  7. 7.  WebView  https://developer.android.com/reference/android/w ebkit/WebView.html  指定されたURLのWebページを読み込み、適切に 表示してくれる。  単体でも簡易的なWebページ表示には使えるが、 本格的に使う場合は、後述するWebViewClient、 WebChromeClient、WebSettingsなどと組み合わ せる。
  8. 8.  WebViewClient  https://developer.android.com/reference/android/w ebkit/WebViewClient.html  WebViewのロード中のステータスに対するイベン トハンドラを持つ。  WebViewClientを継承した派生クラスオブジェク トをWebViewにセットすることで、さまざまなイ ベント処理をカスタマイズできる。  読み込み開始時  読み込み終了時  読み込みURL変更時
  9. 9.  WebChromeClient  https://developer.android.com/reference/android/w ebkit/WebChromeClient.html  これもWebViewに派生クラスをセットする形で使 う。  こちらは比較的UIに関するイベントなどのカスタ マイズに使う。  プログレスバーの更新  タイトルの取得  faviconの取得
  10. 10.  WebSettings  https://developer.android.com/reference/android/w ebkit/WebSettings.html  WebViewのさまざまな設定を行うクラス。  JavaScriptの有効化/無効化  UserAgentの設定/取得
  11. 11.  shouldOverrideUrlLoading  https://developer.android.com/reference/android/w ebkit/WebViewClient.html#shouldOverrideUrlLoadi ng(android.webkit.WebView, java.lang.String)  WebViewClientのハンドラメソッド  読み込みURLが変更された時に、ここに来る。  リダイレクトでURLが切り替わった時  Webページ内のリンクをクリックした時  オーバーライドしないと、Chromeを起動してし まう問題(後述)  Zinc #3 一部のサイトでChromeにリダイレクトする動 作を防止
  12. 12.  オーバーライドしないと、Chromeを起動してしまう問題 shouldOverrideUrlLoadingの戻り値 true → WebViewでは処理しない false → WebViewで処理する
  13. 13. public class CustomWebViewClient extends WebViewClient{ public boolean shouldOverrideUrlLoading(WebView view, String url){ // 必ずWebViewに表示したいので, falseを返す. return false; // falseを返す. } }
  14. 14.  こうすると、Chromeを起動せずにWebViewでロードする。
  15. 15.  EditText  https://developer.android.com/reference/android/w idget/EditText.html  AndroidにはURLバーというViewは無いので、 EditTextを改造する。
  16. 16.  URLバーの更新  URLを入力するEditTextを配置しただけでは、リ ンクをクリックしたり、リダイレクトでURLが変 わった時にURLを更新してくれない。  shouldOverrideUrlLoadingでURLが変わった時、 更新後のURLをsetTextでセットする。  Zinc #8 リンク先URLをURLバーに反映
  17. 17. // MainActivity.java protected void onCreate(Bundle savedInstanceState) { //… WebView webView = (WebView)findViewById(R.id.webview); CustomWebViewClient cwvcl = new CustomWebViewClient(this); // this(MainActivity自身)を渡す. webView.setWebViewClient(cwvcl); //… }
  18. 18. // CustomWebViewClient.java public 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; } }
  19. 19.  これでURLが更新される http://yahoo.co.jp から http://m.yahoo.co.jp/ に更新
  20. 20.  HTTPの補完と省略  現在の一般的なブラウザは、URLバーにいちい ち”http://”から始まるURLを入力しなくても、 HTTPリクエストとして認識する。loadUrl は”http://”または”https://”が無いと、正しく読み込 めないので、この部分を補完する必要がある。  一方、更新時にURLバーにURLを表示する場合 は、”http”の場合は省略する場合が比較的多 い。”http”の除去が必要。  これらの処理もshouldOverrideUrlLoadingなどで 行う。  Zinc #9 ロード時のhttp補完および表示時のhttp省略
  21. 21.  URLバーでリターンキー入力でロード  これまではURLバーの隣にロード用のボタンを用意し、 それを押すとWebページのロードが開始されるような 形にしていた。  しかし、たいていのWebブラウザには、ロード用ボタ ンは無く、リターンキー(Enter, 完了)入力だけでロー ドが開始される。  これを実現するにはTextView.OnEditorActionListenerを 使う。  OnEditorActionの中に書くべき処理はいくつかのパターン があるので注意!  TextView.OnEditorActionListener  https://developer.android.com/reference/android/widget/Te xtView.OnEditorActionListener.html  Zinc #13 URLバー内でのEnterキーでWebページをロード
  22. 22. 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); } } }
  23. 23.  これでロード用のボタンは不要。 (右の×ボタンはURLバーの入力文字列をクリアするボタン)
  24. 24.  バックキーで戻る  バックキーで一つ前のページに戻るには、 onKeyDownのKEYCODE_BACKや onBackPressedで、webView.canGoBackがtrueの 時に、webView.goBackすればいい。  Zinc #10 ハードバックキーで前のページに戻る
  25. 25.  ブックマーク機能の大幅縮小(事実上廃止)  Android5.1以前はBrowser.saveBookmarkで専用ダ イアログを表示してくれたり、管理もAndroid側 でよろしくやってくれていた。  Android6からは、これらの便利なメソッドが軒並 み廃止になり、アプリ内でブックマークデータを 管理しなければならなくなった。  https://developer.android.com/about/versions/marshmall ow/android-6.0-changes.html#behavior-bookmark- browser  Zinc #6 ブックマークDBテーブルへの登録
  26. 26.  AlertDialog.builder  https://developer.android.com/reference/android/a pp/AlertDialog.Builder.html  ダイアログクラス。登録するURLの確認用に使う。  SQLiteOpenHelper  https://developer.android.com/reference/android/d atabase/sqlite/SQLiteOpenHelper.html  SQLiteのヘルパークラス。これをオーバーライド して、DBの作成やアップグレードを行う。
  27. 27. 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); } }
  28. 28.  SQLiteDatabase  https://developer.android.com/reference/android/d atabase/sqlite/SQLiteDatabase.html  ヘルパーから取得したDB本体。これでinsertや updateを行う。
  29. 29. 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);
  30. 30.  ListView  https://developer.android.com/reference/android/widget /ListView.html  ブックマークや履歴などのURLリストはリストビュー で表示する。  単体では使用せず、Adapter系クラスと組み合わせて使 う。  ArrayAdapter  https://developer.android.com/reference/android/widget /ArrayAdapter.html  実際のリストデータとリストビューの橋渡しをするク ラス。  渡されたリストデータをアイテム一つ一つどういうレ イアウトで表示するかを決定する。
  31. 31. <?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>
  32. 32. 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; } }
  33. 33. <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>
  34. 34. 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); //…
  35. 35. 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();
  36. 36.  メニュー(今回は割愛)でブックマークの登録を選択
  37. 37.  確認用ダイアログ表示
  38. 38.  ブックマークの管理を選択
  39. 39.  ブックマークに登録されている
  40. 40.  履歴も自前DBで管理  ブックマークと同様、通算の履歴もAndroid6からは、 自前で管理しなければならない。  WebBackForwardList は通算の履歴ではない  WebBackForwardList  https://developer.android.com/reference/android/webki t/WebBackForwardList.html  これはWebViewがページ遷移のどの位置にいるかをスタッ ク状に蓄積しているだけ。  ページA→ページB→ページC→戻る→戻る  このとき、 WebBackForwardListに残っているのはページ Aのみ  履歴の表示  ブックマークと同様にListViewでDBから読み込んで表 示する。
  41. 41.  履歴の登録  どのタイミングで登録すべきかは、おそらく正解 がない。  いまのところ、おそらくonPageFinishedで登録す るのが良いと思われる。  Chromeでは、ロードの途中のどこかで履歴に登 録されている模様だが、詳細は不明。
  42. 42.  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をいったんメンバに保持 しておくだけにしておくのが良いと思われる。
  43. 43.  onPageFinished  https://developer.android.com/reference/android/webkit /WebViewClient.html#onPageFinished(android.webkit. WebView, java.lang.String)  WebViewClientのハンドラメソッド  読み込みが終了した時に、ここに来る。  しかし、なぜか同じURLで2度ここに来ることがある。  例えばYahooなど  バグ?JavaScriptのせい?  そのため、最後にメンバに保持していたURLと同じ URLで、なおかつ1回目の到着時のみ、履歴DBに登録 するのがベスト(というかベター)という結論に。
  44. 44. 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++; } }
  45. 45.  タブブラウザにしたい  これまで一つのActivityに一つのWebViewを置いて ページを表示してきたが、Chromeを始め、ほと んどのブラウザはタブブラウザが基本となってい る。  タブブラウザの機能も標準として用意されている わけではなく、自前で切り替えや表示の機能を作 らなければならない。  Chrome Custom Tabsというものも最近出てきた が、どこまでカスタム出来るか不明なので今後調 査してみようと思う。  https://developer.chrome.com/multidevice/android/custo mtabs
  46. 46.  タブ系ビュー(1)  TabActivity  https://developer.android.com/reference/android/app/Ta bActivity.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で非推奨となり、こっち は非推奨が撤回された模様(?)
  47. 47.  タブ系ビュー(2)  ActionBar.Tab  https://developer.android.com/reference/android/app/Ac tionBar.Tab.html  APIレベル11(Android3)から追加  ActionBarの機能の一部としてタブの機能が追加された。  中はFragmentなので、画面切り替えとしても使いやす い。  しかし、APIレベル21(Android5)から非推奨に。
  48. 48.  タブ系ビュー(3)  FragmentTabHost  https://developer.android.com/reference/android/support/v 4/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が多い とタイトルがほとんど表示できない。
  49. 49.  タブ系ビュー(4)  PagerTabStrip  https://developer.android.com/reference/android/support/v4/ view/PagerTabStrip.html  Support.v4ライブラリにあったみたいだが試してない。  『こっちの方がよかったのでは』と後悔。  TabLayout  https://developer.android.com/reference/android/support/des ign/widget/TabLayout.html  Design.Supportライブラリにあるので比較的新しいが試して ない。  『今だとこちらがトレンドかも』とさらに後悔。  BottomNavigationView  https://developer.android.com/reference/android/support/des ign/widget/BottomNavigationView.html  下タブなので却下。
  50. 50.  概念上のタブ  FragmentTabHostまでで疲弊したので、改めてタブに ついて考えてみた。  そもそも、タブ系ビューでないといけないのか。  Google Chromeは、謎の3DなUIによって切り替えられ ている。  サードパーティー製のブラウザやWindows10Mobileな どはグリッドビューのようなタイル形式でサムネイル を選択するような形が多い。  (概念上の)タブごとにWebViewを始めとするViewがの 状態が維持されていれば、タブ系ビューにこだわる必 要は無いのでは。 (概念上の)タブの画面はそれぞれFragmentにして、 タブ切り替え用の一覧画面は専用の特殊な Fragmentにしよう。
  51. 51.  Fragment  https://developer.android.com/reference/android/a pp/Fragment.html  Activityの中に配置できる子Activityのようなもの  これを複数配置して画面を切り替えるのが一般的。  FragmentManager  https://developer.android.com/reference/android/a pp/FragmentManager.html  Activity内のFragment管理。  FragmentTransaction  https://developer.android.com/reference/android/a pp/FragmentTransaction.html  Fragment操作などを行う。
  52. 52.  add  https://developer.android.com/reference/android/app/Fr agmentTransaction.html#add(int, android.app.Fragment, java.lang.String)  Fragmentを追加する。  既に追加済みのFragmentはそのまま残る。  show/hideで表示/非表示を切り替える。  show/hideの切り替え時にFragmentライフサイクルは 発生しない  replace  https://developer.android.com/reference/android/app/Fr agmentTransaction.html#replace(int, android.app.Fragment, java.lang.String)  Fragmentを置換する。  既に追加済みのFragmentは消える。  置換なのでFragmentライフサイクルが発生する。
  53. 53.  起動時に表示する最初のタブ  FragmentTransaction.addで追加する。
  54. 54. 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++ }
  55. 55.  新しいタブの追加  FragmentTransaction.addで追加する。  Zinc #34 新しいタブの追加
  56. 56. // メニュー選択時 @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); }
  57. 57.  真っ白な新しいタブが追加された
  58. 58.  タブの切り替えボタン  メニューにタブ切り替えボタンを用意し、それを 押すと、タブ一覧画面のFragmentを表示する。
  59. 59.  タブ一覧画面  タブ一覧画面のFragmentにGridViewでタブのサム ネイルを表示する。
  60. 60. // メニュー選択時 @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バーの非表示. }
  61. 61. // フラグメントのビューのキャプチャを撮る. 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()); } } }
  62. 62. // フラグメントの追加 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; }
  63. 63. 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; }
  64. 64. // グリッドアイテムが選択された時. 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); }
  65. 65.  実際にWebブラウザを作ってみると、WebView以外の部分 がViewや機能として用意されていなかったので、自分で作 り込むのが大変だった。  ただ、Androidのよく使う機能について、全体的に学べたこ とは良かった。  今後もWebブラウザ開発、Androidについての知見を積んで いきたいとおもった。  「もっと簡単にできるよ!」「楽にできるよ!」っていう のがあったら教えていただきたい。

×