SoFunction
Updated on 2025-03-11

Using matrix to control the rotation, zoom and movement of pictures in Android


/**
* Use matrix to control image movement, zoom, and rotation
 */ 
public class CommonImgEffectView extends View { 

    private Context context ; 
    private Bitmap mainBmp , controlBmp ; 
    private int mainBmpWidth , mainBmpHeight , controlBmpWidth , controlBmpHeight ; 
    private Matrix matrix ;  
    private float [] srcPs , dstPs ; 
    private RectF srcRect , dstRect ; 
    private Paint paint ,paintRect , paintFrame; 
private float deltaX = 0, deltaY = 0; //displacement value
private float scaleValue = 1; //Scaling value
    private Point lastPoint ; 
    private Point prePivot , lastPivot; 
    private float preDegree , lastDegree ; 
private short currentSelectedPointindex;         //Click the current operation
private Point symmetricPoint = new Point();    //The current operation point symmetric point

    /**
* Image operation type
     */ 
public static final int OPER_DEFAULT = -1;      //Default
public static final int OPER_TRANSLATE = 0;     //Mobile
public static final int OPER_SCALE = 1;           //Scaling
public static final int OPER_ROTATE = 2;        //Rotate
public static final int OPER_SELECTED = 3;      //Select
    public int lastOper = OPER_DEFAULT; 

/* Picture control point
     * 0---1---2
     * |       |
     * 7   8   3
     * |       |
     * 6---5---4 
     */ 
    public static final int CTR_NONE = -1; 
    public static final int CTR_LEFT_TOP = 0; 
    public static final int CTR_MID_TOP = 1; 
    public static final int CTR_RIGHT_TOP = 2; 
    public static final int CTR_RIGHT_MID = 3; 
    public static final int CTR_RIGHT_BOTTOM = 4; 
    public static final int CTR_MID_BOTTOM = 5; 
    public static final int CTR_LEFT_BOTTOM = 6; 
    public static final int CTR_LEFT_MID = 7; 
    public static final int CTR_MID_MID = 8; 
    public int current_ctr = CTR_NONE; 

    public CommonImgEffectView(Context context){ 
        super(context); 
        = context ; 
    } 

    public CommonImgEffectView(Context context, AttributeSet attrs) { 
        super(context, attrs); 
        = context ; 
        initData(); 
    } 

    /**
* Initialize data
* @author Zhang Jin
     */ 
    private void initData(){ 
        mainBmp = ((), ); 
        controlBmp = ((), ); 
        mainBmpWidth = (); 
        mainBmpHeight = (); 
        controlBmpWidth = (); 
        controlBmpHeight = (); 

        srcPs = new float[]{ 
                                0,0,  
                                mainBmpWidth/2,0,  
                                mainBmpWidth,0,  
                                mainBmpWidth,mainBmpHeight/2, 
                                mainBmpWidth,mainBmpHeight,  
                                mainBmpWidth/2,mainBmpHeight,  
                                0,mainBmpHeight,  
                                0,mainBmpHeight/2,  
                                mainBmpWidth/2,mainBmpHeight/2 
                            }; 
        dstPs = (); 
        srcRect = new RectF(0, 0, mainBmpWidth, mainBmpHeight); 
        dstRect = new RectF(); 

        matrix = new Matrix(); 

        prePivot = new Point(mainBmpWidth/2, mainBmpHeight/2); 
        lastPivot = new Point(mainBmpWidth/2, mainBmpHeight/2); 

        lastPoint = new Point(0,0); 

        paint = new Paint(); 

        paintRect = new Paint(); 
        (); 
        (100); 
        (true); 

        paintFrame = new Paint(); 
        (); 
        (true); 

        setMatrix(OPER_DEFAULT); 
    } 

    /**
* Matrix transformation to achieve the purpose of graph translation
* @author Zhang Jin
     */ 
    private void setMatrix(int operationType){ 
        switch (operationType) { 
        case OPER_TRANSLATE: 
            (deltaX , deltaY); 
            break; 
        case OPER_SCALE: 
            (scaleValue, scaleValue, , ); 
            break; 
        case OPER_ROTATE: 
            (preDegree - lastDegree, dstPs[CTR_MID_MID * 2], dstPs[CTR_MID_MID * 2 + 1]); 
            break; 
        } 

        (dstPs, srcPs); 
        (dstRect, srcRect); 
    } 

    private boolean isOnPic(int x , int y){ 
        if((x, y)){ 
            return true; 
        }else  
            return false; 
    } 

    private int getOperationType(MotionEvent event){ 

        int evX = (int)(); 
        int evY = (int)(); 
        int curOper = lastOper; 
        switch(()) { 
            case MotionEvent.ACTION_DOWN: 
                current_ctr = isOnCP(evX, evY); 
                ("img", "current_ctr is "+current_ctr); 
                if(current_ctr != CTR_NONE || isOnPic(evX, evY)){ 
                    curOper = OPER_SELECTED; 
                } 
                break; 
            case MotionEvent.ACTION_MOVE: 
                    if(current_ctr > CTR_NONE && current_ctr < CTR_MID_MID ){ 
                        curOper = OPER_SCALE; 
                    }else if(current_ctr == CTR_MID_MID ){ 
                        curOper = OPER_ROTATE; 
                    }else if(lastOper == OPER_SELECTED){ 
                        curOper = OPER_TRANSLATE; 
                    } 
                break; 
            case MotionEvent.ACTION_UP: 
                curOper = OPER_SELECTED; 
                break; 
            default: 
                break; 
        } 
        ("img", "curOper is "+curOper); 
        return curOper; 

    } 

     /**
* The control point where the judgment point is located
     * @param evX
     * @param evY
     * @return
     */  
    private int isOnCP(int evx, int evy) { 
        Rect rect = new Rect(evx-controlBmpWidth/2,evy-controlBmpHeight/2,evx+controlBmpWidth/2,evy+controlBmpHeight/2); 
        int res = 0 ; 
        for (int i = 0; i < ; i+=2) { 
            if(((int)dstPs[i], (int)dstPs[i+1])){ 
                return res ; 
            } 
            ++res ;  
        } 
        return CTR_NONE; 
    } 

     

    @Override 
    public boolean dispatchTouchEvent(MotionEvent event) { 
        int evX = (int)(); 
        int evY = (int)(); 

        int operType = OPER_DEFAULT; 
        operType = getOperationType(event); 

        switch (operType) { 
        case OPER_TRANSLATE: 
            translate(evX, evY); 
            break; 
        case OPER_SCALE: 
            scale(event); 
            break; 
        case OPER_ROTATE: 
            rotate(event); 
            break; 
        } 

        = evX; 
        = evY;   

        lastOper = operType; 
invalidate();//Repaint
        return true; 
    } 

    /**
* move
     * @param evx
     * @param evy
     * @author zhang_jin1
     */ 
    private void translate(int evx , int evy){ 

        += evx - ; 
        += evy -; 

        deltaX = - ; 
        deltaY = - ; 

        = ; 
        = ; 

setMatrix(OPER_TRANSLATE); //Set the matrix

    } 

    /**
* Zoom
     * 0---1---2
     * |       |
     * 7   8   3
     * |       |
     * 6---5---4
     * @param evX
     * @param evY
     */ 
    private void scale(MotionEvent event) { 

        int pointIndex = current_ctr*2 ; 

        float px = dstPs[pointIndex]; 
        float py = dstPs[pointIndex+1]; 

        float evx = (); 
        float evy = (); 

        float oppositeX = 0 ; 
        float oppositeY = 0 ; 
        if(current_ctr<4 && current_ctr >= 0){ 
             oppositeX = dstPs[pointIndex+8]; 
             oppositeY = dstPs[pointIndex+9]; 
        }else if(current_ctr >= 4){ 
             oppositeX = dstPs[pointIndex-8]; 
             oppositeY = dstPs[pointIndex-7]; 
        } 
        float temp1 = getDistanceOfTwoPoints(px,py,oppositeX,oppositeY); 
        float temp2 = getDistanceOfTwoPoints(evx,evy,oppositeX,oppositeY); 

        = temp2 / temp1 ; 
        = (int) oppositeX; 
        = (int)oppositeY; 

        ("img", "scaleValue is "+scaleValue); 
        setMatrix(OPER_SCALE); 
    } 

    /**
* Rotate picture
     * 0---1---2
     * |       |
     * 7   8   3
     * |       |
     * 6---5---4 
     * @param evX
     * @param evY
     */ 
    private void rotate(MotionEvent event) { 

        if(() == 2){ 
            preDegree = computeDegree(new Point((int)(0), (int)(0)), new Point((int)(1), (int)(1))); 
        }else{ 
            preDegree = computeDegree(new Point((int)(), (int)()), new Point((int)dstPs[16], (int)dstPs[17])); 
        } 
        setMatrix(OPER_ROTATE); 
        lastDegree = preDegree; 
    } 

     
    /**
* Calculate the angle between the two points and the vertical direction
     * @param p1
     * @param p2
     * @return
     */ 
    public float computeDegree(Point p1, Point p2){ 
        float tran_x = - ; 
        float tran_y = - ; 
        float degree = 0.0f; 
        float angle = (float)((tran_x/(tran_x*tran_x + tran_y* tran_y))*180/); 
        if(!(angle)){ 
if(tran_x >= 0 && tran_y <= 0){//First Quadrant
                degree = angle; 
}else if(tran_x <= 0 && tran_y <= 0){//Second Quadrant
                degree = angle; 
}else if(tran_x <= 0 && tran_y >= 0){//Third Quadrant
                degree = -180 - angle; 
}else if(tran_x >= 0 && tran_y >= 0){//The fourth quadrant
                degree = 180 - angle; 
            } 
        } 
        return degree; 
    } 

    /**
* Calculate the distance between two points
     * @param p1
     * @param p2
     * @return
     */ 
    private float getDistanceOfTwoPoints(Point p1, Point p2){ 
        return (float)((( - ) * ( - ) + ( - ) * ( - ))); 
    } 

    private float getDistanceOfTwoPoints(float x1,float y1,float x2,float y2){ 
        return (float)(((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))); 
    } 

     
    @Override 
    public void onDraw(Canvas canvas){ 
drawBackground(canvas);//Draw the background to test the mapping of the rectangle.
(mainBmp, matrix, paint);//Draw the main picture
drawFrame(canvas);//Draw the border to test the mapping of points
drawControlPoints(canvas);//Draw control point pictures
    } 

    private void drawBackground(Canvas canvas){ 
        (dstRect, paintRect); 
    } 

    private void drawFrame(Canvas canvas){ 
        (dstPs[0], dstPs[1], dstPs[4], dstPs[5], paintFrame); 
        (dstPs[4], dstPs[5], dstPs[8], dstPs[9], paintFrame); 
        (dstPs[8], dstPs[9], dstPs[12], dstPs[13], paintFrame); 
        (dstPs[0], dstPs[1], dstPs[12], dstPs[13], paintFrame); 
        (dstPs[16], dstPs[17], paintFrame); 
    } 

    private void drawControlPoints(Canvas canvas){ 

        for (int i = 0; i < ; i+=2) { 
            (controlBmp, dstPs[i]-controlBmpWidth/2, dstPs[i+1]-controlBmpHeight/2, paint); 
        } 

    }