この文章はもともと韓国のアンドロイド・コミュニティで見つけたもので、原文は下のリンクをクリックすると確認できます。
http://www.androidpub.com/899711
「原文翻訳」
1.TabActivityの構成
まず、TabActivityが動作する簡単な原理をまとめてみると
1)TabActivityが生成されているTabHostを取る。
2)TabHostを通じてTabSpecを生成しながらIndicatorとIntentを指定する。
Intent intent = new Intent().setClass(this, com.mk.counsel.group.ViewCounselGroup.class);
spec = tabHost.newTabSpec(“tab01”).setIndicator(new TabView( this, R.drawable.tab1_selector, “tab01”)).setContent(intent);
3)TabHostにTabSpecを登録する。
上の過程でTabSpecに適用されるIntentが一つのAcitivityのみを処理する形と色んなActivityが一つのTab内で管理される場合(絵を参考)で構成されられますが、後の場合は問題が発生する恐れがあります。

単 純にActivityを変更する為にstartActivity()関数を使うとTabActivityが新しいActivityに変更されてしまって既 存のTab画面を維持できない問題が出るので、この問題を解決するために登場したのがActivityGroupです。
2.ActivityGroup
ActivityGroup は飽くまでActivity実行に対する管理と画面処理(View)を分けて管理する為のものだと思ったら問題ないと思います。Activity実行に対 する管理は全的にLocalActivityManagerに委任して、各種イベント処理に関してはActivityに任せて生成された最終画面 (View)のみを本人が持つように構成されています。(絵を参考)
問題は典型的なActivityGroupは全てのActivityの管理権 限をLocalActivityManagerに委任した状態で、Viewのみを貰うので、自体的にナビゲーション処理が出来ないということです。ここで ナビゲーションを管理する対象が決まります。それはActivityGroup が唯一に所有できるViewです。このViewらはActivityGroupが管理することでナビゲーションが可能になるということです。
3.実際適用
次のコードをご覧ください。
public class NavigationGroupActivity extends ActivityGroup {
ArrayList<View> history; // Viewを管理する為のList
NavigationGroupActivity group; // Activityが接近する為のGroup
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
history = new ArrayList<View>();
group = this;
}
public void changeView(View v) { // 同じLevelの Activityを他のActivityに変更する場合
history.remove(history.size()-1);
history.add(v);
setContentView(v);
}
public void replaceView(View v) { // 新しいLevelのActivityを追加する場合
Log.d("MK","REPLACE VIEW...");
history.add(v);
setContentView(v);
}
public void back() { // Back Keyが押された場合の処理
if(history.size() > 1) {
history.remove(history.size()-1);
setContentView(history.get(history.size()-1));
}
else {
finish(); // 最上位Levelの場合TabActvityを終了しなければならない。
}
}
@Override
public void onBackPressed() { // Back Keyに対するEvent Handler
group.back();
return;
}
}
上で言ったとおりActivityGroupが管理できる唯一なリソースがViewなのでViewを保存するList を一つ生成します。既存には Activityが変更されるたびにLocalActivityManagerが渡すViewをActivityGroupが貰って画面処理だけしたのを 少し能動的にViewを直接管理することに変わりました。つまりBack Keyに対するイベントが発生すると、これを処理するハンドラーがActivityGroupにView Listで画面転換処理を直接求めることです。
では、Back Key処理は分かるが、新しいActivityを追加する場合はどこで誰が呼び出してくれるかが知りたくなると思います。次のコードをご覧ください。
public class NavigationActivity extends Activity {
public void goNextHistory(String id,Intent intent) { //次の画面に移す処理
NavigationGroupActivity parent = ((NavigationGroupActivity)getParent());
View view = parent.group.getLocalActivityManager()
.startActivity(id,intent)
.getDecorView();
parent.group.replaceView(view);
}
@Override
public void onBackPressed() { //前の画面に戻る処理
NavigationGroupActivity parent = ((NavigationGroupActivity)getParent());
parent.back();
}
}
上のコードを見ると該当Activityを所有しているActivityGroupをgetParent()関数で求めた後に実際のActivityの実行をLocalActivityManagerに任せています。
LocalActivityManager で実行された(startActivity()関数を呼び出し)ActivityのViewをgetDecorderView()関数を通じて得た後、こ のViewをActivityGroupのViewに適用する姿が見られます。逆にBack Keyが発生するとParentのback関数を呼び出してActivityGroup内のViewを調節してナビゲーション処理をするように求めます。
では、今からはこの二つのクラスを相続してもらった実際のActivityGroupとActivityがどういうふうに適用されるかを見てみます。
下のコードをご覧ください。
public class ViewCounselGroup extends NavigationGroupActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Intent intent = new Intent(this,ViewCounselMainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |Intent.FLAG_ACTIVITY_SINGLE_TOP);
View view = getLocalActivityManager().startActivity("CounselMainActivity",intent)
.getDecorView();
replaceView(view);
}
@Override
public void onBackPressed() { // Back Keyに対するリクエスト
super.onBackPressed();
}
}
ViewCounselGroup というActivityGroupではLocalActivityManagerを通じて最初に実行するActivityの ViewCounselMainActivityというクラスをIntentで実行します。この時Intentに適当なFlagを設定しなければ LocalActivityManagerが管理するStackにActivityがViewの保存構造と違う形にたまられますのでナビゲーションが同期 化できないです。従って必ず上のIntent Flagを設定してからActivityとViewの順序が同期化されます。なおBack Keyに関する処理は全てのActivityで処理されることではなく、ActivityGroupで実行した最初のActivityでばかりイベントを 貰えるということを必ず覚えてください。
Intentに関するFlagの説明は次のアドレスを参考してください。
http://chihun80.springnote.com/pages/6423199
Intent が設定されるとLocalActivityManagerを通じてActivityを実行するし、この時に生成されたViewを ActivityGroupに適用してくれなければならないですが、ただ適用したら出来ないのでreplace()という関数を通じてViewを管理する Listに登録した後から画面に適用するべきです。
public class ViewCounselMainActivity extends NavigationActivity
implements OnItemClickListener{
……省略……
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.view_counsel_list);
adapter = new CounselAdapter(this,items);
ListView view = (ListView)findViewById(R.id.counsel_list);
view.setAdapter(adapter);
view.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
Intent intent = new Intent(ViewCounselMainActivity.this,
com.mk.counsel.ViewCounselDetailActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("id", items.get(position).id);
goNextHistory("ViewCounselMainActivity",intent);
}
}
動作に対するイベント処理などは実行されたActivity内で管理がされると言ったとおり GroupActivityで最初に実行された ViewCounselMainActivity内で特定のアイテムが選択された場合にItemClickハンドラーを処理してから次のLevelの Activityに移動しようとしたらgoNextHistory()を通じて移したいIntentを渡せばGroupActivity内部で LocalActivityManagerを通じてIntentを実行して、ViewをGroupActivityのViewListに追加して画面を転 換する作業をしてくれます。
4.最後に
TapSpecにIntentを設定する時、GroupActivityをまず登録してからGroupActivity内で最初に呼び出すActivityをLocalActivityManagerを通じて実行してViewを得る過程がポイントです。
こ れでTabActivityとActivityGroupの間の関係整理とActivityGroupでLocalActivityManagerを通じ てActivityとViewを管理するメカニズムを見てみました。まず、絵を見て全体的な流れを理解した後から該当のソースを見たらもっと分かりやすい と思われます。
…...<省略>.....
上の処理はActivityのライフサイクルに従っていることではなく、 画面の状態をキャッシュしてウェブブラウザみたいにViewを変える処理です。もし Activityのライフサイクルを従うのが欲しい方はhistory内にViewの代わりにActivityを実行するIntentを保存した後、移動 可否によって該当のIntentをLocalActivityMangerを通じて実行すれば良いです。
このようにしたら次のようなライフサイクルになります。
// A Activity実行
11-11 09:34:20.449: DEBUG/USilver(21719): (A):Lifecycle is OnStart
11-11 09:34:20.454: DEBUG/USilver(21719): (A):Lifecycle is OnRestart
// B Activity実行
11-11 09:34:29.174: DEBUG/USilver(21719): (A):Lifecycle is OnPause
11-11 09:34:29.184: DEBUG/USilver(21719): (B):Lifecycle is OnStart
11-11 09:34:29.184: DEBUG/USilver(21719): (B):Lifecycle is OnResume
// B -> A ActivityでBack処理
11-11 09:34:49.419: DEBUG/USilver(21719): (B):Lifecycle is OnPause
11-11 09:34:49.424: DEBUG/USilver(21719): (A):Lifecycle is OnNewIntent
11-11 09:34:49.424: DEBUG/USilver(21719): (A):Lifecycle is OnRestart
// A->B Activity実行
11-11 09:40:57.024: DEBUG/USilver(21719): A:Lifecycle is OnPause
11-11 09:40:57.024: DEBUG/USilver(21719): B:Lifecycle is OnNewIntent
11-11 09:40:57.024: DEBUG/USilver(21719): B:Lifecycle is OnResume
이 글은 스프링노트에서 작성되었습니다.