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

プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

【Android】Rectについて

スマートフォン android

androidで描画処理を書いているときに、Rectクラスの4点の関係が気になったので、調べてみました。

1.矩形の内/外と4点の関係

素朴な疑問として、Rectの要素、top / bottom / left / right はRectの中をあらわしているのか、Rectの外をあらわしているのかが気になりました。

contains(int x, int y) メソッドの説明を読むと、

Returns true if (x,y) is inside the rectangle. The left and top are  considered to be inside, 
while the right and bottom are not. This means that for a x,y to be contained: left <= x < right
and top <= y < bottom. An empty rectangle never contains any point.

となっています。

つまり、top / left はRectの中だけど、 bottom / right はRectの外として扱う、ということになります。

参考までに、Rectのwidth()、height()メソッドをソースコードで確認すると、

    /**
      * @return the rectangle's width. This does not check for a valid rectangle
      * (i.e. left <= right) so the result may be negative.
      */
     public final int width() {
         return right - left;
     }
 
     /**
      * @return the rectangle's height. This does not check for a valid rectangle
      * (i.e. top <= bottom) so the result may be negative.
      */
     public final int height() {
         return bottom - top;
     }

と定義されており、width, height の戻り値が、Rectで表す矩形の辺の画素数に一致していることがわかります。

2.dawRectとの関係

Rectの定義は分かったのですが、CanvasクラスのdrawRectでRectを渡して描画してみると、どうも予想と違う結果になる場合があります。

そこで、drawRect とRectの関係を調べるために、同じ大きさのRectを、その位置を移動させながら、Paintの設定をいくつか変えて描画してみました。

具体的には、Rectを20x20の矩形として、青色でdrawRect(rect, paint) として描画したあと、緑色でdrawPoint()としてRectのtop / bottom / left / right に対応する4点を描画したものです。従って、緑色の点は、Rectの定義より、矩形の左上の点(leftとtopの組み合わせ)以外は、矩形の外側になることを期待しました。

Paintの設定としては、StrokeStyleとStrokeWIdthを変化させました。

 

コードを書いて実験をしてみると、結果は下記のようになりました。

drawRectの実験結果

左から、StrokeStyleを、FILL、STROKE、FILL_AND_STROKEとしています。

上段から、StrokeWIdthを、0、1、2 としています。

 

これから、

  • 一番左上を見ると、FILLの時は、左上の点(leftとtopの組み合わせ)は矩形の中にあり、他の緑色の3点は矩形の外側にあります。しかし、その隣(最上段の中央)を見ると、STROKEの場合は4点を含むように線が描画されています。
  • StrokeWidthに0を指定したとき(最上段右端)は、FILL_AND_STROKEを指定しても中央部が塗られていません。一方、StrokeWidthに1を指定すると(2段目右端)、FILL_AND_STROKEで正しく塗られます。なお、この実験中にはありませんが、StrokeWidthを指定しない場合は、StrokeWidth(0)と同じ結果になりました。

ということがわかります。

(android1.6および2.1のエミュレータで同じ結果になりました)

 

個人的には、Rectの定義から考えると、bottom / right が矩形外、とのことなので、StrokeStyleがSTROKEの場合に、bottom / right の位置が矩形の線上にあることに違和感を感じています。

いずれにせよ、1画素にこだわるような描画をする場合は注意をしたほうがよいかもしれません。

3.サンプルコード

上記の実験で使用したサンプルコードを下記に載せておきます。

package com.mori_soft.anroid;

import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.View;

public class DrawRectSample extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        View v = new View(this) {
            
            private Paint mPaint = new Paint();
            
            @Override
            public void draw(Canvas canvas) {
                super.draw(canvas);

                int [] sw = {0, 1, 2};      // StrokeWidth
                int [] oy = {0, 50, 100};   // 矩形描画時のY方向のオフセット
                
                for (int i = 0; i < 3; i++) {
                    draw_rect(canvas, 0,   oy[i], Paint.Style.FILL,            sw[i]);
                    draw_rect(canvas, 50,  oy[i], Paint.Style.STROKE,          sw[i]);
                    draw_rect(canvas, 100, oy[i], Paint.Style.FILL_AND_STROKE, sw[i]);
                }
            }
            
            private void draw_rect(Canvas canvas, int ox, int oy, Paint.Style style, int sw) {
                final int l = 10;
                final int t = 20;
                final int r = 30;
                final int b = 40;
                
                Rect rect = new Rect(l, t, r, b);
                rect.offset(ox, oy);
                
                Paint paint = mPaint;
                
                paint.setColor(Color.BLUE);
                paint.setStyle(style);
                paint.setStrokeWidth(sw);
                
                canvas.drawRect(rect, paint);
                
                paint.setColor(Color.GREEN);
                canvas.drawPoint(ox+l, oy+t, paint); // 左上
                canvas.drawPoint(ox+r, oy+t, paint); // 右上
                canvas.drawPoint(ox+l, oy+b, paint); // 左下
                canvas.drawPoint(ox+r, oy+b, paint); // 右下
            }
        };
        setContentView(v);
    }
}