外部からViewの生成を知る

public GuidanceView(Context context, AttributeSet attrs) {
    super(context, attrs);
    //attr.xml経由で拡張されたプロパティを取得
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GuidanceView);
    
    this.guidance = a.getString(R.styleable.GuidanceView_guidance);}

拡張されたViewのために、拡張された属性を読み込むには、以前に紹介したコンストラクタで実装するのがコンベンショナルな方法だが、Viewを拡張したくない、又はViewの外部から属性の読みこみをコントロールしたい場合もあるだろう。(私がそうだ) その場合は外部でViewの生成を知る必要がある。

Viewを組み立てるのはLayoutInflaterクラスの仕事だが、LayoutInflaterの実装は機器依存の部分があるため、直接インスタンスを作らず、現在のコンテキスト(ActivityはWindow)か、システムサービスを経由して取得する必要がある。

//1. システムサービスを取得する
LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

//2. アクティビティから取得する
LayoutInflater LayoutInflater = this.getLayoutInflater();

//3. LayoutInflaterの静的メソッドを使う
LayoutInflater inflator = LayoutInflater.from(this); // 1.と同じ

Activity上でViewが生成された時に割り込みをかけたい場合は、以下のようにonCreateメソッドをオーバライドして、LayoutInflator.FactoryインタフェースのonCreateViewメソッドを実装したFactoryの無名クラスでイベントを補足すればよい。

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

    final LayoutInflater inflator = this.getLayoutInflater();
    inflator.setFactory(new Factory(){

        @Override
        public View onCreateView(String name, Context context,
                AttributeSet attrs) {
            Log.d("TestActivity", "Name = " + name);
            for ( int i = 0; i < attrs.getAttributeCount(); i++) {
                Log.d("TestActivity", " attr name = " + attrs.getAttributeName(i) + "" +
                        ", value = " + attrs.getAttributeValue(i));
            }
            return null;  //nullを返すことで、他のインフレータに生成を任せている
    }});
    this.setContentView(R.layout.main);

ちなみに、イベントを補足する処理とActivity#setContentViewメソッドとの順序は重要だ。

実際のViewの生成(インフレート)はsetContentViewメソッド中で実行されるため、イベントハンドラを有効にするためには、setContentViewメソッドよりも前でLayoutInflator.Factoryのイベントハンドラをアタッチする必要がある。

また、LayoutInflator.Factoryは内部でインスタンスを管理されており、setFactoryメソッドで新たなLayoutInflator.Factoryをセットしても、今まで使用していたインスタンスへの参照は無くなるわけではなく、インスタンスチェーンとして取り扱われる。

    • 実行時のログ (DEBUG)
12-21 14:45:10.225: DEBUG/TestActivity(197): Name = LinearLayout
12-21 14:45:10.245: DEBUG/TestActivity(197):  attr name = orientation, value = 1
12-21 14:45:10.245: DEBUG/TestActivity(197):  attr name = fitsSystemWindows, value = true
12-21 14:45:10.265: DEBUG/TestActivity(197): Name = FrameLayout
12-21 14:45:10.265: DEBUG/TestActivity(197):  attr name = layout_width, value = -1
12-21 14:45:10.275: DEBUG/TestActivity(197):  attr name = layout_height, value = ?16842842
12-21 14:45:10.275: DEBUG/TestActivity(197):  attr name = style, value = ?android:attr/windowTitleBackgroundStyle
:
:
12-21 14:45:10.495: DEBUG/TestActivity(197): Name = jp.hoge.GuidanceView
12-21 14:45:10.495: DEBUG/TestActivity(197):  attr name = id, value = @2131165187
12-21 14:45:10.505: DEBUG/TestActivity(197):  attr name = layout_width, value = -1
12-21 14:45:10.505: DEBUG/TestActivity(197):  attr name = layout_height, value = -2
12-21 14:45:10.505: DEBUG/TestActivity(197):  attr name = layout_marginLeft, value = 2.0dip
12-21 14:45:10.505: DEBUG/TestActivity(197):  attr name = text, value = http://
12-21 14:45:10.515: DEBUG/TestActivity(197):  attr name = layout_weight, value = 1.0
12-21 14:45:10.515: DEBUG/TestActivity(197):  attr name = guidance, value = URLã‚’å…\力してください※

ログでは日本語が化けているが、これはADT側の問題。

これでLayoutリソース(xml)の全てにアクセスできるようになった。