読者です 読者をやめる 読者になる 読者になる

ナカザンドットネット

Android Developer's memo

AndroidのレイアウトXML内に自作Viewを貼る方法

Android Programming tips

SampleView extends Viewを、res/layout/main.xmlの中に組み込んでみました。
ポイントは3つ。
一つ目は、Viewクラスのコンストラクタの引数を(Context context, AttributeSet attrs) にすること。
二つ目は、onMeasure(int widthMeasureSpec, int heightMeasureSpec)を定義すること。
最後に、Viewの名前を付けたXMLタグを専用のレイアウトに放り込むこと。(これは自信ない

SampleView.java

package org.ateliernkzn.InnerLayout;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class SampleView extends View {

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

	protected void onDraw(Canvas canvas){
		super.onDraw(canvas);
		
		canvas.drawColor(Color.BLUE);
		
		Paint mPaint = new Paint();
		mPaint.setStyle(Paint.Style.FILL);
		mPaint.setARGB(255, 255, 255, 100);
		
		canvas.drawRect(20, 50, 40, 70, mPaint);
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
		setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
	}
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
	<TextView  
	    android:layout_width="fill_parent" 
	    android:layout_height="wrap_content" 
	    android:text="こんなかんじで"
	    android:layout_weight="1"
	    android:gravity="bottom"/>
	<LinearLayout
		android:id="@+id/sampleLinear"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:layout_weight="1">
		<org.ateliernkzn.InnerLayout.SampleView
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"/>
	</LinearLayout>
	<TextView
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="どうじゃろか"
		android:gravity="right"
		android:layout_weight="1"/>
</LinearLayout>

で、こうなる。

問題点

入れ子になってるSampleViewは、実はウィンドウサイズと同じだけの表示域を持っています。
SampleViewに大して馬鹿正直にgetWidthとかgetHeightすると、VGA端末なら640*480がそっくりそのまま取得できるということです。
上のサンプル画像で言うと、黄色い四角の下はTextViewに隠されてるだけで存在はしているってことです。
なので、普通にやってると「ビュー部分の底辺のY座標を取ってきてそこから上へhogeピクセルのところに……」という作業ができません。

解決策として、Activity#onWindowFocusChangedのタイミングで「View用レイアウトの」サイズを取得することができます。
こんな感じ。

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
      super.onWindowFocusChanged(hasFocus);
      int width = findViewById(R.id.sampleLinear).getWidth();
      int height = findViewById(R.id.sampleLinear).getHeight();
    }

2015.8.27追記

qiita.com

OnGlobalLayoutListenerっていうのがあるらしいですよ。

はい(震え声)