热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android中利用matrix控制图片的旋转、缩放、移动

本篇文章是对Android中利用matrix控制图片的旋转、缩放、移动进行了详细的分析介绍,需要的朋友参考下
本文主要讲解利用android中Matrix控制图形的旋转缩放移动,具体参见一下代码:
代码如下:

/**
 * 使用矩阵控制图片移动、缩放、旋转
 */ 
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; //位移值 
    private float scaleValue = 1; //缩放值 
    private Point lastPoint ; 
    private Point prePivot , lastPivot; 
    private float preDegree , lastDegree ; 
    private short currentSelectedPointindex;        //当前操作点击点 
    private Point symmetricPoint  = new Point();    //当前操作点对称点 

    /**
     * 图片操作类型
     */ 
    public static final int OPER_DEFAULT = -1;      //默认 
    public static final int OPER_TRANSLATE = 0;     //移动 
    public static final int OPER_SCALE = 1;         //缩放 
    public static final int OPER_ROTATE = 2;        //旋转 
    public static final int OPER_SELECTED = 3;      //选择 
    public int lastOper = OPER_DEFAULT; 

    /* 图片控制点
     * 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); 
        this.cOntext= context ; 
    } 

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

    /**
     * 初始化数据
     * @author 张进
     */ 
    private void initData(){ 
        mainBmp = BitmapFactory.decodeResource(this.context.getResources(), R.drawable.flower); 
        cOntrolBmp= BitmapFactory.decodeResource(this.context.getResources(), R.drawable.control); 
        mainBmpWidth = mainBmp.getWidth(); 
        mainBmpHeight = mainBmp.getHeight(); 
        cOntrolBmpWidth= controlBmp.getWidth(); 
        cOntrolBmpHeight= controlBmp.getHeight(); 

        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 = srcPs.clone(); 
        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(); 
        paintRect.setColor(Color.RED); 
        paintRect.setAlpha(100); 
        paintRect.setAntiAlias(true); 

        paintFrame = new Paint(); 
        paintFrame.setColor(Color.GREEN); 
        paintFrame.setAntiAlias(true); 

        setMatrix(OPER_DEFAULT); 
    } 

    /**
     * 矩阵变换,达到图形平移的目的
     * @author 张进
     */ 
    private void setMatrix(int operationType){ 
        switch (operationType) { 
        case OPER_TRANSLATE: 
            matrix.postTranslate(deltaX , deltaY); 
            break; 
        case OPER_SCALE: 
            matrix.postScale(scaleValue, scaleValue, symmetricPoint.x, symmetricPoint.y); 
            break; 
        case OPER_ROTATE: 
            matrix.postRotate(preDegree - lastDegree, dstPs[CTR_MID_MID * 2], dstPs[CTR_MID_MID * 2 + 1]); 
            break; 
        } 

        matrix.mapPoints(dstPs, srcPs); 
        matrix.mapRect(dstRect, srcRect); 
    } 

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

    private int getOperationType(MotionEvent event){ 

        int evX = (int)event.getX(); 
        int evY = (int)event.getY(); 
        int curOper = lastOper; 
        switch(event.getAction()) { 
            case MotionEvent.ACTION_DOWN: 
                current_ctr = isOnCP(evX, evY); 
                Log.i("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                         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; 
        } 
        Log.d("img", "curOper is "+curOper); 
        return curOper; 

    } 

     /**
     * 判断点所在的控制点
     * @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             if(rect.contains((int)dstPs[i], (int)dstPs[i+1])){ 
                return res ; 
            } 
            ++res ;  
        } 
        return CTR_NONE; 
    } 

     

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

        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; 
        } 

        lastPoint.x = evX; 
        lastPoint.y = evY;   

        lastOper = operType; 
        invalidate();//重绘 
        return true; 
    } 

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

        prePivot.x += evx - lastPoint.x; 
        prePivot.y += evy -lastPoint.y; 

        deltaX = prePivot.x - lastPivot.x; 
        deltaY = prePivot.y - lastPivot.y; 

        lastPivot.x = prePivot.x; 
        lastPivot.y = prePivot.y; 

        setMatrix(OPER_TRANSLATE); //设置矩阵 

    } 

    /**
     * 缩放
     * 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 = event.getX(); 
        float evy = event.getY(); 

        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); 

        this.scaleValue = temp2 / temp1 ; 
        symmetricPoint.x = (int) oppositeX; 
        symmetricPoint.y = (int)oppositeY; 

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

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

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

     
    /**
     * 计算两点与垂直方向夹角
     * @param p1
     * @param p2
     * @return
     */ 
    public float computeDegree(Point p1, Point p2){ 
        float tran_x = p1.x - p2.x; 
        float tran_y = p1.y - p2.y; 
        float degree = 0.0f; 
        float angle = (float)(Math.asin(tran_x/Math.sqrt(tran_x*tran_x + tran_y* tran_y))*180/Math.PI); 
        if(!Float.isNaN(angle)){ 
            if(tran_x >= 0 && tran_y <= 0){//第一象限 
                degree = angle; 
            }else if(tran_x <= 0 && tran_y <= 0){//第二象限 
                degree = angle; 
            }else if(tran_x <= 0 && tran_y >= 0){//第三象限 
                degree = -180 - angle; 
            }else if(tran_x >= 0 && tran_y >= 0){//第四象限 
                degree = 180 - angle; 
            } 
        } 
        return degree; 
    } 

    /**
     * 计算两个点之间的距离
     * @param p1
     * @param p2
     * @return
     */ 
    private float getDistanceOfTwoPoints(Point p1, Point p2){ 
        return (float)(Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y))); 
    } 

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

     
    @Override 
    public void onDraw(Canvas canvas){ 
        drawBackground(canvas);//绘制背景,以便测试矩形的映射 
        canvas.drawBitmap(mainBmp, matrix, paint);//绘制主图片 
        drawFrame(canvas);//绘制边框,以便测试点的映射 
        drawControlPoints(canvas);//绘制控制点图片 
    } 

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

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

    private void drawControlPoints(Canvas canvas){ 

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

    } 



Demo效果:



推荐阅读
  • 本文分享了作者在不同版本的Ubuntu系统上尝试编译VLC for Android的经历,包括遇到的技术难题及解决方案,旨在帮助开发者避免常见的编译问题。 ... [详细]
  • 在 Django 模型中,ForeignKey 的 on_delete 参数定义了当关联对象被删除时,当前模型实例的行为。本文详细解释了 on_delete 的各个选项及其具体作用。 ... [详细]
  • Android Studio: 修改应用包名的详细步骤
    本文详细介绍了在Android Studio中如何修改应用的包名,包括具体的操作步骤和注意事项。这对于需要更改包名以适应不同需求的开发者非常有用。 ... [详细]
  • 本文介绍了一个使用mii-tool工具检查网络接口状态的Bash脚本,并将结果记录到日志文件中。 ... [详细]
  • 本文探讨了如何在Windows程序中实现高精度的定时控制,特别是针对需要精确控制发包频率的应用场景,如“小兵以太网测试仪”。 ... [详细]
  • 本文将介绍如何使用原生JavaScript实现一个连连看小游戏中的一个关键功能:当用户点击列表项时,获取该列表项的索引。 ... [详细]
  • 在使用 Python 编程时,如果在一个函数中引用了未声明为全局变量的外部变量,可能会遇到 UnboundLocalError。本文将通过一个具体的例子来解释如何解决这个问题。 ... [详细]
  • 本文详细介绍了进程、线程和协程的概念及其之间的区别与联系。进程是在内存中运行的独立实体,具有独立的地址空间和资源;线程是操作系统调度的基本单位,属于进程内部;协程则是用户态下的轻量级调度单元,性能更高。 ... [详细]
  • Python 日志记录模块详解
    日志记录机制是软件开发中不可或缺的一部分,它帮助开发者追踪和调试程序运行时的各种异常。Python 提供了内置的 logging 模块,使我们在代码中记录和管理日志信息变得更加方便。本文将详细介绍如何使用 Python 的 logging 模块。 ... [详细]
  • 题目描述:计算从起点到终点的最小能量消耗。如果下一个单元格的风向与当前单元格相同,则消耗为0,否则为1。共有8个可能的方向。 ... [详细]
  • 本文介绍如何通过参数化查询来防止SQL注入攻击,确保数据库的安全性。示例代码展示了在C#中使用参数化查询添加学生信息的方法。 ... [详细]
  • 树莓派4B:安装基础操作系统指南
    本文将详细介绍如何为树莓派4B安装基础操作系统,包括所需材料、镜像下载、镜像烧录以及更换国内源等步骤。 ... [详细]
  • 本文介绍了GitHub上的一些Python开源项目,特别是IM(即时通讯)技术的应用。通过Sealtalk项目,探讨了如何利用开源SDK提升开发效率。 ... [详细]
  • 本文探讨了如何在游戏启动画面中移除广告,特别是在游戏数据加载期间(大约5-6秒)广告会短暂显示的问题。通过调整XML布局和代码逻辑,可以实现广告的延迟加载或完全移除。 ... [详细]
  • 腾讯云移动推送TPNS(Tencent Push Notification Service)为APP开发者和运营人员提供了一站式、高效、稳定的推送解决方案,帮助提升用户活跃度和运营效率。 ... [详细]
author-avatar
交通运输程俊
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有