狠狠撸

狠狠撸Share a Scribd company logo
2017年1月21日
わんくま勉強会東京
B.G
※この発言は個人の見解であり、所属する組織
の公式見解ではありません
? HN: B.G
? TwitterID:
? @miu_hiro_(メイン, 仕事だけだったはず
が???。)
? @bg1_333(元メイン, 同人関係その他)
? Blog:
? 車輪のx発明 ~B.G's Blog~
? (http://bg1.hatenablog.com/)
? 言語?環境など:
? C++(とくにC++1x)わかりません
? C#わかりません
? Javaわかりません
? Win32マン
? Androidわかりません
? 最近はAndroidも多い
Androidアプリ本格開発入門 webブラウザ編
Androidわからないマンが
ブラウザを作ることになった
というお話
? はじめに
? WebView
? URLバー
? ブックマーク
? 履歴
? タブブラウザ
? まとめ
? WebView
? https://developer.android.com/reference/android/w
ebkit/WebView.html
? 指定されたURLのWebページを読み込み、適切に
表示してくれる。
? 単体でも簡易的なWebページ表示には使えるが、
本格的に使う場合は、後述するWebViewClient、
WebChromeClient、WebSettingsなどと組み合わ
せる。
? WebViewClient
? https://developer.android.com/reference/android/w
ebkit/WebViewClient.html
? WebViewのロード中のステータスに対するイベン
トハンドラを持つ。
? WebViewClientを継承した派生クラスオブジェク
トをWebViewにセットすることで、さまざまなイ
ベント処理をカスタマイズできる。
? 読み込み開始時
? 読み込み終了時
? 読み込みURL変更時
? WebChromeClient
? https://developer.android.com/reference/android/w
ebkit/WebChromeClient.html
? これもWebViewに派生クラスをセットする形で使
う。
? こちらは比較的UIに関するイベントなどのカスタ
マイズに使う。
? プログレスバーの更新
? タイトルの取得
? faviconの取得
? WebSettings
? https://developer.android.com/reference/android/w
ebkit/WebSettings.html
? WebViewのさまざまな設定を行うクラス。
? JavaScriptの有効化/無効化
? UserAgentの設定/取得
? 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にリダイレクトする動
作を防止
? オーバーライドしないと、Chromeを起動してしまう問題
shouldOverrideUrlLoadingの戻り値
true → WebViewでは処理しない
false → WebViewで処理する
public class CustomWebViewClient extends WebViewClient{
public boolean shouldOverrideUrlLoading(WebView view, String url){
// 必ずWebViewに表示したいので, falseを返す.
return false; // falseを返す.
}
}
? こうすると、Chromeを起動せずにWebViewでロードする。
? EditText
? https://developer.android.com/reference/android/w
idget/EditText.html
? AndroidにはURLバーというViewは無いので、
EditTextを改造する。
? URLバーの更新
? URLを入力するEditTextを配置しただけでは、リ
ンクをクリックしたり、リダイレクトでURLが変
わった時にURLを更新してくれない。
? shouldOverrideUrlLoadingでURLが変わった時、
更新後のURLをsetTextでセットする。
? Zinc #8 リンク先URLをURLバーに反映
// MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
//…
WebView webView = (WebView)findViewById(R.id.webview);
CustomWebViewClient cwvcl = new CustomWebViewClient(this);
// this(MainActivity自身)を渡す.
webView.setWebViewClient(cwvcl);
//…
}
// 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;
}
}
? これでURLが更新される
http://yahoo.co.jp
から
http://m.yahoo.co.jp/
に更新
? HTTPの補完と省略
? 現在の一般的なブラウザは、URLバーにいちい
ち”http://”から始まるURLを入力しなくても、
HTTPリクエストとして認識する。loadUrl
は”http://”または”https://”が無いと、正しく読み込
めないので、この部分を補完する必要がある。
? 一方、更新時にURLバーにURLを表示する場合
は、”http”の場合は省略する場合が比較的多
い。”http”の除去が必要。
? これらの処理もshouldOverrideUrlLoadingなどで
行う。
? Zinc #9 ロード時のhttp補完および表示時のhttp省略
? 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ページをロード
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);
}
}
}
? これでロード用のボタンは不要。
(右の×ボタンはURLバーの入力文字列をクリアするボタン)
? バックキーで戻る
? バックキーで一つ前のページに戻るには、
onKeyDownのKEYCODE_BACKや
onBackPressedで、webView.canGoBackがtrueの
時に、webView.goBackすればいい。
? Zinc #10 ハードバックキーで前のページに戻る
? ブックマーク機能の大幅縮小(事実上廃止)
? 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テーブルへの登録
? 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の作成やアップグレードを行う。
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);
}
}
? SQLiteDatabase
? https://developer.android.com/reference/android/d
atabase/sqlite/SQLiteDatabase.html
? ヘルパーから取得したDB本体。これでinsertや
updateを行う。
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);
? ListView
? https://developer.android.com/reference/android/widget
/ListView.html
? ブックマークや履歴などのURLリストはリストビュー
で表示する。
? 単体では使用せず、Adapter系クラスと組み合わせて使
う。
? ArrayAdapter
? https://developer.android.com/reference/android/widget
/ArrayAdapter.html
? 実際のリストデータとリストビューの橋渡しをするク
ラス。
? 渡されたリストデータをアイテム一つ一つどういうレ
イアウトで表示するかを決定する。
<?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>
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;
}
}
<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>
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);
//…
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();
? メニュー(今回は割愛)でブックマークの登録を選択
? 確認用ダイアログ表示
? ブックマークの管理を選択
? ブックマークに登録されている
? 履歴も自前DBで管理
? ブックマークと同様、通算の履歴もAndroid6からは、
自前で管理しなければならない。
? WebBackForwardList は通算の履歴ではない
? WebBackForwardList
? https://developer.android.com/reference/android/webki
t/WebBackForwardList.html
? これはWebViewがページ遷移のどの位置にいるかをスタッ
ク状に蓄積しているだけ。
? ページA→ページB→ページC→戻る→戻る
? このとき、 WebBackForwardListに残っているのはページ
Aのみ
? 履歴の表示
? ブックマークと同様にListViewでDBから読み込んで表
示する。
? 履歴の登録
? どのタイミングで登録すべきかは、おそらく正解
がない。
? いまのところ、おそらくonPageFinishedで登録す
るのが良いと思われる。
? Chromeでは、ロードの途中のどこかで履歴に登
録されている模様だが、詳細は不明。
? 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をいったんメンバに保持
しておくだけにしておくのが良いと思われる。
? 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に登録
するのがベスト(というかベター)という結論に。
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++;
}
}
? タブブラウザにしたい
? これまで一つのActivityに一つのWebViewを置いて
ページを表示してきたが、Chromeを始め、ほと
んどのブラウザはタブブラウザが基本となってい
る。
? タブブラウザの機能も標準として用意されている
わけではなく、自前で切り替えや表示の機能を作
らなければならない。
? Chrome Custom Tabsというものも最近出てきた
が、どこまでカスタム出来るか不明なので今後調
査してみようと思う。
? https://developer.chrome.com/multidevice/android/custo
mtabs
? タブ系ビュー(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で非推奨となり、こっち
は非推奨が撤回された模様(?)
? タブ系ビュー(2)
? ActionBar.Tab
? https://developer.android.com/reference/android/app/Ac
tionBar.Tab.html
? APIレベル11(Android3)から追加
? ActionBarの機能の一部としてタブの機能が追加された。
? 中はFragmentなので、画面切り替えとしても使いやす
い。
? しかし、APIレベル21(Android5)から非推奨に。
? タブ系ビュー(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が多い
とタイトルがほとんど表示できない。
? タブ系ビュー(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
? 下タブなので却下。
? 概念上のタブ
? FragmentTabHostまでで疲弊したので、改めてタブに
ついて考えてみた。
? そもそも、タブ系ビューでないといけないのか。
? Google Chromeは、謎の3DなUIによって切り替えられ
ている。
? サードパーティー製のブラウザやWindows10Mobileな
どはグリッドビューのようなタイル形式でサムネイル
を選択するような形が多い。
? (概念上の)タブごとにWebViewを始めとするViewがの
状態が維持されていれば、タブ系ビューにこだわる必
要は無いのでは。
(概念上の)タブの画面はそれぞれFragmentにして、
タブ切り替え用の一覧画面は専用の特殊な
Fragmentにしよう。
? 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操作などを行う。
? 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ライフサイクルが発生する。
? 起動時に表示する最初のタブ
? FragmentTransaction.addで追加する。
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++
}
? 新しいタブの追加
? FragmentTransaction.addで追加する。
? Zinc #34 新しいタブの追加
// メニュー選択時
@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);
}
? 真っ白な新しいタブが追加された
? タブの切り替えボタン
? メニューにタブ切り替えボタンを用意し、それを
押すと、タブ一覧画面のFragmentを表示する。
? タブ一覧画面
? タブ一覧画面のFragmentにGridViewでタブのサム
ネイルを表示する。
// メニュー選択時
@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バーの非表示.
}
// フラグメントのビューのキャプチャを撮る.
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());
}
}
}
// フラグメントの追加
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;
}
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;
}
// グリッドアイテムが選択された時.
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);
}
? 実際にWebブラウザを作ってみると、WebView以外の部分
がViewや機能として用意されていなかったので、自分で作
り込むのが大変だった。
? ただ、Androidのよく使う機能について、全体的に学べたこ
とは良かった。
? 今後もWebブラウザ開発、Androidについての知見を積んで
いきたいとおもった。
? 「もっと簡単にできるよ!」「楽にできるよ!」っていう
のがあったら教えていただきたい。

More Related Content

Similar to Androidアプリ本格開発入門 webブラウザ編 (20)

Mongo db使ってみよう
Mongo db使ってみようMongo db使ってみよう
Mongo db使ってみよう
Oda Shinsuke
?
Mongodb
MongodbMongodb
Mongodb
Satoru Mikami
?
データマイニング+奥贰叠勉强会资料第6回
データマイニング+奥贰叠勉强会资料第6回データマイニング+奥贰叠勉强会资料第6回
データマイニング+奥贰叠勉强会资料第6回
Naoyuki Yamada
?
肠辞肠辞蝉2诲-虫における叠辞虫2顿の利用方法および便利なツール
肠辞肠辞蝉2诲-虫における叠辞虫2顿の利用方法および便利なツール肠辞肠辞蝉2诲-虫における叠辞虫2顿の利用方法および便利なツール
肠辞肠辞蝉2诲-虫における叠辞虫2顿の利用方法および便利なツール
Tomoaki Shimizu
?
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
decode2016
?
Android Studioの魅力
Android Studioの魅力Android Studioの魅力
Android Studioの魅力
Keiji Ariyama
?
Inside mobage platform
Inside mobage platformInside mobage platform
Inside mobage platform
Toru Yamaguchi
?
搁虫顿补迟补厂辞耻谤肠别を狈厂顿颈蹿蹿补产濒别顿补迟补厂辞耻谤肠别へ置き换える际の罢颈辫蝉集绍介
搁虫顿补迟补厂辞耻谤肠别を狈厂顿颈蹿蹿补产濒别顿补迟补厂辞耻谤肠别へ置き换える际の罢颈辫蝉集绍介搁虫顿补迟补厂辞耻谤肠别を狈厂顿颈蹿蹿补产濒别顿补迟补厂辞耻谤肠别へ置き换える际の罢颈辫蝉集绍介
搁虫顿补迟补厂辞耻谤肠别を狈厂顿颈蹿蹿补产濒别顿补迟补厂辞耻谤肠别へ置き换える际の罢颈辫蝉集绍介
Fumiya Sakai
?
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
hokori matu
?
WordPress と Bootstrap
WordPress と BootstrapWordPress と Bootstrap
WordPress と Bootstrap
株式会社ガリレオ(开発グループ)
?
jQuery Mobile(開発編)勉強会資料
jQuery Mobile(開発編)勉強会資料jQuery Mobile(開発編)勉強会資料
jQuery Mobile(開発編)勉強会資料
Nobumasa Ura
?
D3js入門 - Code for Kobe 可視化勉強会資料
D3js入門 - Code for Kobe 可視化勉強会資料D3js入門 - Code for Kobe 可視化勉強会資料
D3js入門 - Code for Kobe 可視化勉強会資料
充彦 保田
?
苍辞诲别+蝉辞肠办别迟.颈辞+别苍肠丑补苍迟.箩蝉でチャットゲーを作る
苍辞诲别+蝉辞肠办别迟.颈辞+别苍肠丑补苍迟.箩蝉でチャットゲーを作る苍辞诲别+蝉辞肠办别迟.颈辞+别苍肠丑补苍迟.箩蝉でチャットゲーを作る
苍辞诲别+蝉辞肠办别迟.颈辞+别苍肠丑补苍迟.箩蝉でチャットゲーを作る
Kiyoshi SATOH
?
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフラインWebフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Shumpei Shiraishi
?
骋谤辞辞惫测で楽に厂蚕尝を実行してみよう
骋谤辞辞惫测で楽に厂蚕尝を実行してみよう骋谤辞辞惫测で楽に厂蚕尝を実行してみよう
骋谤辞辞惫测で楽に厂蚕尝を実行してみよう
Akira Shimosako
?
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
Shumpei Shiraishi
?
ASP.NET MVC Part 2
ASP.NET MVC Part 2ASP.NET MVC Part 2
ASP.NET MVC Part 2
Yoshitaka Seo
?
颁辞诲别滨驳苍颈迟别谤による笔丑飞颈迟迟谤
颁辞诲别滨驳苍颈迟别谤による笔丑飞颈迟迟谤颁辞诲别滨驳苍颈迟别谤による笔丑飞颈迟迟谤
颁辞诲别滨驳苍颈迟别谤による笔丑飞颈迟迟谤
kenjis
?
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
日本マイクロソフト株式会社
?
Mongo db使ってみよう
Mongo db使ってみようMongo db使ってみよう
Mongo db使ってみよう
Oda Shinsuke
?
データマイニング+奥贰叠勉强会资料第6回
データマイニング+奥贰叠勉强会资料第6回データマイニング+奥贰叠勉强会资料第6回
データマイニング+奥贰叠勉强会资料第6回
Naoyuki Yamada
?
肠辞肠辞蝉2诲-虫における叠辞虫2顿の利用方法および便利なツール
肠辞肠辞蝉2诲-虫における叠辞虫2顿の利用方法および便利なツール肠辞肠辞蝉2诲-虫における叠辞虫2顿の利用方法および便利なツール
肠辞肠辞蝉2诲-虫における叠辞虫2顿の利用方法および便利なツール
Tomoaki Shimizu
?
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
decode2016
?
Android Studioの魅力
Android Studioの魅力Android Studioの魅力
Android Studioの魅力
Keiji Ariyama
?
搁虫顿补迟补厂辞耻谤肠别を狈厂顿颈蹿蹿补产濒别顿补迟补厂辞耻谤肠别へ置き换える际の罢颈辫蝉集绍介
搁虫顿补迟补厂辞耻谤肠别を狈厂顿颈蹿蹿补产濒别顿补迟补厂辞耻谤肠别へ置き换える际の罢颈辫蝉集绍介搁虫顿补迟补厂辞耻谤肠别を狈厂顿颈蹿蹿补产濒别顿补迟补厂辞耻谤肠别へ置き换える际の罢颈辫蝉集绍介
搁虫顿补迟补厂辞耻谤肠别を狈厂顿颈蹿蹿补产濒别顿补迟补厂辞耻谤肠别へ置き换える际の罢颈辫蝉集绍介
Fumiya Sakai
?
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
hokori matu
?
jQuery Mobile(開発編)勉強会資料
jQuery Mobile(開発編)勉強会資料jQuery Mobile(開発編)勉強会資料
jQuery Mobile(開発編)勉強会資料
Nobumasa Ura
?
D3js入門 - Code for Kobe 可視化勉強会資料
D3js入門 - Code for Kobe 可視化勉強会資料D3js入門 - Code for Kobe 可視化勉強会資料
D3js入門 - Code for Kobe 可視化勉強会資料
充彦 保田
?
苍辞诲别+蝉辞肠办别迟.颈辞+别苍肠丑补苍迟.箩蝉でチャットゲーを作る
苍辞诲别+蝉辞肠办别迟.颈辞+别苍肠丑补苍迟.箩蝉でチャットゲーを作る苍辞诲别+蝉辞肠办别迟.颈辞+别苍肠丑补苍迟.箩蝉でチャットゲーを作る
苍辞诲别+蝉辞肠办别迟.颈辞+别苍肠丑补苍迟.箩蝉でチャットゲーを作る
Kiyoshi SATOH
?
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフラインWebフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Shumpei Shiraishi
?
骋谤辞辞惫测で楽に厂蚕尝を実行してみよう
骋谤辞辞惫测で楽に厂蚕尝を実行してみよう骋谤辞辞惫测で楽に厂蚕尝を実行してみよう
骋谤辞辞惫测で楽に厂蚕尝を実行してみよう
Akira Shimosako
?
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
Shumpei Shiraishi
?
颁辞诲别滨驳苍颈迟别谤による笔丑飞颈迟迟谤
颁辞诲别滨驳苍颈迟别谤による笔丑飞颈迟迟谤颁辞诲别滨驳苍颈迟别谤による笔丑飞颈迟迟谤
颁辞诲别滨驳苍颈迟别谤による笔丑飞颈迟迟谤
kenjis
?
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
日本マイクロソフト株式会社
?

More from bg1 333 (6)

はじめての転职
はじめての転职はじめての転职
はじめての転职
bg1 333
?
Windows 10 on ARM やるの?やらないの?どっちなんだい!
Windows 10 on ARM やるの?やらないの?どっちなんだい!Windows 10 on ARM やるの?やらないの?どっちなんだい!
Windows 10 on ARM やるの?やらないの?どっちなんだい!
bg1 333
?
惭础顿翱厂惭础に厂滨惭を挿してみた
惭础顿翱厂惭础に厂滨惭を挿してみた惭础顿翱厂惭础に厂滨惭を挿してみた
惭础顿翱厂惭础に厂滨惭を挿してみた
bg1 333
?
惭厂颁颁に参加してみた
惭厂颁颁に参加してみた惭厂颁颁に参加してみた
惭厂颁颁に参加してみた
bg1 333
?
Azure mobileserviceを使ってみた
Azure mobileserviceを使ってみたAzure mobileserviceを使ってみた
Azure mobileserviceを使ってみた
bg1 333
?
とある惭惫苍辞キャリアを支える技术(ガラケーからスマホへの移行)
とある惭惫苍辞キャリアを支える技术(ガラケーからスマホへの移行)とある惭惫苍辞キャリアを支える技术(ガラケーからスマホへの移行)
とある惭惫苍辞キャリアを支える技术(ガラケーからスマホへの移行)
bg1 333
?
はじめての転职
はじめての転职はじめての転职
はじめての転职
bg1 333
?
Windows 10 on ARM やるの?やらないの?どっちなんだい!
Windows 10 on ARM やるの?やらないの?どっちなんだい!Windows 10 on ARM やるの?やらないの?どっちなんだい!
Windows 10 on ARM やるの?やらないの?どっちなんだい!
bg1 333
?
惭础顿翱厂惭础に厂滨惭を挿してみた
惭础顿翱厂惭础に厂滨惭を挿してみた惭础顿翱厂惭础に厂滨惭を挿してみた
惭础顿翱厂惭础に厂滨惭を挿してみた
bg1 333
?
惭厂颁颁に参加してみた
惭厂颁颁に参加してみた惭厂颁颁に参加してみた
惭厂颁颁に参加してみた
bg1 333
?
Azure mobileserviceを使ってみた
Azure mobileserviceを使ってみたAzure mobileserviceを使ってみた
Azure mobileserviceを使ってみた
bg1 333
?
とある惭惫苍辞キャリアを支える技术(ガラケーからスマホへの移行)
とある惭惫苍辞キャリアを支える技术(ガラケーからスマホへの移行)とある惭惫苍辞キャリアを支える技术(ガラケーからスマホへの移行)
とある惭惫苍辞キャリアを支える技术(ガラケーからスマホへの移行)
bg1 333
?

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