2010年8月20日金曜日

Android Menu の背景を変える

Menu を作る方法は http://developer.android.com/guide/topics/ui/menus.html
などに書いてありますが、まっとうな方法では、アイコンとタイトルしか
変更できません。

*Menuとはメニューキーを押したときに下から”にゅっ”と出てくる
やつです。



↑これの「使い方」とか「ネイル・ド・ロンジョについて」とかのパネルの部分


システムとの一貫性を保つためにそうなっているんだろうとは
思うのですが、アイコンにすごくカラフルなのとか使ったら
一貫性もなにもないだろぅ。とか思ったりもします。

例えば、ic_menu_**.png はアイコンが灰色ですが、このアイコンの
色を変えた画像を用意して、アイコンに指定すると



となって、メニューパネルの背景色がアプリ全体の色バランスと
合ってない感じになってしまいます。

そこで、メニューパネル部分の背景を変えてみました。






以下やりかたです。

*注! この方法は Reflection で内部クラスを使っているので、SDKのアップデートなどで使えなくなる可能性があります。


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater.Factory;

public class MenuTest extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// ここで設定
ensureInflaterFactory();

setContentView(R.layout.main);
}


void ensureInflaterFactory() {
LayoutInflater service = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
final Factory orgFactory = service.getFactory();

if (orgFactory == null) {
// View をインスタンス化するための Factory オブジェクトを LayoutInflater に設定する。
// Factory の onCreateView で View のインスタンスを return すればそれが使われる。
// null を return すれば LayoutInflater 自身がViewをインスタンス化する。
final Factory factory = new Factory() {
public View onCreateView(String name, Context context, AttributeSet attrs) {

// メニューパネル部分の内部クラスだけ変更
if(name.equals("com.android.internal.view.menu.IconMenuView")) {
try {
ClassLoader cl = getClassLoader();
Class clazz;
clazz = cl.loadClass("com.android.internal.view.menu.IconMenuView");
Constructor constructor = clazz.getConstructor(Context.class, AttributeSet.class);
Object view = constructor.newInstance(context, attrs);
ViewGroup vg = (ViewGroup) view;
// ここで色指定
// vg.setBackgroundColor(R.color.hoge); だとうまくいかなかった
//vg.setBackgroundColor(Color.CYAN); これはうまくいく
vg.setBackgroundColor(Color.parseColor("#66ffccff"));
//vg.setBackgroundResource(R.drawable.hoge); で画像を背景にすることも可能
return vg;
}
catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return null;
}
};
service.setFactory(factory);
}
}



Reflection を使っているので、通常の方法よりも
呼び出すときにオーバーヘッドが大きくなります。

#メニューボタンが押された一番最初しか呼ばれないし、
#実際そこまで気になるわけではないけど

メニューの中の各アイテムは com.android.internal.view.menu.IconMenuItemView
というクラスで TextView を extends してるので、同じようにして
文字色を変えられないかな、と思ったのですが変わりませんでした。残念。


あ、あと背景に指定する色を透過にしても、透過にはならないで白のパネルになりました。
白のパネルの上に載せる色を指定する、という感じなんですね。

0 件のコメント:

コメントを投稿