TextViewやLinearLayoutを継承してクラスを作りたい時がある。作ったら当然それをlayoutのxmlで参照したい。xmlから独自の属性(主にsetterメソッド)にアクセスできるようにしたい。そのためのTips。

よくありがちなのはLinearLayoutの中に複数のViewを入れて1つのクラスと扱うケース。今回はそれを例にやってみる。

カスタムビュー用のlayout xmlを定義

res/layout/my_view.xmlを作る。

今回はLinearLayoutの中にTextViewがあるカスタムビューを作る。もちろんもっと色々なViewを入れて複雑にも出来る。

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:orientation="vertical" 
> 
  <TextView 
    android:id="@+id/textView" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
  /> 
</LinearLayout>

res/values/attrs.xmlを作る

attrs.xmlではlayoutのxmlで使用できる属性を定義する。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView">
        <attr name="titleText" format="string"/>
    </declare-styleable>
</resources>

これをビルドするとR.stylable.MyViewができる

クラスの継承

// my_view.xmlで一番トップの要素がLinearLayoutであるのに合わせてLinearLayoutを継承する
public class MyView extends LinearLayout {
  public MyView(Context context) {
    super(context);
    init(null);
  }

  public MyView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(attrs);
  }

  public void init(AttributeSet attrs) {
    Context context = getContext();
    LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        
    // my_view.xmlのレイアウト構成を適用する
    inflater.inflate(R.layout.my_view, this);

    if (attrs == null) return;

    // R.stylable.MyViewを指定
    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.stylable.MyView);
    // layoutのxmlで指定された属性値を読む
    String titleText = typedArray.getString(R.stylable.MyView_titleText);
    // TypedArrayはrecycleを読んで破棄しないといけないっぽい
    typedArray.recycle();

    // my_view.xmlで定義されているtextViewを得る
    TextView textView = (TextView) findViewById(R.id.textView);
    // xmlから取得した属性値を設定する
    textView.setText(titleText);
}

これでカスタムビューの作成とlayout xmlから呼び出す準備は整いました

作成したカスタムビューをxmlで使用する

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:{任意の名前。ここではappにする}="http://schemas.android.com/apk/res/{アプリケーションのパッケージ名}"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >
    
    <{カスタムビューが属するパッケージ名}.MyView
      app:titleText="Main Menu" <-- app:titleText appは上で定義したnamespace。titleTextはattrs.xmlで定義した属性
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
    />
    
    <ListView
      android:id="@+id/listView"
      android:layout_width="match_parent"
      android:layout_height="match_parent">        
    </ListView>

</LinearLayout>