Viewを継承した独自のカスタムビュー内のレイアウトをxmlで定義することができる。
レイアウトに関する事はJavaコードで書くよりもxmlで定義しておいたほうがコード量が減るし、後々メンテナンスしやすくなる。
今回はこの方法について見ていく。
まず、サンプルアプリの完成形となる画面を示す。
画面上部に表示された赤色の部分がカスタムビューとなっている。
テキストビューとその下に3つボタンがあって、それぞれのボタンを押すとテキストビューに押したボタンのテキストが表示されるようになっている。
まずは、このカスタムビューに適用するレイアウトxmlを示す。
<?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="match_parent"
android:orientation="vertical"
android:background="#FF8888" >
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="button1"/>
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="button2"/>
<Button
android:id="@+id/button3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="button3"/>
</LinearLayout>
</LinearLayout>
このxmlを見ると分かるとおり、内容は通常ActivityのsetContentView()で渡しているxmlの形式と変わらない。
次に、カスタムビューのJavaコード部分を示す。
package com.example.customviewlayoutlesson;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
// 適用するレイアウトxmlのトップレベルViewがLinearLayoutなので、ここでもLinearLayoutを継承する
public class MyView extends LinearLayout {
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// LayoutInflaterでレイアウトxmlの内容でViewを作る
// LayoutInflater#inflate()の第2引数ではルートとなるViewとして自分自身を指定する
View layout = LayoutInflater.from(context).inflate(R.layout.myview, this);
final TextView textView = (TextView)layout.findViewById(R.id.textView);
final View.OnClickListener onClickListener = new OnClickListener() {
public void onClick(View v) {
// 押されたボタンのテキストをTextViewに表示する
textView.setText( ((Button)v).getText() );
}
};
layout.findViewById(R.id.button1).setOnClickListener(onClickListener);
layout.findViewById(R.id.button2).setOnClickListener(onClickListener);
layout.findViewById(R.id.button3).setOnClickListener(onClickListener);
}
}
本来、カスタムビューはViewを継承すれば良いのだが、今回はLinearLayoutを継承している。
これは、適用するxmlのトップレベルのViewがLinearLayoutだからで、その型を合わせる必要があるからである。
サンプルではLinearLayoutを例として取り上げたが、ほかにもViewGroupの子クラスのFrameLayoutやRelativeLayoutなどが使える。
19行目で、LayoutInflator#inflate()メソッドを使ってレイアウトxmlからViewを作っている。このメソッドの第2引数で、ルートとなるViewとして自分自身を指定する。こうすることでレイアウトxmlの内容がカスタムビューに適用される。
その後は、いつものActivity#onCreate()の中で行っている手順と同様、findViewById()メソッドで操作したいViewをレイアウトから取得できる。
今回のサンプルでは28-30行目でレイアウト内の3つのボタンにイベントリスナをセットしている。
最後にMain側のレイアウトxmlとActivityのソースを示す。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.customviewlayoutlesson.MyView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
package com.example.customviewlayoutlesson;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v4.app.NavUtils;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
主要な処理は全てカスタムビュー側に書いてしまったので、Main側の処理はメインレイアウトxmlでカスタムビューを定義するだけになった。
このようにして、各部分のコンポーネント化を押し進めていくとコードの保守性・可読性が上がっていくだろう。