Soumettre la recherche
Mettre en ligne
Androidアプリ本格開発入門 webブラウザ編
•
Télécharger en tant que PPTX, PDF
•
2 j'aime
•
5,517 vues
bg1 333
Suivre
Androidわからないの私がブラウザを作ることになったという話
Lire moins
Lire la suite
Logiciels
Signaler
Partager
Signaler
Partager
1 sur 66
Télécharger maintenant
Recommandé
全社情報共有サイトへのAlfresco Community 5 導入事例紹介 - 第27回Alfresco勉強会
全社情報共有サイトへのAlfresco Community 5 導入事例紹介 - 第27回Alfresco勉強会
Ryota Watabe
EventListener使いこなし術 - Symfony勉強会#10
EventListener使いこなし術 - Symfony勉強会#10
Yuichi Okada
Paging Like A Pro
Paging Like A Pro
Gabor Varadi
A deep dive into PEP-3156 and the new asyncio module
A deep dive into PEP-3156 and the new asyncio module
Saúl Ibarra Corretgé
The Integration of Laravel with Swoole
The Integration of Laravel with Swoole
Albert Chen
Keycloak開発入門
Keycloak開発入門
Yuichi Nakamura
Kiosk-mode browser using Chromium Embedded Framework (CEF)
Kiosk-mode browser using Chromium Embedded Framework (CEF)
Igalia
Spring Boot
Spring Boot
Pei-Tang Huang
Recommandé
全社情報共有サイトへのAlfresco Community 5 導入事例紹介 - 第27回Alfresco勉強会
全社情報共有サイトへのAlfresco Community 5 導入事例紹介 - 第27回Alfresco勉強会
Ryota Watabe
EventListener使いこなし術 - Symfony勉強会#10
EventListener使いこなし術 - Symfony勉強会#10
Yuichi Okada
Paging Like A Pro
Paging Like A Pro
Gabor Varadi
A deep dive into PEP-3156 and the new asyncio module
A deep dive into PEP-3156 and the new asyncio module
Saúl Ibarra Corretgé
The Integration of Laravel with Swoole
The Integration of Laravel with Swoole
Albert Chen
Keycloak開発入門
Keycloak開発入門
Yuichi Nakamura
Kiosk-mode browser using Chromium Embedded Framework (CEF)
Kiosk-mode browser using Chromium Embedded Framework (CEF)
Igalia
Spring Boot
Spring Boot
Pei-Tang Huang
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
Fabio Collini
/etc/network/interfaces について
/etc/network/interfaces について
Kazuhiro Nishiyama
Unit Test
Unit Test
guest45ac48
Testing RESTful Webservices using the REST-assured framework
Testing RESTful Webservices using the REST-assured framework
Micha Kops
Keycloakの実際・翻訳プロジェクト紹介
Keycloakの実際・翻訳プロジェクト紹介
Hiroyuki Wada
フリーでできるセキュリティWeb編(SQLMあpを楽しもう)
フリーでできるセキュリティWeb編(SQLMあpを楽しもう)
abend_cve_9999_0001
Android audio system(audioflinger)
Android audio system(audioflinger)
fefe7270
Testing RESTful web services with REST Assured
Testing RESTful web services with REST Assured
Bas Dijkstra
Introduction to Gstreamer
Introduction to Gstreamer
Rand Graham
Writing a fast HTTP parser
Writing a fast HTTP parser
fukamachi
Keycloakの紹介と最新開発動向
Keycloakの紹介と最新開発動向
Yuichi Nakamura
Kamailio - SIP Firewall for Carrier Grade Traffic
Kamailio - SIP Firewall for Carrier Grade Traffic
Daniel-Constantin Mierla
[若渴計畫] Challenges and Solutions of Window Remote Shellcode
[若渴計畫] Challenges and Solutions of Window Remote Shellcode
Aj MaChInE
第4回コンテナ型仮想化勉強会@東京 Oracle Solaris のコンテナ技術「Solaris Zones」
第4回コンテナ型仮想化勉強会@東京 Oracle Solaris のコンテナ技術「Solaris Zones」
Kazuyuki Sato
Web automation using selenium.ppt
Web automation using selenium.ppt
Ana Sarbescu
Alfresco紹介
Alfresco紹介
Tetsuya Hasegawa
C/C++プログラマのための開発ツール
C/C++プログラマのための開発ツール
MITSUNARI Shigeo
Spring Security e Spring Boot Aula - 2018
Spring Security e Spring Boot Aula - 2018
André Luiz Forchesatto
Smooth scrolling in UITableView and UICollectionView
Smooth scrolling in UITableView and UICollectionView
Andrea Prearo
Lineamientos Globales de Q.A
Lineamientos Globales de Q.A
Verul Abisai Garcia Valdes
ブラウザはつくるもんじゃない
ブラウザはつくるもんじゃない
bg1 333
使うっきゃない!iOS9で楽になったAuto Layout!
使うっきゃない!iOS9で楽になったAuto Layout!
SatoTakeshi
Contenu connexe
Tendances
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
Fabio Collini
/etc/network/interfaces について
/etc/network/interfaces について
Kazuhiro Nishiyama
Unit Test
Unit Test
guest45ac48
Testing RESTful Webservices using the REST-assured framework
Testing RESTful Webservices using the REST-assured framework
Micha Kops
Keycloakの実際・翻訳プロジェクト紹介
Keycloakの実際・翻訳プロジェクト紹介
Hiroyuki Wada
フリーでできるセキュリティWeb編(SQLMあpを楽しもう)
フリーでできるセキュリティWeb編(SQLMあpを楽しもう)
abend_cve_9999_0001
Android audio system(audioflinger)
Android audio system(audioflinger)
fefe7270
Testing RESTful web services with REST Assured
Testing RESTful web services with REST Assured
Bas Dijkstra
Introduction to Gstreamer
Introduction to Gstreamer
Rand Graham
Writing a fast HTTP parser
Writing a fast HTTP parser
fukamachi
Keycloakの紹介と最新開発動向
Keycloakの紹介と最新開発動向
Yuichi Nakamura
Kamailio - SIP Firewall for Carrier Grade Traffic
Kamailio - SIP Firewall for Carrier Grade Traffic
Daniel-Constantin Mierla
[若渴計畫] Challenges and Solutions of Window Remote Shellcode
[若渴計畫] Challenges and Solutions of Window Remote Shellcode
Aj MaChInE
第4回コンテナ型仮想化勉強会@東京 Oracle Solaris のコンテナ技術「Solaris Zones」
第4回コンテナ型仮想化勉強会@東京 Oracle Solaris のコンテナ技術「Solaris Zones」
Kazuyuki Sato
Web automation using selenium.ppt
Web automation using selenium.ppt
Ana Sarbescu
Alfresco紹介
Alfresco紹介
Tetsuya Hasegawa
C/C++プログラマのための開発ツール
C/C++プログラマのための開発ツール
MITSUNARI Shigeo
Spring Security e Spring Boot Aula - 2018
Spring Security e Spring Boot Aula - 2018
André Luiz Forchesatto
Smooth scrolling in UITableView and UICollectionView
Smooth scrolling in UITableView and UICollectionView
Andrea Prearo
Lineamientos Globales de Q.A
Lineamientos Globales de Q.A
Verul Abisai Garcia Valdes
Tendances
(20)
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
/etc/network/interfaces について
/etc/network/interfaces について
Unit Test
Unit Test
Testing RESTful Webservices using the REST-assured framework
Testing RESTful Webservices using the REST-assured framework
Keycloakの実際・翻訳プロジェクト紹介
Keycloakの実際・翻訳プロジェクト紹介
フリーでできるセキュリティWeb編(SQLMあpを楽しもう)
フリーでできるセキュリティWeb編(SQLMあpを楽しもう)
Android audio system(audioflinger)
Android audio system(audioflinger)
Testing RESTful web services with REST Assured
Testing RESTful web services with REST Assured
Introduction to Gstreamer
Introduction to Gstreamer
Writing a fast HTTP parser
Writing a fast HTTP parser
Keycloakの紹介と最新開発動向
Keycloakの紹介と最新開発動向
Kamailio - SIP Firewall for Carrier Grade Traffic
Kamailio - SIP Firewall for Carrier Grade Traffic
[若渴計畫] Challenges and Solutions of Window Remote Shellcode
[若渴計畫] Challenges and Solutions of Window Remote Shellcode
第4回コンテナ型仮想化勉強会@東京 Oracle Solaris のコンテナ技術「Solaris Zones」
第4回コンテナ型仮想化勉強会@東京 Oracle Solaris のコンテナ技術「Solaris Zones」
Web automation using selenium.ppt
Web automation using selenium.ppt
Alfresco紹介
Alfresco紹介
C/C++プログラマのための開発ツール
C/C++プログラマのための開発ツール
Spring Security e Spring Boot Aula - 2018
Spring Security e Spring Boot Aula - 2018
Smooth scrolling in UITableView and UICollectionView
Smooth scrolling in UITableView and UICollectionView
Lineamientos Globales de Q.A
Lineamientos Globales de Q.A
En vedette
ブラウザはつくるもんじゃない
ブラウザはつくるもんじゃない
bg1 333
使うっきゃない!iOS9で楽になったAuto Layout!
使うっきゃない!iOS9で楽になったAuto Layout!
SatoTakeshi
Bottom navigation
Bottom navigation
Yuki Nanri
Android2でも動くMaterialデザイン実装
Android2でも動くMaterialデザイン実装
Yusuke Konishi
【 ITベンチャーを支えるテクノロジー 】チャットワークを支える技術|Chatwork株式会社
【 ITベンチャーを支えるテクノロジー 】チャットワークを支える技術|Chatwork株式会社
leverages_event
【18-C-5】C# で iOS/Androidアプリ開発 - Visual Studio 2015 + Xamarin + MVVMCross -
【18-C-5】C# で iOS/Androidアプリ開発 - Visual Studio 2015 + Xamarin + MVVMCross -
ShinichiAoyagi
Webエンジニアなら抑えておきたい最近のOSS事情
Webエンジニアなら抑えておきたい最近のOSS事情
Atsushi Nakatsugawa
En vedette
(7)
ブラウザはつくるもんじゃない
ブラウザはつくるもんじゃない
使うっきゃない!iOS9で楽になったAuto Layout!
使うっきゃない!iOS9で楽になったAuto Layout!
Bottom navigation
Bottom navigation
Android2でも動くMaterialデザイン実装
Android2でも動くMaterialデザイン実装
【 ITベンチャーを支えるテクノロジー 】チャットワークを支える技術|Chatwork株式会社
【 ITベンチャーを支えるテクノロジー 】チャットワークを支える技術|Chatwork株式会社
【18-C-5】C# で iOS/Androidアプリ開発 - Visual Studio 2015 + Xamarin + MVVMCross -
【18-C-5】C# で iOS/Androidアプリ開発 - Visual Studio 2015 + Xamarin + MVVMCross -
Webエンジニアなら抑えておきたい最近のOSS事情
Webエンジニアなら抑えておきたい最近のOSS事情
Similaire à Androidアプリ本格開発入門 webブラウザ編
Mongo db使ってみよう
Mongo db使ってみよう
Oda Shinsuke
Mongodb
Mongodb
Satoru Mikami
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
Naoyuki Yamada
cocos2d-xにおけるBox2Dの利用方法および便利なツール
cocos2d-xにおけるBox2Dの利用方法および便利なツール
Tomoaki Shimizu
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
decode2016
Android Studioの魅力
Android Studioの魅力
Keiji Ariyama
Inside mobage platform
Inside mobage platform
Toru Yamaguchi
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
Fumiya Sakai
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
hokori matu
WordPress と Bootstrap
WordPress と Bootstrap
株式会社ガリレオ(開発グループ)
jQuery Mobile(開発編)勉強会資料
jQuery Mobile(開発編)勉強会資料
Nobumasa Ura
D3js入門 - Code for Kobe 可視化勉強会資料
D3js入門 - Code for Kobe 可視化勉強会資料
充彦 保田
node+socket.io+enchant.jsでチャットゲーを作る
node+socket.io+enchant.jsでチャットゲーを作る
Kiyoshi SATOH
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Shumpei Shiraishi
Groovyで楽にSQLを実行してみよう
Groovyで楽にSQLを実行してみよう
Akira Shimosako
JSDoc ToolKit
JSDoc ToolKit
Ryo Maruyama
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
Shumpei Shiraishi
ASP.NET MVC Part 2
ASP.NET MVC Part 2
Yoshitaka Seo
CodeIgniterによるPhwittr
CodeIgniterによるPhwittr
kenjis
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
日本マイクロソフト株式会社
Similaire à Androidアプリ本格開発入門 webブラウザ編
(20)
Mongo db使ってみよう
Mongo db使ってみよう
Mongodb
Mongodb
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
cocos2d-xにおけるBox2Dの利用方法および便利なツール
cocos2d-xにおけるBox2Dの利用方法および便利なツール
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
Android Studioの魅力
Android Studioの魅力
Inside mobage platform
Inside mobage platform
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
WordPress3.0 新デフォルトテーマ Twenty Ten 大解剖! ~秋バージョン~
WordPress と Bootstrap
WordPress と Bootstrap
jQuery Mobile(開発編)勉強会資料
jQuery Mobile(開発編)勉強会資料
D3js入門 - Code for Kobe 可視化勉強会資料
D3js入門 - Code for Kobe 可視化勉強会資料
node+socket.io+enchant.jsでチャットゲーを作る
node+socket.io+enchant.jsでチャットゲーを作る
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Webフロントエンド開発の最新トレンド - HTML5, モバイル, オフライン
Groovyで楽にSQLを実行してみよう
Groovyで楽にSQLを実行してみよう
JSDoc ToolKit
JSDoc ToolKit
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
html5j.orgがHTML5+JavaScriptで Metro Style アプリを作ってみた
ASP.NET MVC Part 2
ASP.NET MVC Part 2
CodeIgniterによるPhwittr
CodeIgniterによるPhwittr
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
【de:code 2020】 「あつまれ フロントエンドエンジニア」 Azure Static Web Apps がやってきた
Plus de bg1 333
はじめての転職
はじめての転職
bg1 333
Windows 10 on ARM やるの?やらないの?どっちなんだい!
Windows 10 on ARM やるの?やらないの?どっちなんだい!
bg1 333
MADOSMAにSIMを挿してみた
MADOSMAにSIMを挿してみた
bg1 333
MSCCに参加してみた
MSCCに参加してみた
bg1 333
Azure mobileserviceを使ってみた
Azure mobileserviceを使ってみた
bg1 333
とあるMvnoキャリアを支える技術(ガラケーからスマホへの移行)
とあるMvnoキャリアを支える技術(ガラケーからスマホへの移行)
bg1 333
Plus de bg1 333
(6)
はじめての転職
はじめての転職
Windows 10 on ARM やるの?やらないの?どっちなんだい!
Windows 10 on ARM やるの?やらないの?どっちなんだい!
MADOSMAにSIMを挿してみた
MADOSMAにSIMを挿してみた
MSCCに参加してみた
MSCCに参加してみた
Azure mobileserviceを使ってみた
Azure mobileserviceを使ってみた
とあるMvnoキャリアを支える技術(ガラケーからスマホへの移行)
とあるMvnoキャリアを支える技術(ガラケーからスマホへの移行)
Androidアプリ本格開発入門 webブラウザ編
1.
2017年1月21日 わんくま勉強会東京 B.G
2.
※この発言は個人の見解であり、所属する組織 の公式見解ではありません
3.
HN: B.G
TwitterID: @miu_hiro_(メイン, 仕事だけだったはず が・・・。) @bg1_333(元メイン, 同人関係その他) Blog: 車輪のx発明 ~B.G's Blog~ (http://bg1.hatenablog.com/)
4.
言語・環境など: C++(とくにC++1x)わかりません
C#わかりません Javaわかりません Win32マン Androidわかりません 最近はAndroidも多い
5.
6.
Androidわからないマンが ブラウザを作ることになった というお話
7.
はじめに WebView
URLバー ブックマーク 履歴 タブブラウザ まとめ
8.
WebView https://developer.android.com/reference/android/w ebkit/WebView.html
指定されたURLのWebページを読み込み、適切に 表示してくれる。 単体でも簡易的なWebページ表示には使えるが、 本格的に使う場合は、後述するWebViewClient、 WebChromeClient、WebSettingsなどと組み合わ せる。
9.
WebViewClient https://developer.android.com/reference/android/w ebkit/WebViewClient.html
WebViewのロード中のステータスに対するイベン トハンドラを持つ。 WebViewClientを継承した派生クラスオブジェク トをWebViewにセットすることで、さまざまなイ ベント処理をカスタマイズできる。 読み込み開始時 読み込み終了時 読み込みURL変更時
10.
WebChromeClient https://developer.android.com/reference/android/w ebkit/WebChromeClient.html
これもWebViewに派生クラスをセットする形で使 う。 こちらは比較的UIに関するイベントなどのカスタ マイズに使う。 プログレスバーの更新 タイトルの取得 faviconの取得
11.
WebSettings https://developer.android.com/reference/android/w ebkit/WebSettings.html
WebViewのさまざまな設定を行うクラス。 JavaScriptの有効化/無効化 UserAgentの設定/取得
12.
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にリダイレクトする動 作を防止
13.
オーバーライドしないと、Chromeを起動してしまう問題 shouldOverrideUrlLoadingの戻り値 true →
WebViewでは処理しない false → WebViewで処理する
14.
public class CustomWebViewClient
extends WebViewClient{ public boolean shouldOverrideUrlLoading(WebView view, String url){ // 必ずWebViewに表示したいので, falseを返す. return false; // falseを返す. } }
15.
こうすると、Chromeを起動せずにWebViewでロードする。
16.
EditText https://developer.android.com/reference/android/w idget/EditText.html
AndroidにはURLバーというViewは無いので、 EditTextを改造する。
17.
URLバーの更新 URLを入力するEditTextを配置しただけでは、リ ンクをクリックしたり、リダイレクトでURLが変 わった時にURLを更新してくれない。
shouldOverrideUrlLoadingでURLが変わった時、 更新後のURLをsetTextでセットする。 Zinc #8 リンク先URLをURLバーに反映
18.
// MainActivity.java protected void
onCreate(Bundle savedInstanceState) { //… WebView webView = (WebView)findViewById(R.id.webview); CustomWebViewClient cwvcl = new CustomWebViewClient(this); // this(MainActivity自身)を渡す. webView.setWebViewClient(cwvcl); //… }
19.
// 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; } }
20.
これでURLが更新される http://yahoo.co.jp から http://m.yahoo.co.jp/ に更新
21.
HTTPの補完と省略 現在の一般的なブラウザは、URLバーにいちい ち”http://”から始まるURLを入力しなくても、 HTTPリクエストとして認識する。loadUrl は”http://”または”https://”が無いと、正しく読み込 めないので、この部分を補完する必要がある。
一方、更新時にURLバーにURLを表示する場合 は、”http”の場合は省略する場合が比較的多 い。”http”の除去が必要。 これらの処理もshouldOverrideUrlLoadingなどで 行う。 Zinc #9 ロード時のhttp補完および表示時のhttp省略
22.
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ページをロード
23.
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); } } }
24.
これでロード用のボタンは不要。 (右の×ボタンはURLバーの入力文字列をクリアするボタン)
25.
バックキーで戻る バックキーで一つ前のページに戻るには、 onKeyDownのKEYCODE_BACKや onBackPressedで、webView.canGoBackがtrueの 時に、webView.goBackすればいい。
Zinc #10 ハードバックキーで前のページに戻る
26.
ブックマーク機能の大幅縮小(事実上廃止) 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テーブルへの登録
27.
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の作成やアップグレードを行う。
28.
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); } }
29.
SQLiteDatabase https://developer.android.com/reference/android/d atabase/sqlite/SQLiteDatabase.html
ヘルパーから取得したDB本体。これでinsertや updateを行う。
30.
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);
31.
ListView https://developer.android.com/reference/android/widget /ListView.html
ブックマークや履歴などのURLリストはリストビュー で表示する。 単体では使用せず、Adapter系クラスと組み合わせて使 う。 ArrayAdapter https://developer.android.com/reference/android/widget /ArrayAdapter.html 実際のリストデータとリストビューの橋渡しをするク ラス。 渡されたリストデータをアイテム一つ一つどういうレ イアウトで表示するかを決定する。
32.
<?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>
33.
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; } }
34.
<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>
35.
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); //…
36.
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();
37.
メニュー(今回は割愛)でブックマークの登録を選択
38.
確認用ダイアログ表示
39.
ブックマークの管理を選択
40.
ブックマークに登録されている
41.
履歴も自前DBで管理 ブックマークと同様、通算の履歴もAndroid6からは、 自前で管理しなければならない。
WebBackForwardList は通算の履歴ではない WebBackForwardList https://developer.android.com/reference/android/webki t/WebBackForwardList.html これはWebViewがページ遷移のどの位置にいるかをスタッ ク状に蓄積しているだけ。 ページA→ページB→ページC→戻る→戻る このとき、 WebBackForwardListに残っているのはページ Aのみ 履歴の表示 ブックマークと同様にListViewでDBから読み込んで表 示する。
42.
履歴の登録 どのタイミングで登録すべきかは、おそらく正解 がない。
いまのところ、おそらくonPageFinishedで登録す るのが良いと思われる。 Chromeでは、ロードの途中のどこかで履歴に登 録されている模様だが、詳細は不明。
43.
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をいったんメンバに保持 しておくだけにしておくのが良いと思われる。
44.
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に登録 するのがベスト(というかベター)という結論に。
45.
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++; } }
46.
タブブラウザにしたい これまで一つのActivityに一つのWebViewを置いて ページを表示してきたが、Chromeを始め、ほと んどのブラウザはタブブラウザが基本となってい る。
タブブラウザの機能も標準として用意されている わけではなく、自前で切り替えや表示の機能を作 らなければならない。 Chrome Custom Tabsというものも最近出てきた が、どこまでカスタム出来るか不明なので今後調 査してみようと思う。 https://developer.chrome.com/multidevice/android/custo mtabs
47.
タブ系ビュー(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で非推奨となり、こっち は非推奨が撤回された模様(?)
48.
タブ系ビュー(2) ActionBar.Tab
https://developer.android.com/reference/android/app/Ac tionBar.Tab.html APIレベル11(Android3)から追加 ActionBarの機能の一部としてタブの機能が追加された。 中はFragmentなので、画面切り替えとしても使いやす い。 しかし、APIレベル21(Android5)から非推奨に。
49.
タブ系ビュー(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が多い とタイトルがほとんど表示できない。
50.
タブ系ビュー(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 下タブなので却下。
51.
概念上のタブ FragmentTabHostまでで疲弊したので、改めてタブに ついて考えてみた。
そもそも、タブ系ビューでないといけないのか。 Google Chromeは、謎の3DなUIによって切り替えられ ている。 サードパーティー製のブラウザやWindows10Mobileな どはグリッドビューのようなタイル形式でサムネイル を選択するような形が多い。 (概念上の)タブごとにWebViewを始めとするViewがの 状態が維持されていれば、タブ系ビューにこだわる必 要は無いのでは。 (概念上の)タブの画面はそれぞれFragmentにして、 タブ切り替え用の一覧画面は専用の特殊な Fragmentにしよう。
52.
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操作などを行う。
53.
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ライフサイクルが発生する。
54.
起動時に表示する最初のタブ FragmentTransaction.addで追加する。
55.
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++ }
56.
新しいタブの追加 FragmentTransaction.addで追加する。
Zinc #34 新しいタブの追加
57.
// メニュー選択時 @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); }
58.
真っ白な新しいタブが追加された
59.
タブの切り替えボタン メニューにタブ切り替えボタンを用意し、それを 押すと、タブ一覧画面のFragmentを表示する。
60.
タブ一覧画面 タブ一覧画面のFragmentにGridViewでタブのサム ネイルを表示する。
61.
// メニュー選択時 @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バーの非表示. }
62.
// フラグメントのビューのキャプチャを撮る. 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()); } } }
63.
// フラグメントの追加 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; }
64.
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; }
65.
// グリッドアイテムが選択された時. 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); }
66.
実際にWebブラウザを作ってみると、WebView以外の部分 がViewや機能として用意されていなかったので、自分で作 り込むのが大変だった。 ただ、Androidのよく使う機能について、全体的に学べたこ とは良かった。
今後もWebブラウザ開発、Androidについての知見を積んで いきたいとおもった。 「もっと簡単にできるよ!」「楽にできるよ!」っていう のがあったら教えていただきたい。
Télécharger maintenant