Android

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だとちょっと不便…を解消できるかなと。

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

0 件のコメント:

コメントを投稿