2012年2月23日木曜日

[Android] アクションバーを画面下側から表示させてみました。アプリ編

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

@adakodaさんが面白い事をしていらっしゃったのでインスパイア。
[Android] アクションバーを画面下側から表示させてみました
http://www.adakoda.com/adakoda/2012/02/android-47.html

アプリからでも出来るのでは?


アプリケーションではアプリを初期化する際にsetContentView()するんで、setContentView()で出来たView群が画面上の全てだと思いがちです。しかーし画面上に存在する以上タイトルバーやActionBarなども当然Viewの一種なわけです。ちゅーことはアプリケーションのトップレベルのViewを取れたらタイトルバーやActionBarも操作出来るのでは無いでしょうか!?

そもそもまずトップレベルのViewをアプリケーション側から取得する事が出来るのか。出来ます。ActicvityからWindowを取り出してgetDecorView()するだけ。
View root = activity.getWindow().getDecorView()

ではこのViewどういう構造になっているか。Viewツリーをダンプしてみましょう。ViewツリーのダンプはViewの階層構造をダンプするスニペットを参考に。
View v = getWindow().getDecorView();
Util.dumpViewTree(v, "");

吐かれたのがこれ。Android4.0.2のGalaxy Nexusで、プロジェクトはEclipse+ADTで生成して出来たものを全く触らずに利用。ターゲットは4.0です。

Util  D  com.android.internal.policy.impl.PhoneWindow$DecorView
Util  D   android.widget.LinearLayout
Util  D    com.android.internal.widget.ActionBarContainer
Util  D     com.android.internal.widget.ActionBarView
Util  D      android.widget.LinearLayout
Util  D       android.widget.ImageView
Util  D       android.widget.LinearLayout
Util  D        android.widget.TextView
Util  D        android.widget.TextView
Util  D      com.android.internal.widget.ActionBarView$HomeView
Util  D       android.widget.ImageView
Util  D       android.widget.ImageView
Util  D     com.android.internal.widget.ActionBarContextView
Util  D    android.widget.FrameLayout
Util  D     android.widget.LinearLayout
Util  D      android.widget.TextView
Util  D    com.android.internal.widget.ActionBarContainer

"com.android.internal.widget.ActionBarContainer"てのが居ますね。露骨ですね。なんで二個あるのかはちょっとわかりません。

findViewsWithClass()を拡張


じゃあ"com.android.internal.widget.ActionBarContainer"を取り出して操作したらいいんじゃまいか?どうやって取り出すの。ああ、何かそういうエントリを昨日見たよ。あった。List<T> findViewsWithClass(View v, Class<T>)。助かるわー。#ステマ

しかしどうもList<T> findViewsWithClass(View v, Class<T>)ではClass<T>を使っています。com.android.internal.widget.ActionBarContainerは名前しか判らないのでちょっと拡張する必要がありそうですね。

こうなった
public static <T extends View> List<T> findViewsWithClass(View v, Class<T> clazz) {
  List<T> views = new ArrayList<T>();
  findViewsWithClass(v, clazz.getName(), views);
  return views;
 }
 public static List<View> findViewsWithClassName(View v, String className) {
  List<View> views = new ArrayList<View>();
  findViewsWithClass(v, className, views);
  return views;
 }
 private static <T extends View> void findViewsWithClass(View v, String clazz, List<T> views) {
  if (v.getClass().getName().equals(clazz)) {
   views.add((T) v);
  }
  if (v instanceof ViewGroup) {
   ViewGroup g = (ViewGroup) v;
   for (int i = 0; i < g.getChildCount(); i++) {
    findViewsWithClass(g.getChildAt(i), clazz, views);
   }
  }
 }

これでcom.android.internal.widget.ActionBarContainerを簡単に取り出せます。

View root = activity.getWindow().getDecorView();
List<View> views = Util.findViewsWithClassName(root, "com.android.internal.widget.ActionBarContainer");



試しにsetVisibility(View.GONE)してみる


では試しに取り出したcom.android.internal.widget.ActionBarContainerをView.GONEで消してみましょう。

View root = activity.getWindow().getDecorView();
List<View>l views = Util.findViewsWithClassName(root, "com.android.internal.widget.ActionBarContainer");
for(View v : views){
  v.setVisibility(View.GONE);
}

消えたー!操作出来る。当たり前だけど。


actionBarUpsideDown(Activity activity)


Viewツリーのダンプを見ると、事実上ルートのコンテナはcom.android.internal.policy.impl.PhoneWindow$DecorView直下のLinearLayout。多分Vertical。つまり、この人にいるcom.android.internal.widget.ActionBarContainerをremoveView()で消して、addView()で足すとケツに移動するのではないか!?
という事でメソッドを作成。

public final static void actionBarUpsideDown(Activity activity) {
  View root = activity.getWindow().getDecorView();
  View firstChild = ((ViewGroup) root).getChildAt(0);
  if (firstChild instanceof ViewGroup) {
   ViewGroup viewGroup = (ViewGroup) firstChild;
   List<View> views = Util.findViewsWithClassName(root, "com.android.internal.widget.ActionBarContainer");
   if (!views.isEmpty()) {
    for (View vv : views) {
     viewGroup.removeView(vv);
    }
    for (View vv : views) {
     viewGroup.addView(vv);
    }
   }
  }
  else{
   Log.e(TAG, "first child is not ViewGroup.");
  }
 }

試す


メソッドにしたので試すのは簡単ですね

setContentView(R.layout.main);
Util.actionBarUpsideDown(this); 

キタワァ.*・゜゚・*:.。..。.:*・゜(n‘∀‘)η゚・*:.。. .。.:*・゜゚・*!!!!!☆



まとめ


ICS以外で呼ぶとどうなるか試してないので適宜何とかして下さい。
ActionBarが上に居るのは個人的に良く思っていないので、これ使って下に表示させる様にしていきたい。



0 件のコメント:

コメントを投稿