カスタムImageViewを作成する
k.tsuka
今回は、ImageViewを拡張した独自ImageViewを作成したいと思います。
作成する独自ImageViewの仕様は以下の通りです。。

○機能要件
  1. ピンチ操作による画像の拡大縮小
  2. 画像が見切れている場合は、ドラッグ操作により画像を移動可能。ただし画像の端を移動の限界とする。
○非機能要件
  1. 表示する画像が画面の幅(または高さ)より小さい場合はセンタリングする
  2. 画面向きは縦方向のみ

それでは独自ImageViewのポイントを説明していきたいと思います。


○画像の操作(Matrixオブジェクト)

今回、移動や拡大・縮小にはMatrix操作を利用しています。具体的には以下のメソッドを使用します。
// 移動
matrix.postTranslate(dx, dy);

// 拡大・縮小 (第3、第4引数は拡大・縮小の基点)
matrix.postScale(scaleX, scaleY, x, y);
Matrixを操作した後、MatrixオブジェクトをViewに設定することで描画処理をおこないます。
// 描画
setImageMatrix(matrix);
注意するポイントとしては、Matrixを使用して画像を拡大、縮小する場合は、Viewの属性の1つであるscaleTypeを「matrix」に設定しなければならないということです。
レイアウトファイルか、ソースコード上のどちらかで必ず設定するようにしましょう(今回は、レイアウトファイルで行っています)。


○イベントハンドリング(ドラッグ)

ドラッグ操作で画像を移動させることにより、画面から見切れている部分を表示可能にしなければなりません。ドラッグイベントは、OnTouchListenerを使用してイベント制御を行います。

                          表. イベントハンドリング(ドラッグ)
No イベント種別 内容
1 MotionEvent.ACTION_DOWN 画面がシングルタッチ(指1本でタッチ)されたタイミングで発生します。
タッチ座標を保存する処理を行います。
2 MotionEvent.ACTION_MOVE 画面にタッチされた指がそのまま移動(ドラッグ)されたタイミングで発生します。
移動量を計算し画像の移動処理を行います。
3 MotionEvent.ACTION_UP 画面にタッチされた指が画面から離されたタイミングで発生します。
画像操作後のMatrix値を保存する処理を行います。


○イベントハンドリング(ピンチ操作)

ピンチ操作もドラッグと同様にOnTouchListenerを使用してActionコードでユーザー操作を判別しイベントを制御します。
ピンチ操作の検出にはActionコードによる判別以外にもいくつか方法があります。
  1. SimpleOnScaleGestureListenerインタフェースを実装する
  2. ScaleGestureDetector#OnScaleGestureListenerインタフェースを実装する
これらの方法を使用する場合は、ピンチ操作が始まった際にコールされるonScaleBegin()、ピンチ操作中にコールされるonScale()、ピンチ操作が終了された際にコールされるonScaleEnd()メソッドを実装しなければなりませんので、それぞれ用途に応じた使い分けをして下さい。

ピンチ操作を判別する為のActionコードを下表にまとめました。


                          表. イベントハンドリング(ピンチ操作)
No イベント種別 内容
1 MotionEvent.ACTION_POINTER_DOWN 画面がマルチタッチ(指2本でタッチ)されたタイミングで発生します。
OnScaleGestureListenerを実装している場合、onScaleBegin()がこれに該当します。
拡大・縮小の基点となる座標を算出し保存する処理を行います。
2 MotionEvent.ACTION_MOVE 画面にマルチタッチされた指がそのまま移動(ピンチ操作)されたタイミングで発生します(※ドラッグ時と同じアクションコードですのでご注意ください)。
OnScaleGestureListenerを実装している場合、onScale()がこれに該当します。
マルチタッチ位置の移動量を計算し、拡大・縮小率を特定し、画像のズーム処理を行います。
3 MotionEvent.ACTION_POINTER_UP 画面にマルチタッチされた指が画面から離されたタイミングで発生します。
OnScaleGestureListenerを実装している場合、onScaleEnd()がごれに該当します。
画像操作後のMatrix値を保存する処理を行います。


位置補正

画像の拡大・縮小ではタッチ位置を基点にズーム処理を行いますので、単純にズームすると意図せず画像が見切れてしまったり、意図しないスペースができてしまうことがあります。したがって、美しくズーム処理を行うためには、拡大・縮小後に画像位置を補正してあげることが必要となります。

画像の描画処理には、前述した通り、setImageMatrix()メソッドを使用します。
注意して頂きたいのは、setImageMatrix()メソッドをcallしてもこのメソッド内で描画処理が行われる訳ではないということです。あくまでもこのメソッドが基点になり描画処理が起動されるだけで、実際に描画が行われるのはonDraw()メソッドです。
そして、setImageMatrix()のコールとonDraw()の呼び出しは必ず1対になっている訳ではないということを忘れないでください。

例えば、ドラッグイベントやピンチズームイベントは、onDraw()がコールされる前に何度もコールされますが、onDraw()はAndroid的に描画ができる状態になったタイミングで呼び出されます。その際、描画にはその時に設定されているMatrix値が使用され、移動や拡大・縮小は、その時にCanvas上に描かれている画に対して適用されます。




画像の拡大や縮小は画面上の基点の位置がずれないように自動的に画像の位置が調節されます。それに対してセンタリング等の画像位置補正を行うので、よほどうまく補正しないと描画時にチラツキが発生したり、画像が意図しない座標へ移動してしまいます。
ピンチ操作がゆっくりであればできなくはありませんが、急激なピンチ操作が発生するとさすがに制御しきれませんでしたので、onDraw()で描画した直後に位置を補正する処理を行うようにしています。


それではサンプルコードを紹介していきます。

レイアウトファイル

基本的にレイアウトファイルはViewを設定しているだけです。
scaleTypeにmatrixを設定することを忘れないようにしましょう。
<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.customview.image.CustomImageView
        android:id="@+id/id_my_image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="matrix"     <------- Matrixで画像を操作する設定
        />
</RelativeLayout>


○Activity
Activityの処理は、タイトルバー消しとフルスクリーン処理、カスタムViewで表示する画像のパス設定等を行っています。別にフルスクリーンでなくても良いのですが、計算が楽なのでフルスクリーンにしました。
public class MainActivity extends Activity {

	// デバッグ用ファイルパス
	private static final String DEBUG_IMAGE_PATH = "/mnt/sdcard/test.jpg";

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		// タイトルバー消し、フルスクリーン表示
		this.requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		
		setContentView(R.layout.activity_main);
		
		CustomImageView cv = (CustomImageView)findViewById(R.id.id_my_image_view);
		cv.setImage(DEBUG_IMAGE_PATH);
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}
}

○独自ImageView

public class CustomImageView extends ImageView implements OnTouchListener {

    //===============================================================
    // 定数定義
    //===============================================================
    
    /** 動作モード:未設定 */
    private static final int MODE_NONE = 0;
    
    /** 動作モード:移動 */
    private static final int MODE_DRAG = 1;
    
    /** 動作モード:ズーム */
    private static final int MODE_ZOOM = 2;
    
    /** Matrixオブジェクトが保有しているデータの数 */
    private static final int MATRIX_VALUES_NUM = 9;
    
    /** 5倍超えるとそろそろ見ずらくなるので、デフォルト値は5とする */
    private static final float DEFAULT_MAX_SCALE = 5.0f;
    
    /** 等倍スケール */
    private static final float DEFAULT_SCALE = 1.0f;
    
    //===============================================================
    // メンバー変数
    //===============================================================
    // 表示画像のファイルパス
    private String mFilePath = null;
    
    // 表示画像のビットマップオブジェクト
    private Bitmap mBitmap = null;
    
    // ドラッグ操作時の基準座標
    private PointF mMovePoint = new PointF();
    
    // ピンチ操作時のMatrixオブジェクト
    private Matrix mImageMatrix = new Matrix();
    
    // ピンチ操作時のMatrixオブジェクト(一時保存用)
    private Matrix mSavedImageMatrix = new Matrix();
    
    // ピンチ操作時 マルチタッチ位置の距離
    private float mSpan = 0.0f;
    
    // 画像の最小拡大率
    private float mInitialScale = 1.0f;
    
    // 画像の最大拡大率
    private float mMaxScale = DEFAULT_MAX_SCALE;
    
    // ピンチ操作時の基点座標
    private PointF mMidPoint = new PointF();
    
    // 動作モード
    private int mMode = MODE_NONE;
    
    //===============================================================
    // Constructor
    //===============================================================
    public CustomImageView(Context context) {
        super(context);
        init(context);
    }
    public CustomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    public CustomImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }
    
    /** 初期化メソッド */
    private void init(Context context) {
        // Touchリスナー登録
        super.setOnTouchListener(this);
    }
    
    //===============================================================
    
    
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        boolean ret = true;
        
        int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
        
        // ドラッグイベントチェック
        ret = onTouchDragEvent(event, actionCode);
        if(!ret) {
            // マルチタッチイベントチェック
            ret = onTouchPointerEvent(event, actionCode);
        }
        return ret;
    }
    
    
    
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        
        if(super.getWidth() == 0) {
            // Viewの幅が0の場合(まだViewが形成されていない)
            return;
        }
        
        if(mBitmap != null) {
            // ビットマップを作成しなおす。
            // 現在のビットマップが使用しているメモリを解放
            mBitmap.recycle();
        }
        // View生成されたので、初期描画
        drawInitial();
    }
    
    
    
    @Override
    protected void onDraw(Canvas canvas) {
        
        Matrix matrix = super.getImageMatrix();
        float[] values = new float[MATRIX_VALUES_NUM];
        
        // ズームの場合は、まず画像をズームさせる
        if(mMode == MODE_ZOOM) {
            
            // 描画(拡縮画像)
            super.onDraw(canvas);
            // 描画後のImageMatrixを取得(表示位置補正に使用する為)
            matrix = super.getImageMatrix();
            
            // キャンバスをクリア(背景色で上書き)
            // Zoomの場合は、Zoom画像と位置補正の2回描画するので、
            // 1回目の描画を背景色で塗りつぶす
            // (そうしないと画面に残りっぱなしになる)。
            canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_OVER);
        }
        matrix.getValues(values);
        
        // 画像表示位置を補正する
        setCenteringY(mBitmap, values[Matrix.MSCALE_X], matrix);    // 縦方向センタリング
        chkXPosition(mBitmap, values[Matrix.MSCALE_X], matrix);     // 横方向余白チェック
        chkYPosition(mBitmap, values[Matrix.MSCALE_Y], matrix);     // 縦方向余白チェック
        
        // 補正値が設定されたMatrixオブジェクトをViewに設定
        super.setImageMatrix(matrix);
        // 描画開始
        super.onDraw(canvas);
    }
    
    
    
    /**
     * 画像ファイルパス設定
     * @param filePath
     */
    public void setImage(String filePath) {
        
        if(filePath == null) {
            return;
        }
        mFilePath = filePath;
        
        if(super.getWidth() == 0) {
            return;
        }
        // 設定されたイメージを描画
        drawInitial();
    }
    
    
    
    /**
     * 最大拡大率を設定する
     * @param scale
     */
    public void setMaxScale(float scale) {
        
        if(mInitialScale > scale) {
            return;
        }
        mMaxScale = scale;
    }
    
    
    
    /**
     * 動作モード設定
     * @param mode
     */
    private void setMode(int mode) {
        mMode = mode;
    }
    
    
    
    /**
     * 初期描画
     */
    private void drawInitial() {
        
        if(mBitmap != null) {
            // ビットマップを作成しなおす。
            // 現在のビットマップが使用しているメモリを解放
            mBitmap.recycle();
        }
        
        // ビットマップ作成
        mBitmap = getBitmap(mFilePath);
        
        // 描画
        super.setImageBitmap(mBitmap);
        
        // 描画位置センタリング
        float[] values = new float[MATRIX_VALUES_NUM];
        mImageMatrix.getValues(values);
        
        mInitialScale = getInitialScale(mBitmap);
        
        setCenteringY(mBitmap, mInitialScale, mImageMatrix);
        setValueToImageMatrix(Matrix.MTRANS_X, 0f, mImageMatrix);
        
        if(values[Matrix.MSCALE_X] == DEFAULT_SCALE) {
            // onLayoutは何度も呼ばれるので、
            // 拡縮率が1倍の時(初期ロード時)のみ初期Zoomを実行する
            mImageMatrix.postScale(mInitialScale, mInitialScale);
            mSavedImageMatrix.set(mImageMatrix);
        }
        // 描画処理起動
        super.setImageMatrix(mImageMatrix);
    }
    
    
    
    /**
     * 初期拡縮率を取得する
     * @param bitmap
     * @return
     */
    private float getInitialScale(Bitmap bitmap) {
        
        // Viewのサイズ
        float viewWidth = super.getWidth();
        float viewHeight = super.getHeight();
        
        // bitmapのサイズ
        float imageWidth = bitmap.getWidth();
        float imageHeight = bitmap.getHeight();
        
        // X軸、Y軸のサイズ比
        float scaleX = viewWidth / imageWidth;
        float scaleY = viewHeight / imageHeight;
        
        // 初期状態で画像の見切れをなくしたいので、
        // 小さいほうに合わせる
        return Math.min(scaleX, scaleY);
    }
    
    
    
    /**
     * ドラッグイベント処理
     * @param event
     * @param actionCode
     * @return
     */
    private boolean onTouchDragEvent(MotionEvent event, int actionCode) {
        boolean ret = false;
        
        switch(actionCode) {
        case MotionEvent.ACTION_DOWN:       // シングルタッチスタート
            actionDown(event);
            setMode(MODE_DRAG);             // ドラッグイベントモードへ
            ret = true;
            break;
        case MotionEvent.ACTION_MOVE:       // ドラッグ
            if(mMode == MODE_DRAG) {        // ドラッグイベントモードの場合の処理を実行
                actionMove(event);
                mMovePoint.set(event.getX(), event.getY());
                ret = true;
            }
            break;
        case MotionEvent.ACTION_UP:         // タッチ終了
            setMode(MODE_NONE);             // ドラッグイベントモード終了
            mSavedImageMatrix.set(super.getImageMatrix());
            ret = true;
            break;
        default:
            break;
        }
        return ret;
    }
    
    
    
    /**
     * マルチタッチイベント(ピンチ操作)処理
     * @param event
     * @param actionCode
     * @return
     */
    private boolean onTouchPointerEvent(MotionEvent event, int actionCode) {
        boolean ret = false;
        
        switch(actionCode) {
        case MotionEvent.ACTION_POINTER_DOWN:   // マルチタッチスタート
            actionPointerDown(event);
            setMode(MODE_ZOOM);                 // ズームイベントモードへ
            ret = true;
            break;
        case MotionEvent.ACTION_MOVE:           // ピンチ操作
            if(mMode == MODE_ZOOM) {
                ret = actionPointerMove(event);
            }
            break;
        case MotionEvent.ACTION_POINTER_UP:     // マルチタッチ終了
            setMode(MODE_NONE);                 // ズームイベントモード終了
            mSavedImageMatrix.set(super.getImageMatrix());
            ret = true;
            break;
        default:
            break;
        }
        return ret;
    }
    
    
    
    /**
     * Matrixオブジェクトに値を設定する
     * @param index
     * @param value
     * @param dst
     */
    private void setValueToImageMatrix(int index, float value, Matrix dst) {
        
        float[] values = new float[MATRIX_VALUES_NUM];
        dst.getValues(values);
        
        values[index] = value;
        dst.setValues(values);
    }
    
    
    
    /**
     * 縦方向センタリング
     * @param bitmap
     * @param scale
     * @param matrix
     */
    private void setCenteringY(Bitmap bitmap, float scale, Matrix matrix) {
        
        float viewHeight = (float)super.getHeight();        // Viewのサイズ
        
        float imageHeight = (float)bitmap.getHeight();
        imageHeight *= scale;                               // 画像サイズ
        
        float[] values = new float[MATRIX_VALUES_NUM];
        matrix.getValues(values);
        
        // 画像サイズがViewのサイズより小さい場合のみセンタリング
        float cal = viewHeight - imageHeight;
        if(cal > 0) {
            cal /= 2.0f;
            setValueToImageMatrix(Matrix.MTRANS_Y, cal, matrix);
        }
    }
    
    
    
    /**
     * X方向の余白チェック
     * @param bitmap
     * @param scale
     * @param matrix
     */
    private void chkXPosition(Bitmap bitmap, float scale, Matrix matrix) {
        
        float viewWidth = (float)super.getWidth();
        float imageWidth = (float)bitmap.getWidth();
        imageWidth *= scale;
        
        float[] values = new float[MATRIX_VALUES_NUM];
        matrix.getValues(values);
        
        float currentX = values[Matrix.MTRANS_X];
        
        if(currentX > 0) {
            // 画面左に余白あり
            setValueToImageMatrix(Matrix.MTRANS_X, 0f, matrix);
        }else if((imageWidth + currentX) < viewWidth){
            // 画面右に余白あり
            float cal = values[Matrix.MTRANS_X] + (viewWidth - (imageWidth + currentX));
            setValueToImageMatrix(Matrix.MTRANS_X, cal, matrix);
        }
    }
    
    
    
    /**
     * Y方向の余白チェック
     * @param bitmap
     * @param scale
     * @param matrix
     */
    private void chkYPosition(Bitmap bitmap, float scale, Matrix matrix) {
        
        float viewHeight = (float)super.getHeight();
        float imageHeight = (float)bitmap.getHeight();
        imageHeight *= scale;
        
        float[] values = new float[MATRIX_VALUES_NUM];
        matrix.getValues(values);
        
        float currentY = values[Matrix.MTRANS_Y];
        
        if(viewHeight > imageHeight) {
            return;
        }
        
        if(currentY > 0) {
            // 画面左に余白あり
            setValueToImageMatrix(Matrix.MTRANS_Y, 0f, matrix);
        }else if((imageHeight + currentY) < viewHeight){
            // 画面右に余白あり
            float cal = values[Matrix.MTRANS_Y] + (viewHeight - (imageHeight + currentY));
            setValueToImageMatrix(Matrix.MTRANS_Y, cal, matrix);
        }
    }
    
    
    
    /**
     * ファイルをビットマップに変換する
     * @param filePath
     * @return
     */
    private Bitmap getBitmap(String filePath) {
        
        if(mFilePath == null) {
            return null;
        }
        // Bitmap取得
        Bitmap bitmap = loadBitmap(mFilePath);
        return bitmap;
    }
    
    
    
    /**
     * ファイルをビットマップに変換する
     * @param filePath
     * @return
     */
    private Bitmap loadBitmap(String filePath) {
        
        int width = super.getWidth();
        int height = super.getHeight();
        
        BitmapFactory.Options options = new BitmapFactory.Options();
        return loadImage(filePath, options, width, height);
    }
    
    
    
    /**
     * ビットマップをロードする
     * @param path
     * @param options
     * @param maxWidth
     * @param maxHeight
     * @return
     */
    private Bitmap loadImage(String path, BitmapFactory.Options options, int maxWidth, int maxHeight) {
        
        Bitmap  bmpImage;
        
        // ビットマップデコード(生成後のビットマップ情報のみ取得)
        decode(options, path, true);
        
        int imageWidth = options.outWidth;
        int imageHeight = options.outHeight;
        
        // リサイズ必要かチェック
        if(chkSize(imageWidth, imageHeight, maxWidth, maxHeight)) {
            // リサイズする際の縮小率を取得
            int scale = getBmpImageScale(imageWidth, imageHeight, maxWidth, maxHeight);
            // 縮小率設定
            options.inSampleSize = scale;
        }
        // ビットマップデコード(画像をデコード)
        bmpImage = decode(options, path, false);
        
        return bmpImage;
    }
    
    
    
    /**
     * ビットマップにする際にリサイズ必要かチェックする
     * @param imageWidth
     * @param imageHeight
     * @param maxWidth
     * @param maxHeight
     * @return
     */
    private boolean chkSize(int imageWidth, int imageHeight, int maxWidth, int maxHeight) {
        
        boolean isResize = true;
        if(imageWidth <= maxWidth && imageHeight <= maxHeight) {
            // 縮小しないでも画面に収まる
            isResize = false;
        }
        return isResize;
    }
    
    
    /**
     * ビットマップへデコードする
     * @param options
     * @param path
     * @param decodeBounds
     * @return
     */
    private Bitmap decode(BitmapFactory.Options options, String path, boolean decodeBounds) {
        
        options.inJustDecodeBounds = decodeBounds;
        return BitmapFactory.decodeFile(path, options);
    }
    
    
    
    /**
     * ビットマップ生成時の縮尺率を取得する
     * @param imageWidth
     * @param imageHeight
     * @param maxWidth
     * @param maxHeight
     * @return
     */
    private int getBmpImageScale(float imageWidth, float imageHeight, float maxWidth, float maxHeight) {
        
        int retScale = 1;
        
        float scaleX = (imageWidth / maxWidth) + 1.0f;
        float scaleY = (imageHeight / maxHeight) + 1.0f;
        
        // ここでは小数点以下を切り捨てていますが、
        // 小数点以下を切り捨てるとちょっとはみ出るので注意が必要
        retScale = Math.max((int)scaleX, (int)scaleY);
        
        return retScale;
    }
    
    
    
    /**
     * シングルタッチ(ドラッグ)イベント
     * @param event
     */
    private void actionDown(MotionEvent event) {
        
        // タッチ位置保持
        mMovePoint.set(event.getX(), event.getY());
        // Matrix値をロード
        mImageMatrix.set(mSavedImageMatrix);
    }
    
    
    
    /**
     * ドラッグイベント処理
     * @param event
     */
    private void actionMove(MotionEvent event) {
        
        PointF current = new PointF(event.getX(), event.getY());
        
        // X、Y座標の変化差分を取得
        float deltaX = current.x - mMovePoint.x;
        float deltaY = current.y - mMovePoint.y;
        
        // 画像の移動を実行
        mImageMatrix.postTranslate(deltaX, deltaY);
        super.setImageMatrix(mImageMatrix);
    }
    
    
    
    /**
     * マルチタッチ(ピンチ操作)イベント
     * @param event
     * @return
     */
    private boolean actionPointerDown(MotionEvent event) {
        
        float span = getSpan(event);
        if(span < 10f) {
            // 2点距離が短い場合は、無視。
            return false;
        }
        mSpan = span;
        
        // 拡縮の基点座標取得
        float deltaX = event.getX(0) + event.getX(1);
        float deltaY = event.getY(0) + event.getY(1);
        
        mMidPoint.set(deltaX / 2f, deltaY / 2f);
        mSavedImageMatrix.set(super.getImageMatrix());
        
        return true;
    }
    
    
    
    /**
     * ピンチ操作処理
     * @param event
     * @return
     */
    private boolean actionPointerMove(MotionEvent event) {
        
        mImageMatrix.set(mSavedImageMatrix);
        
        // scale
        float currentScale = getMatrixScale(mImageMatrix);
        float scale = getScale(event);
        
        float tmpScale = scale * currentScale;
        if(tmpScale < mInitialScale) {
            // 最小拡大率を下回る場合は、イベントを無視
            return false;
        }
        
        if(tmpScale > mMaxScale) {
            // 最大拡大率を超える場合は、イベントを無視
            return false;
        }
        
        // 拡縮率と基点を設定
        mImageMatrix.postScale(scale, scale, mMidPoint.x, mMidPoint.y);
        // 描画用にMatrixを設定
        super.setImageMatrix(mImageMatrix);
        
        return true;
    }
    
    
    
    /**
     * 拡縮率取得
     * @param event
     * @return
     */
    private float getScale(MotionEvent event) {
        
        float span = getSpan(event);
        
        return span / mSpan;
    }
    
    
    
    /**
     * 現在の拡縮率取得
     * @param matrix
     * @return
     */
    private float getMatrixScale(Matrix matrix) {
        
        float[] values = new float[MATRIX_VALUES_NUM];
        matrix.getValues(values);
        
        float currentScale = values[Matrix.MSCALE_X];
        if(currentScale == 0f) {
            return 1f;
        }
        return currentScale;
    }
    
    
    
    /**
     * 2点距離取得
     * @param event
     * @return
     */
    private float getSpan(MotionEvent event) {
        
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        
        return FloatMath.sqrt(x * x + y * y);
    }
    
}

※今回作成した独自ImageViewは、onDraw()内の塗りつぶし色がWhite色決めうちにしており制限事項はありますが、 こんな感じで作ればできるよってところまでで、今回は終わりにしたいと思います。

参考にしたサイトおよび資料

    Android(X06HT Desire) ピンチイン・ピンチアウトのサンプルを作成してみる

    Matrixクラスを使ってBitmapを加工する

    グラフィックス(6)-Bitmapの描画とMatrixの操作

    Androidででっかい画像ファイルを扱う



inserted by FC2 system