Android

2011年2月16日水曜日

GoogleMapをIntentで表示する。

GoogleMapをIntentで表示するだけなら簡単なのだが、パラメータの渡し方に色々とくせがあるようです。

まず、GoogleMapをIntentで起動し表示する基本的なコードは次の通りです。


public class GoogleMapTestActivity extends Activity {
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  
  String mapurl = "geo:38.230844,140.323316";  //←ここの書き方で動作が微妙に変わる!

  Intent intent = new Intent();
  intent.setAction(Intent.ACTION_VIEW);  intent.setClassName("com.google.android.apps.maps","com.google.android.maps.MapsActivity");  intent.setData(Uri.parse(mapurl));
  startActivity(intent);
 }
}
ここで、mapurlの部分の書き方によりいろんなパターンが指定できます。
ちなみにこの情報はGoogleMap アプリのバージョンで変わる可能性があります。
  1. 座標指定

  2. mapurl = "geo:latitude,longitude"と指定した場合、 指定位置を中心とした地図を表示する。
    その位置にPinは表示しない。
    戻るボタン一回で元アプリに戻る。

  3. ピンを立てたい

  4. mapurl = "geo:0,0?q=latitude,longitude"と指定した場合、 最初に(0,0)を指定、つまり現在位置の地図を表示する。
    その後、latitude,longitudeで指定した位置を検索し検索位置を中心とした地図に移動する。
    (検索位置への移動はアニメーション効果あり。)
    0,0の代わりにlatitude,longitudeを指定すれば移動アニメーションは発生しないが、やはり動作は2段階になる。
    指定位置にはピンを表示する。
    戻るボタンは一回目で検索前の画面(つまりピンがない状態)に戻り、 2回目で元のアプリに戻る。
    プログラム的には微妙に使いにくい。

  5. ズーム指定

  6. mapurl = "geo:latitude,longitude?z=xx"でズーム指定ができます。
    xxは1~23で1を指定すると全世界表示、23で最大拡大になります。
    ただし、位置を指定して?z=1を指定するとエラーになることがある!
    あくまでめやすですが、13~15で市町村サイズ・10~11で都道府県サイズ・6で日本全土サイズくらい。
    今のところ、上記2の検索とズーム指定は組み合わせできないようだ…。
と言うことで、IntentでGoogleMapを表示してみたけど、やはりキチンとした表示を行うならMapViewを使って自ら制御するしかない??

2011年2月12日土曜日

AndroidでLibraryを使う(Part2)

さらに前回の続き…。

前回はビルド・パスの構成にてライブラリを追加しましたが、この方法だとLibrary内のリソースが参照できない;;
<libraryPackageName>.R.xxxとかでLibrary側のR.javaのリソースIDは参照できるようだが、そのIDを使ってgetString()とかやってもLibrary側のリソースは取得できない。

さらにLibraryの内部でリソースにアクセスする場合にもcontextが必要で、contextはプロジェクトのMain側で取得するため、プロジェクトのMain側のリソースにアクセスすることはできるが、Library側のリソースにはアクセスできません。

どうやら、リソースを参照するにはライブラリのソースパスごとプロジェクトに含めてやる必要がありそうです。
(どうにかなんないのかな??リソースを使うLibraryはJARで配布できないんだろうか?)

具体的には、

  • プロジェクトのプロパティを開く→Androidのタブ
  • 追加を選んで、Libraryのプロジェクトを選択します。
    ※Libraryのプロジェクトは事前に作成するか、既存のプロジェクトをImportするかしておきます。



  • あとはOKを押すだけで完了。ソースパス毎追加するとパッケージ・エクスプローラはこんな感じになります。

  • genの中にLibraryのR.javaが追加されています。



  • これで、Library側のR.javaも参照できて、そのIDでリソースが参照できるようになります。 ただし、プロジェクト Main側のリソース名とLibrary側のリソース名が区別されない(どちらもR.string.xxxとかで参照することになる)ので、名前の付け方には注意する必要がありそうです。

2011年2月10日木曜日

AndroidでLibraryを使う。

前回の続き…。

今度は作ったLibraryを別のProjectで使う方法。
Libraryの運用方法により、Library自体をProject内部に保存しておきたい場合と、外部のものを参照したい場合の2通りの運用方法が考えられます。

まずは、ProjectにLibraryファイルを含めて運用したい場合。

  1. 普通にAndroid Projectを作成

  2. 既存のプロジェクトを使ってももちろんOK!

  3. プロジェクト内にLibrary(.jarファイル)をコピー。

  4. 場所はどこでも構わないと思うけどlibフォルダを作ってコピーするのがわかりやすいかと。

    ↓こんな感じ


  5. ビルドパスの構成変更

  6. プロジェクトを右クリックしてビルド・パス→ビルド・パスの構成を選ぶ

    ↓↓こんな画面が開きます


    ここで、JARの追加を選ぶとさらに次の画面に


    ここで先程追加したJARファイルを選択してOK。今回はlibフォルダの中に追加してます。
    そうすると↓のようにビルド・パスにライブラリが追加されます。

    このままOKで完了。パッケージ・エクスプローラに参照ライブラリーの項が追加されています。

  7. ライブラリを利用するコード

  8. ライブラリのパッケージをimportすると、ライブラリ内のクラスが使えるようになります。たったこれだけ。
    package com.sample.Application;
    
    import android.app.Activity;
    import android.os.Bundle;
    import com.sample.library.SampleLibrary;
    
    public class SampleApplicationActivity extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            SampleLibrary sampleLibraryClass = new SampleLibrary();
            
            sampleLibraryClass.test(); //SampleLibraryのtest()メソッドを実行
        }
    }
    
外部Libraryを参照する場合
    プロジェクトにLibraryを含めたくない場合(共通Libraryを使う場合など?)は、上記手順の
    2.JARファイルのコピーを行わない。

    3.ビルド・パスの構成の 『プロジェクトのプロパティ・Javaのビルド・パス』の画面で『外部JARの追加』を選ぶ。
    出てきたダイアログで使いたいのJARファイルを選択する以外は一緒です。

2011年2月8日火曜日

AndroidでLibraryを作る

Android用のライブラリを作成する方法

手順をメモ。
  1. プロジェクト作成
    • 新規→Androidプロジェクトを選択
    • 通常通りにプロジェクト名・ビルドターゲットの設定を行う。
    • Create Activityのチェックを外す。


  2. プロジェクトのプロパティ設定
    • プロジェクトを選択→右クリック→プロパティ→Androidタブで Is Libraryをチェックする


  3. AndroidManifestの設定
    • アプリケーションタブでDefine an <application> tag in the AndroidManifest.xmlのチェックを外す。
    • なお、Label/Iconの指定は無視されるので気にしない。


    • これで、できたAndroidManifest.xmlはこんな感じでとってもシンプル。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.sample.library"
          android:versionCode="1"
          android:versionName="1.0">
    </manifest>
    

  4. リソースの整理
    • デフォルトで不必要なリソースができているので削除する。


    • こんな感じでできてるはずなので、drawable-xxx/icon.png(3か所), layout/main.xml, values/string.xmlを削除。
    • リソースを使用しない場合は、resフォルダ内のサブフォルダ drawable-xxx, layout, values等は削除しても構わないが、 assets, res, genのフォルダのみは残しておかないとエラーになる。
    • genフォルダ内にR.javaが自動生成されている。気持ち悪いので一応これも消しておく。
    • リソースを使用する場合は、必要なものを残しておくか、削除後に新しく作成する。

  5. ソースクラスの作成
    • ライブラリとして実装したいクラスをjavaで普通に作成する。

  6. ビルド実行
    • 普通に自動ビルドされるが、一旦クリーンアップして再ビルドするのがなにかと問題が起きにくい。
    • default.propertiesが非同期と言われた場合は、そのファイルを右クリック→リフレッシュしてみる。

  7. ライブラリ(*.jar)作成
    • プロジェクトを右クリック→エクスポート→Java→JARファイル→次へ を選択。
    • エクスポートするものを選択。
      初期設定ではすべてが選択されているが、rootにある次のものは除外すべき。
      (.classpath, .project, AndroidManifest.xml, default.properties, propguard.cfg)
      これらを除外しないと、(これらを除外していない)複数のLibraryを使用するときにDuplicate ..... といって怒られる。
    • また、ライブラリを自プロジェクト内のフォルダに生成した場合、2回目のエクスポート時にライブラリもエクスポート対象になっていると、「JAR ファイルは、それ自体にはエクスポートできません。」と表示される。この時は、あわてずライブラリをエクスポート対象から除外する。


    • その他はデフォルトのままで特に問題なし。Jarファイルの場所を名前を選択し、次へ もしくは 完了。
    • 次へを選んだ場合は適宜設定を進めて完了を選ぶ。

  8. これで、選択した場所にライブラリが作成される。
    • あとは、できたjarファイルを好きなところにコピーして運用するだけ。ライブラリの使い方はまた別途書きます。

2011年2月7日月曜日

Twitter雑感

Twitter参加しています。
@aoi_terenceというIDでやっていますので、覗いて見てください。

ということで、現在フォローしている数1891、フォローされている数1767です。
かなりヘヴィユーザーですが、殆ど地元山形の方との出会い交流の場になっています。

さて、Twitterやる上で覚えなくてはいけないことをちょっと説明します。
以下はさっき呟いた内容です。
  1. 南ジャスコへデート?旦那と。
  2. こんなラベルにしたよ。新しいCD。 http://twitpic.com/3xa1tb
  3. @web_eye 主にかあ、してないから主婦じゃないなあ。
  4. 現在は可読性を重視するので短い名前は付けないように、また動詞と名詞、形容詞の順番を英文に近くとなってますね。開発ツールもそれに対応しているのでパンチは楽ですが。RT @_h4: aoi_terence 変数名の付け方で世代がわかりますね。学生時代の実習はpascalで、研究には
  5. どれくらいで復活するんだろうね?QT@ishikotukotu: わたしは一時下山したけど、、、代わりに東北電力さんがすごい勢いで登っていきましたよ~~。ナイターまで復活したら、ナイター行きます~。
  6. 主婦って何をすれば主婦って呼ばれるのかな?三食ご飯作ってないからなあ。RT @web_eye: 私達ってそもそも主婦なんかな?主婦の定義が気になった朝…w RT aoi_terence: web_eye スーパー主婦とな?一番私がなれなさそうなものだな…
解説
1.は、ただ呟いただけのまず基本の呟き。
2.は、ただの呟きに他サイトにあげた画像リンクを貼ってます。
  画像あげる場所はTwitter対応のところが複数あります。
3.@web_eyeに呟きを投げて居ます。通常@やReplyと言われるものです。相手先の@やReplyに表示されます。
4.非公式RT;参照して返信。相手の呟きを自分の呟きに取り込んで返信しています。
 これをすると私をフォローしている方にも参照内容が見えます。
 あまり推奨されてませんが、これに関しては後ほど。
5.QT:上記の非公式RTと一緒です。
他に公式RT があります。参照して情報を付加せずに呟いてます。と言う感じ。

で、まあ、こんな感じでツイッターをだらだらやっていると、遠くの方から、
RTを非公式で使うな、呟きをだらだら長くするな
と言う声が聞こえるのです。
確かに、同じユーザさんをフォローしてると同じ発言が重複してきて見づらいってのは分かるのですが、如何せん現在の私のツイッターは一人で誰に向かって呟いてるのか分からない状態ではなく、ある程度意味を持った集まりの中で繋がった関係を持って呟いているので、呟きの前後の状況が分かった方が話の流れに乗り易いのです。
これは既にコミュニティを作ってるのと同じ感覚です。
意図して作ったのではなく、同じような地域で暮らしているもの同士がふとした生活の中の情報を共有しているうちに出来ていったもので、ある程度の人数が集まってきたのもこのRT・QTのおかげだと思います。
自分のフォロワーさんがRT・QTして居る方ってどんな人だろう、あ、私が知りたい情報を知ってる人だ。じゃあ、私もフォローしておこう。
こんな連鎖で、どんどん膨らんできている気がします。
現実にお会いした方も、もう10人を超えようとしていて異業種交流が活発になっています。
このように、目的をもってフォローして行くのはとても有意義なのですが、Twitter本来の姿からするとどうでしょうね?
でも、悪い使用方法ではないと思うので、ここで、非公式RTは罪悪ばかりじゃないと言うことを言いたかったのです。

2011年2月6日日曜日

Activity内でのViewの遷移について

さて、アプリケーションの開発についてですが、
大抵のAndroidアプリ開発サイト(本)では、Activity = Windowっぽく解説してあります。

つまり、複数の画面間の遷移が必要なアプリの場合は、
Activity1⇔Activity2⇔Activity3・・・のようにActivityをIntentで遷移するのが一般的だということでしょうか。

しかし、草木塔アプリは1つのActivityでViewを入れ替える方式で作ってみました。
もちろん、Activity切り替え方法を否定するつもりは毛頭なく、こういった実装方法もあるのでは?といった意味で実験的手法です。
実際やってみるとパンくずリスト付きの画面遷移っぽいことをやるのにはこっちの方が向いてるかもしれません。

 この方式で悩ましい点は、
  1. 俗にMVC手法で言うところのControlerの役割をどうする?
    • 本来Activityがこの役割のはず。→でも今回はActivityは1個って決めたから;;
    • View = ViewControlerという位置づけにする。つまりUserView Classを作成して必要に応じOnClickとかをImplementしてUIイベントをハンドルしようということ。
    • こうすると、ViewとControlerが分離されてないじゃないか…と言う声も聞こえてきそうだけど、この場合はR.layoutで指定した部分をViewだと言い切ることにする。現にlayout部分では、Viewの要素の定義やら、レイアウトに関する記述がある。
  2. 戻る処理をなんとかしなければならない。
    • 通常、Activityではバック キーでActivityを閉じてしまいますが、今回は前の画面に戻るようにしなければならない。
    • この処理は必須ではなく、画面上に戻る遷移のボタンを設けることも可能。(けど一般的なApplicationの操作から乖離するからやめた方が良い?)

ということで、sampleコードは次のようになります。
 (OnBackPressed()はAPI Level5 なんで、その辺はゴニョゴニョ・・・。)

MainActivity Class
メインのActivityはViewの制御機能を持たないようにしています。

public class MainActivity extends Activity {
 private Stack<View> childView;


 /*********************************************************
  * Application実行時の最初のイベント。
  * 最初の画面を作成し、WindowListのTopに設定する。
  *
  * @see android.app.Activity#onCreate(android.os.Bundle)
  *********************************************************/
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  requestWindowFeature(Window.FEATURE_NO_TITLE);

  childView = new Stack<View>();
  childView.clear();

  // 最初のViewを表示
  pushContentView(R.layout.user_first_view);
 }

 /*********************************************************
  * 戻るボタンが押されたイベント処理。
  *
  * @see android.app.Activity#onBackPressed()
  *********************************************************/
 @Override
 public void onBackPressed() {
  if(!popContentView()) {
   // 取り除く前のViewがない場合は、
   //本来のBackPressed(=finish())を実行(必須ではない)
   super.onBackPressed();
  }
 }
 /*********************************************************
  * 子Viewを追加する。
  *
  * @param view 追加する子View
  *********************************************************/
 public void pushContentView(View view) {
  childView.push(view);
  super.setContentView(childView.peek());
 }
 /*********************************************************
  * 子Viewを追加する
  *
  * @param resid 追加する子のリソースID
  *********************************************************/
 public void pushContentView(int resid) {
  // 最初の子Viewを作成
  LayoutInflater inflater
   = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
  View view = (View)inflater.inflate(resid, null);

  childView.push(view);
  super.setContentView(childView.peek());
 }
 /*********************************************************
  * 子tViewを取り除く
  *
  * @return true 取り除き成功 / false 取り除けなかった
  *********************************************************/
 public boolean popContentView() {
  if(childView.size() > 1) {
   // ViewStackに前の画面がある場合はPop
   childView.pop();
   //最上位のViewをsetContentView()する。
   setContentView(childView.peek());
   return true;
  }
  return false;
 }
}

最初のViewのClass
ボタンをクリックすると次のViewに遷移するようにしてみました。

public class UserFirstView extends LinearLayout {
 /*********************************************************
  * コンストラクタ(LinearLayout)
  *
  *********************************************************/
 public UserFirstView(final Context context, AttributeSet attrs) {
  super(context, attrs);
 }
 /*********************************************************
  * Inflaterを使ってViewを実体化する場合は
  * onFinishInflate()以降で findViewByIdをする。
  *
  *  @see android.view.View#onFinishInflate()
  *********************************************************/
 @Override protected void onFinishInflate() {
  //Buttonの処理を追加
  Button buttonNext = (Button)findViewById(R.id.buttonNext);

  buttonNext.setOnClickListener(new OnClickListener() {
   /*********************************************************
    * ボタンがクリックされた
    * →次の画面に遷移する
    *
    * @see android.view.View.OnClickListener#onClick(android.view.View)
    *********************************************************/
   @Override public void onClick(View v) {
    MainActivity mainActivity = (MainActivity)getContext();
    // 次のViewを表示
    mainActivity.pushContentView(R.layout.user_next_view);
   }
  });
  super.onFinishInflate();
 }
}

次のViewのClass
ボタンをクリックすると前のViewに戻るようにしてみました。

public class UserNextView extends LinearLayout {
 /*********************************************************
  * コンストラクタ(LinearLayout)
  *
  *********************************************************/
 public UserNextView(final Context context, AttributeSet attrs) {
  super(context, attrs);
 }
 /*********************************************************
  * Inflaterを使ってViewを実体化する場合は
  * onFinishInflate()以降で findViewByIdをする。
  *
  *  @see android.view.View#onFinishInflate()
  *********************************************************/
 @Override protected void onFinishInflate() {
  //Buttonの処理を追加
  Button buttonNext = (Button)findViewById(R.id.buttonPrev);

  buttonNext.setOnClickListener(new OnClickListener() {
   /*********************************************************
    * ボタンがクリックされた
    * →前の画面に遷移することもできる
    *
    * @see android.view.View.OnClickListener#onClick(android.view.View)
    *********************************************************/
   @Override public void onClick(View v) {
    MainActivity mainActivity = (MainActivity)getContext();
    // 前のViewを表示
    mainActivity.popContentView();
   }
  });
  super.onFinishInflate();
 }
}

UserFirstViewのXMLです
UserNextViewは省略しますが似たようなものです。あくまでSampleなんで…

<?xml version="1.0" encoding="utf-8"?>
<com.sample.UserFirstView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
 <TextView
   android:layout_width="wrap_content" android:layout_height="wrap_content"
   android:text="FirstView">
 </TextView>
 <Button
   android:id="@+id/buttonNext"
   android:layout_width="wrap_content" android:layout_height="wrap_content"
   android:text="Next">
 </Button>
</com.sample.UserFirstView>

※ということで、結局この方法のメリットは何かと言うと、各画面間でたくさんパラメータを受け渡ししなければならない時にIntentだとちょっと不便…を解消できるかなと。

なお、ブログにソース貼り付けるにあたり、こちらのページを参考にさせていただきました。