Habr for Androidのクライアントを作成します

将来を見据えて、これは何が起こったかです:

画像





12:56。 これは、トピックの作成と並行して行います(とても興味深い)。 すべてのステップを説明するクライアントを書く過程で。 そこで、喫煙してお茶を注ぎ、プレイリストを準備し、お茶が冷えている間に、habrahabrという名前が市場で忙しいかどうかを確認します。 では、アプリケーションの作成に移りましょう。



13:02新しいプロジェクトを作成しています。

スクリーンショット



APIレベルが4である理由は、Samsung Galaxy Tabタブレットでは、画面の解像度が不正確になり、これらの奇跡的なデバイスの所有者が市場に多くのマイナスを入れることに失敗することはありません(原則として、これは開発者のカントです)。



13:08 Fiximマニフェスト。

次の2行を追加する必要があります。

-android:configChanges = "orientation"、この行は、画面の向きを変更してもアクティビティが破壊されないようにするために必要です。

-<uses-permission android:name = "android.permission.INTERNET" />、インターネットへのアクセス許可をリクエスト

AndroidManifest

* front242をプレイ-headhunter v3.0



13:13 Fiximレイアウト。

すべてを消去し、1つの要素を追加-WebView-フルスクリーン



13:16アイコン。

firebogの助けを借りて、絶対リンクをトリミングし、Photoshopの助けを借りて48 * 48ピクセルにトリミングし、res / drawableをスローします...



13:27アイコンにより、すべてがより複雑になりました。 縮小後のロゴの染みは泥だらけになりました。 著者が気分を害さないことを願っています。



ほら、私たちは最も難しいことを終えました、ついにあなたはうろついています



13:39ダウンロードHabr

public class habr extends Activity {



private WebView wv;



/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);



wv = (WebView) findViewById(R.id.wv);



WebSettings webSettings = wv.getSettings();

webSettings.setSavePassword( true );

webSettings.setSaveFormData( true );

webSettings.setJavaScriptEnabled( true );



wv.loadUrl( "http://habrahabr.ru" );

}

}




* This source code was highlighted with Source Code Highlighter .








*ここでは、javascriptを有効にしてフォーム/パスワードを記憶した後、habrにビューを設定します。 今のところ見苦しいが、すでに機能している。 煙突。



13:53会話を続けます。



public class habr extends Activity {



private WebView wv;

private String LASTURL = "" ;



/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

this .getWindow().requestFeature(Window.FEATURE_PROGRESS);

setContentView(R.layout.main);



wv = (WebView) findViewById(R.id.wv);



WebSettings webSettings = wv.getSettings();

webSettings.setSavePassword( true );

webSettings.setSaveFormData( true );

webSettings.setJavaScriptEnabled( true );



final Activity activity = this ;



wv.setWebChromeClient( new WebChromeClient() {

public void onProgressChanged(WebView view, int progress)

{

activity.setTitle( " " +LASTURL);

activity.setProgress(progress * 100);



if (progress == 100)

activity.setTitle( " " +LASTURL);

}

});

wv.setWebViewClient( new WebViewClient() {

public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {

Toast.makeText(getApplicationContext(), "Error: " + description+ " " + failingUrl, Toast.LENGTH_LONG).show();

}



@Override

public boolean shouldOverrideUrlLoading(WebView view, String url)

{

if (url.indexOf( "habrahabr" )<=0) {

// the link is not for a page on my site, so launch another Activity that handles URLs

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));

startActivity(intent);

return true ;

}

return false ;

}



public void onPageStarted (WebView view, String url, Bitmap favicon) {

LASTURL = url;

}



public void onPageFinished (WebView view, String url) {



}

});



wv.loadUrl( "http://habrahabr.ru" );

}

}




* This source code was highlighted with Source Code Highlighter .








したがって、ローディング温度計が必要です。

1.機能をリクエストします:this.getWindow()。RequestFeature(Window.FEATURE_PROGRESS);

2. progresschangeで-温度計ウィンドウを埋めます

activity.setTitle( "" + LASTURL);

activity.setProgress(進行状況* 100);

if(progress == 100)activity.setTitle( "" + LASTURL);

3.ロードの開始時に、変数LASTURL = urlのurlを覚えておいてください。



無効化されたエラーでWi-Fiのフォールオフを処理します。

Toast.makeText(getApplicationContext()、 "エラー:" +説明+ "" + failingUrl、Toast.LENGTH_LONG).show();

(絶対にバカにメッセージが表示されますが、すべてがページに書き込まれます)



14:05 Fiximレイアウト。

ちょっとした理論。 ロードされたページでjavascriptを実行できます。 たとえば、ブラウザのアドレスバーにjavascript:alert(document.body.innerHTML)のように入力すると、ページの本文が表示されます(上記の例ではコピーペースト-ロシア語であるため、パーサーはスキップします)。

それでは、HOUSEツリーなどは、少なくともページを完全にやり直して、あなたが望むことをします。 ただし、ここまでは行かず(読みたい)、サイドバーを非表示にして読みやすくします。 また、ブラウザ自体がページ全体に有用なコンテンツを拡大します。 そのため、ページの読み込みの最後にハンドラーを追加しようとします。



public void onPageFinished (WebView view, String url) {

view.loadUrl( "javascript:(function() { " +

"hide('sidebar');" +

"function hide(id){if (document.getElementById(id)){document.getElementById(id).style['display'] = 'none';}}" +

"})()" );

}




* This source code was highlighted with Source Code Highlighter .








/ *仕事に気を取られる* /

ダックスフント、サイドバーは隠れていますが、ある程度の飛躍があります。 画像のアップロードを修正:



14:34ダウンロードを高速化する

これを行うには、ページの開始時に画像をオフにします。

view.getSettings()。setLoadsImagesAutomatically(false);

javascriptでハックした後、フィニッシュでオンにします。

view.getSettings()。setLoadsImagesAutomatically(true);



ダックスフント、コンテンツの読み込みが大幅に高速化されました(私の死んだIotaでは、少なくともエミュレーターで)



14:37昼食



15:23会話を続ける

昼食に行ったとき、無料のWi-Fiゾーンを調べ、同時にテストしました。 残念ながら、空の右隅に検索バーが不条理にぶら下がっています。 それで何かを試してみましょう。

まず第一に、愚かに隠します。



*音楽:submatakana-クリプト(これには何かがあります)



"hideByClass('panel-tools');" +

"function hideByClass(c){var e=document.getElementsByClassName(c);for(var i=0;i<e.length;i++){e[i].style['display'] = 'none';}}" +




* This source code was highlighted with Source Code Highlighter .








うーん、壊れるように構築しないでください。 ダックスフント、「ブログのリストに追加してみてください。



"var parent = document.getElementsByClassName('page-navigation')[0];" +

"var panel = document.getElementsByClassName('panel-tools')[0];" +

"var div = document.createElement('div');" +

"div.innerHTML = panel.innerHTML;" +

"parent.appendChild(div);" +




* This source code was highlighted with Source Code Highlighter .








ops、2つのパネルがあります)

"panel.innerHTML = '';" +

"div.style['margin-left'] = '30px;'" +




* This source code was highlighted with Source Code Highlighter .








15:57おっと、引用符が1つありません。スクリプト全体がカードの家のようにクラッシュしています。

次のようになります。div.style ['margin-left'] = '30px';

そのため、メニューの広告ユニットを削減しようとしますが、いや、そうではないので、私は長い間罪深い組版をいじっていました(嫌いです)。



16:04これらの要素には名前が付けられていないため、これを試しました。

"var urls=document.getElementsByTagName('a');for(var i=0;i<urls.length;i++){if (urls[i].target='_top'){urls[i].appendChild(document.createTextNode(''));}}" +



* This source code was highlighted with Source Code Highlighter .






融合していない



16:16エリアをヒットしてみましょう:

"var imgs=document.getElementsByTagName('IMG');for(var i=0;i<imgs.length;i++){if (imgs[i].height=60) {imgs[i].src='';imgs[i].width=0;} }" +



* This source code was highlighted with Source Code Highlighter .






写真は消えましたが、空のスペースはまだ残っています(さて、希望する人のために残しておいてください。今のところ広告主の喜びのために彼らを生きさせてください。

煙突。



16:45幅を少し狭めた検索に戻りましょう。

"var panel = document.getElementById('search');" +

"panel.style['width'] = '55px';" +




* This source code was highlighted with Source Code Highlighter .








さて、最後に、Androidに取り掛かりましょう。

16:47ハードウェアボタンを重ねます。







  1. @Override
  2. public boolean onKeyDown( int keyCode、KeyEvent event ){
  3. if ((keyCode == KeyEvent.KEYCODE_BACK)&& wv.canGoBack()){
  4. wv.goBack();
  5. trueを 返し ます
  6. }
  7. return super.onKeyDown(keyCode、 event );
  8. }
*このソースコードは、 ソースコードハイライターで強調表示されました。




16:57メニューを作成します







  1. @Override
  2. public boolean onCreateOptionsMenu(メニューメニュー)
  3. {
  4. super.onCreateOptionsMenu(メニュー);
  5. この .myMenu = menu;
  6. MenuItem item = menu.add(0、1、0、 "メインページ" );
  7. item.setIcon(R.drawable.home);
  8. MenuItem item2 = menu.add(0、2、0、 "BACK" );
  9. item2.setIcon(R.drawable.arrowleft);
  10. MenuItem item3 = menu.add(0、3、0、 "F5" );
  11. item3.setIcon(R.drawable.s);
  12. MenuItem item4 = menu.add(0、4、0、 "CLEAR CACHE" );
  13. item4.setIcon(R.drawable.trash);
  14. MenuItem item5 = menu.add(0、5、0、 "VOID" );
  15. item5.setIcon(R.drawable.vote);
  16. trueを 返し ます
  17. }
  18. @Override
  19. public boolean onOptionsItemSelected(MenuItemアイテム){
  20. スイッチ (item.getItemId())
  21. {
  22. ケース 1:
  23. wv.loadUrl( "http://habrahabr.ru" );
  24. 休憩 ;
  25. ケース 2:
  26. if (wv.canGoBack()){
  27. wv.goBack();
  28. }
  29. 休憩 ;
  30. ケース 3:
  31. wv.loadUrl(LASTURL);
  32. 休憩 ;
  33. ケース 4:
  34. wv.clearCache( true );
  35. 休憩 ;
  36. ケース 5:
  37. Intent marketIntent2 = 新しい Intent(Intent.ACTION_VIEW、Uri.parse(
  38. "http://market.android.com/details?id=" + getPackageName()));
  39. startActivity(marketIntent2);
  40. 休憩 ;
  41. }
  42. trueを 返し ます
  43. }
*このソースコードは、 ソースコードハイライターで強調表示されました。




ここでは、特に何も説明しないために...

WebViewはデータをローカルの分離キャッシュに保存し、clearCache関数-キャッシュされた画像などを削除します。



Intent +アクティベーション開始の助けを借りてユーザーの市場に送ります。これは外部アプリケーションとやり取りするための標準的なメカニズムです。



17:01税金、おそらく写真付き/写真なしのモードを作成する価値があります

Fiximメニュー:

menu.add(0、6、0、“ IMG ON”);

menu.add(0、7、0、“ IMG OFF”);



17:05 Mutim保存機能の設定





  1. private void saveSettings(ブール値)
  2. {
  3. SharedPreferences設定= getSharedPreferences(PREFS_NAME、0);
  4. SharedPreferences.Editor editor = settings.edit();
  5. editor.putBoolean( "IMGMODE" 、val);
  6. editor.commit();
  7. }
*このソースコードは、 ソースコードハイライターで強調表示されました。




設定を転送し(画像をロードするかしないか)、渡された値を変数に詰め込みます。

(定数PREFS_NAMEは上記で宣言されました-これは、いわば、構成の名前です)



次に、メニューハンドラで呼び出します。





  1. ケース 6:
  2. saveSettings( true );
  3. 休憩 ;
  4. ケース 7:
  5. saveSettings( false );
  6. 休憩 ;
*このソースコードは、 ソースコードハイライターで強調表示されました。




17:18そして、設定からアプリケーションを作成するときに定数を読み取ります





  1. SharedPreferences設定= getSharedPreferences(PREFS_NAME、0);
  2. imgOn = settings.getBoolean( "IMGMODE"false );
  3. webSettings.setLoadsImagesAutomatically(imgOn);
*このソースコードは、 ソースコードハイライターで強調表示されました。




17:19再テスト



17:25在庫がないようです。 プロジェクトをエクスポートします。

Eclipseでは、これはエクスポートプロジェクトの右ボタンです。ウィザードが起動し、証明書とパッケージングプロジェクトを作成/選択できます。 次に、 市場に踏み込む



17:38公開。

実際には簡単なタスクではありません。 どこかで特定のサイズのプロモグラフィックを大量に取得する必要があるので、デザイナーにとっては難しいことではありません。



パラメータによると。

-ロシア語をメインアプリケーション言語としてインストールするには、最初にロシア語を追加する必要があります。その後でのみ、英語を削除できます。

-説明フィールドとプロモートフィールドでは、アプリケーションが検索できるキーワードを記載することが望ましいです。

-あなたが価格を無料に設定した場合-それを支払った-それは不可能です

-コピー防止ボックスをオンにしないでください。一部のデバイスにはアプリケーションがインストールされません。

-たとえばロシア語話者のみを対象とするアプリケーションであっても、すべての国を指定する方が適切です。



17:50公開。



市場からダウンロードする

ソースコードをダウンロードする



うーん、アイコンがうまく機能しませんでした(何らかの理由で、この考えは私をかじります。だから誰かが48 * 48を描くことができれば、私は非常に感謝します。



完全に、最終的なソース:





  1. パッケージru.habrahabr.android;
  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.content.SharedPreferences;
  5. import android.graphics.Bitmap;
  6. import android.net.Uri;
  7. import android.os.Bundle;
  8. import android.view.KeyEvent;
  9. import android.view.Menu;
  10. import android.view.MenuItem;
  11. import android.view.Window;
  12. import android.webkit.WebChromeClient;
  13. import android.webkit.WebSettings;
  14. import android.webkit.WebView;
  15. import android.webkit.WebViewClient;
  16. import android.widget.Toast;
  17. パブリック クラス habrはActivity {
  18. プライベート Webビューwv;
  19. private String LASTURL = "" ;
  20. メニューmyMenu = null ;
  21. private static final String PREFS_NAME = "MyPrefs" ;
  22. private Boolean imgOn;
  23. / **アクティビティが最初に作成されたときに呼び出されます。 * /
  24. @Override
  25. public void onCreate(バンドルsavedInstanceState){
  26. super.onCreate(savedInstanceState);
  27. this .getWindow()。requestFeature(Window.FEATURE_PROGRESS);
  28. setContentView(R.layout.main);
  29. wv =(WebView)findViewById(R.id.wv);
  30. WebSettings webSettings = wv.getSettings();
  31. webSettings.setSavePassword( true );
  32. webSettings.setSaveFormData( true );
  33. webSettings.setJavaScriptEnabled( true );
  34. SharedPreferences設定= getSharedPreferences(PREFS_NAME、0);
  35. imgOn = settings.getBoolean( "IMGMODE"false );
  36. webSettings.setLoadsImagesAutomatically(imgOn);
  37. 最終アクティビティactivity = this ;
  38. wv.setWebChromeClient( new WebChromeClient(){
  39. public void onProgressChanged(WebViewビュー、 int progress)
  40. {
  41. activity.setTitle( "" + LASTURL);
  42. activity.setProgress(進行状況* 100);
  43. if (進行== 100)
  44. activity.setTitle( "" + LASTURL);
  45. }
  46. });
  47. wv.setWebViewClient( new WebViewClient(){
  48. public void onReceivedError(WebViewビュー、 int errorCode、文字列の説明、文字列failingUrl){
  49. Toast.makeText(getApplicationContext()、 "エラー:" +説明+ "" + failingUrl、Toast.LENGTH_LONG).show();
  50. }
  51. @Override
  52. public boolean shouldOverrideUrlLoading(WebViewビュー、文字列url)
  53. {
  54. if (url.indexOf( "habrahabr" )<= 0){
  55. //リンクはサイトのページ用ではないため、URLを処理する別のアクティビティを起動します
  56. Intent intent = new Intent(Intent.ACTION_VIEW、Uri.parse(url));
  57. startActivity(意図);
  58. trueを 返し ます
  59. }
  60. falseを 返し ます
  61. }
  62. public void onPageStarted(WebViewビュー、文字列URL、ビットマップファビコン){
  63. LASTURL = url;
  64. view.getSettings()。setLoadsImagesAutomatically( false );
  65. }
  66. public void onPageFinished(WebViewビュー、文字列url){
  67. view.loadUrl( "javascript:(function(){" +
  68. 「hide( 'sidebar');」 +
  69. // "var parent = document.getElementsByClassName( 'page-navigation')[0];" +
  70. // "var panel = document.getElementsByClassName( 'panel-tools')[0];" +
  71. // "var div = document.createElement( 'div');" +
  72. //"div.innerHTML = panel.innerHTML; "+
  73. //"parent.appendChild(div);"+
  74. //"panel.innerHTML = ''; "+
  75. //"div.style['margin-left '] =' 31px '; "+
  76. 「var panel = document.getElementById( 'search');」 +
  77. 「panel.style ['width'] = '55px';」 +
  78. // "var imgs = document.getElementsByTagName( 'IMG'); for(var i = 0; i <imgs.length; i ++){if(imgs [i] .height = 60){imgs [i] .src = ''; imgs [i] .width = 0;}} "+
  79. // "var urls = document.getElementsByTagName( 'li'); for(var i = 0; i <urls.length; i ++){if(urls [i] .style = 'margin:-14px 0pt 0pt;')) {urls [i] .style ['display'] = 'none';}} "+
  80. // "hideByClass( 'panel-tools');" +
  81. 「関数の非表示(id){if(document.getElementById(id)){document.getElementById(id).style ['display'] = 'none';}} " +
  82. // "関数hideByClass(c){var e = document.getElementsByClassName(c); for(var i = 0; i <e.length; i ++){e [i] .style ['display'] = 'none' ;}} "+
  83. "})()" );
  84. if (imgOn)view.getSettings()。setLoadsImagesAutomatically( true );
  85. }
  86. });
  87. wv.loadUrl( "http://habrahabr.ru" );
  88. }
  89. @Override
  90. public boolean onKeyDown( int keyCode、KeyEvent event ){
  91. if ((keyCode == KeyEvent.KEYCODE_BACK)&& wv.canGoBack()){
  92. wv.goBack();
  93. trueを 返し ます
  94. }
  95. return super.onKeyDown(keyCode、 event );
  96. }
  97. @Override
  98. public boolean onCreateOptionsMenu(メニューメニュー)
  99. {
  100. super.onCreateOptionsMenu(メニュー);
  101. この .myMenu = menu;
  102. MenuItem item = menu.add(0、1、0、 "メインページ" );
  103. item.setIcon(R.drawable.home);
  104. MenuItem item2 = menu.add(0、2、0、 "BACK" );
  105. item2.setIcon(R.drawable.arrowleft);
  106. MenuItem item3 = menu.add(0、3、0、 "F5" );
  107. item3.setIcon(R.drawable.s);
  108. MenuItem item4 = menu.add(0、4、0、 "CLEAR CACHE" );
  109. item4.setIcon(R.drawable.trash);
  110. MenuItem item5 = menu.add(0、5、0、 "VOID" );
  111. item5.setIcon(R.drawable.vote);
  112. menu.add(0、6、0、 "IMG ON" );
  113. menu.add(0、7、0、 "IMG OFF" );
  114. trueを 返し ます
  115. }
  116. @Override
  117. public boolean onOptionsItemSelected(MenuItemアイテム){
  118. スイッチ (item.getItemId())
  119. {
  120. ケース 1:
  121. wv.loadUrl( "http://habrahabr.ru" );
  122. 休憩 ;
  123. ケース 2:
  124. if (wv.canGoBack()){
  125. wv.goBack();
  126. }
  127. 休憩 ;
  128. ケース 3:
  129. wv.loadUrl(LASTURL);
  130. 休憩 ;
  131. ケース 4:
  132. wv.clearCache( true );
  133. 休憩 ;
  134. ケース 5:
  135. Intent marketIntent2 = 新しい Intent(Intent.ACTION_VIEW、Uri.parse(
  136. "http://market.android.com/details?id=" + getPackageName()));
  137. startActivity(marketIntent2);
  138. 休憩 ;
  139. ケース 6:
  140. saveSettings( true );
  141. 休憩 ;
  142. ケース 7:
  143. saveSettings( false );
  144. 休憩 ;
  145. }
  146. trueを 返し ます
  147. }
  148. private void saveSettings(ブール値)
  149. {
  150. SharedPreferences設定= getSharedPreferences(PREFS_NAME、0);
  151. SharedPreferences.Editor editor = settings.edit();
  152. editor.putBoolean( "IMGMODE" 、val);
  153. editor.commit();
  154. }
  155. }
*このソースコードは、 ソースコードハイライターで強調表示されました。




UPD :レイアウトを再設計しました。

彼は、 almalexa.habrahabr.ruによって開発されたスタイルを基本とし、小さな解像度のファイルで大幅に修正しました。

結果のスタイル: userstyles.org/styles/46932/habr

これで開発は完了したと思います。 市場の顧客-更新されました。

合計:すべてに約1日かかりました。

画像







All Articles