旋转控件(一):矩形的平移旋转缩放

作者 : 开心源码 本文共3752个字,预计阅读时间需要10分钟 发布时间: 2022-05-12 共251人阅读

之前做了这个东西,一个内部素材加外操作边框,包含基本的移动、缩放、旋转,拉伸,快速定位,十字对齐等操作。常见使用场景如增加马赛克,增加画中画等。感觉比较有意思而且中间也遇到了少量问题就记录一下

先上图:

scaleRotae.gif

如图,这次就先讲一下平移、旋转、缩放

假如只是view做平移,有很多种实现方式比方通过layout、动画等。
基于我们的使用场景:知道一个图片的位置信息和旋转角度(中心旋转),即可以将它画出来。所以选择了维护一个rect以及绕中心旋转的角度rotation,这样少量第三方如需要c层操作的地方,直接把这些信息给c层也可以马上明确。

一、如何画
可以看出这个控件其实是一个图片+外面一个框,不过要考虑到旋转的情况,这里我们就借助matrix来做中心旋转。其实相对的就是先把canvas绕图片的中心做一次旋转

  @Override protected void dispatchDraw(Canvas canvas) {    canvas.save();    canvas.concat(mRotateMatrix);    mainDrawable.setBounds((int) mRect.left, (int) mRect.top, (int) mRect.right,        (int) mRect.bottom);    mainDrawable.draw(canvas);    canvas.drawRect(mRect, mPaint);    drawAnchors(canvas, mRect);    canvas.restore();    super.dispatchDraw(canvas);  }

虚线就用paint设置setPathEffect即可以了,虚线框角上的几个角标也是借助rect的位置信息来画出来
比方画右上角的旋转按钮:

if (rotateDrawable != null) {      //右上角      rotateDrawable.setBounds(right - drawableWidth, top - drawableHeight, right + drawableWidth,          top + drawableHeight);      rotateDrawable.draw(canvas);    }

重点就是去计算上面代码的mRotateMatrix

  private void invalidateMatrix() {    mRotateMatrix.reset();    mRotateMatrix.postTranslate(-mRect.centerX(), -mRect.centerY());    mRotateMatrix.postRotate(mRotation);    mRotateMatrix.postTranslate(mRect.centerX(), mRect.centerY());  }

这里简单提一下matrix。学过线性代数的都知道矩阵吧,matrix其实就是个3×3的矩阵,里面的元素控制着旋转、缩放、平移、错切。直接new出来的矩阵是一个单位矩阵,形容的就是原来的图形信息,没有做变换。
而后就是矩阵计算不满足交换律,换句话说矩阵的前乘和后乘结果不一样,反应在matrix里面就是pre和post接口效果不一样。
简单点了解就是pre是放在操作队列头,post放在队尾,还有个set会清空整个队列再把它放进去。假如觉得担心记混,推荐就用post接口,符合先进先出的准则,先post的先执行。

二、如何判断什么时候该缩放、旋转或者者平移
这里一定是事件解决相关的了。处于方便,我们在ontouchEvent的时候把操作委托给GestureDetector,在onDown回调时判断点到了哪里,比方旋转、缩放、平移。而后在onScroll回调的时候通过每次的增量dx,dy计算缩放的比例、旋转角度以及移动距离。
在手指头按下时,因为我们知道图片rec的位置信息和角标位置信息,所以通过x和y可以判断能否点到了角标。
但是有个问题是,画的矩形是在旋转过的画布上面,我们手指头的xy是屏幕上的位置,这里对应不起来,会出现旋转后就点不到角标了
为理解决这个问题,手指头按下的point需要利用matix做一次映射。在这里的场景其实就相当于把旋转的信息考虑进去,其实也完全可以自己用三角函数算,但是matrix已经提供这种接口了,而且是调用的c层计算,效率应该更高些。

 final Matrix rotateMatrix = new Matrix();    //反向旋转回去 抵消canvas的旋转    rotateMatrix.postTranslate(-mRect.centerX(), -mRect.centerY());    rotateMatrix.postRotate(-mRotation);    rotateMatrix.postTranslate(mRect.centerX(), mRect.centerY());    rotateMatrix.mapPoints(point);    eventX = point[0];    eventY = point[1];    RectF rectF = mRect;    //hit rotate  右上    if (Math.abs(rectF.right - eventX) < drawableWidth * 2        && Math.abs(rectF.top - eventY) < drawableHeight * 2) {      return HitModes.ROTATE;    }

需要注意的是这里matix的旋转角度和canvas的是相反的,其实就相当于转了n角度,而后又转回去n角度,相当于没有转。而后即可以按照没有旋转的情况判断有没有点到角标。

三、如何进行旋转、缩放、平移
第二步已经判断到客户想要进行什么操作了,接下来就是执行对应的操作了。
平移
先说简单的平移吧,上面说了,我们的场景是一个矩形位置信息+绕中心旋转角度。所以平移其实就是改矩形的位置罢了,直接调rec的offset,而后肯定要记得在重新绘制前升级canvas的旋转矩阵

  private void onMove(float dx, float dy) {    mRect.offset(-dx, -dy);    invalidateMatrix();    invalidate();  }

缩放
首先我们要明确一个东西,就是缩放时旋转中心肯定是不变的。所以可以算出中心和右下角的距离以及scroll后的中心和右下角的距离算出两个距离的变化当成x的变化。我们这里是等比例缩放,根据比例算出y的变化。
这里做了一个缩放最小的限制,缩放到1.5个角度宽高后就不嫩再缩小了,这样可以保证角标不会挤在一起。

  private void onScale(float dx, float dy) {    // TODO: 2019/4/8 这里的dx,dy计算需要改进    float[] pt1 = new float[] { mRect.centerX(), mRect.centerY() };    float[] pt2 = new float[] { mRect.right, mRect.bottom };    float[] pt3 = new float[] { mRect.right + dx, mRect.bottom + dy };    float distance1 = getPointDistance(pt1, pt2);    float distance2 = getPointDistance(pt1, pt3);    float distance = distance1 - distance2;    if (!checkCanScale(distance)) {      return;    }    mRect.inset(-distance, -distance / mRatio);    invalidateMatrix();    invalidate();  }

旋转
至于旋转其实也简单,由于旋转时中心也是不变的,相似缩放的操作。根据右上角的旋转角标和中心的角度,以及scroll后右上角和中心的角度,这两个的角度差就是旋转角度。
已知两个点的位置,通过math的atan2函数可以算出角度

  private void onRotate(float triggerX, float triggerY) {    // TODO: 2019/4/8 这里的dx,dy计算需要改进    float[] pt1 = new float[] { mRect.centerX(), mRect.centerY() };    float[] pt2 = new float[] { mRect.right, mRect.top };    float[] pt3 = new float[] { triggerX, triggerY };    double angel1 = PointUtil.calculateAngleBetweenPoints(pt2, pt1);    double angel2 = PointUtil.calculateAngleBetweenPoints(pt3, pt1);    mRotation = (float) (angel1 - angel2);    invalidateMatrix();    invalidate();  }

到这里基本的平移旋转缩放都详情完了,详细的少量位置计算可以参考demo
dynamicBai/ScaleRotateView 给个star鼓励下呗

后面会逐渐详情:拉伸四边的操作(旋转中心会变化)、图片在屏幕上的快速定位和微调、移动时十字辅助线对齐等效果。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 旋转控件(一):矩形的平移旋转缩放

发表回复