一个自定义控件画图形的例子让你彻底明白画布旋转和save(),restore()的使用。

安卓的控件提供了绘制接口onDraw(Canvas)方法,并提供了画布类Canvas以及丰富的api。本文通过一个示例工程来详细讲解使用Canvas进行图形的绘制、画布的变换、画布状态的存档和恢复等等。
示例代码下载

- 首先我们在MainActivity的界面上展示一个自定义控件,控件上默认画一个向上的箭头和靠近坐标原点的圆,主要绘制代码如下。 // 画一个左上角的圆

private void drawLeftUpCircle(Canvas canvas) {
// 以坐标(100,100)为圆心,80为半径画圆。
canvas.drawCircle(100, 100, 80, mLinePaint);
}

// 画一个向上的箭头
private void drawUpArrow(Canvas canvas) {
canvas.drawLine(mHalfWidth, 0, 0, mHalfHeight, mLinePaint); // 左边的蓝色加粗斜杠
mLinePaint.setColor(Color.RED);
mLinePaint.setStrokeWidth(4);
canvas.drawLine(mHalfWidth, 0, mViewWidth, mHalfHeight, mLinePaint);// 右边的斜杠
canvas.drawLine(mHalfWidth, 0, mHalfWidth, mViewHeight, mLinePaint);// 垂直的竖杠
}

显示效果如下图:

分析总结:以上是调用Canvas的方法绘制三条直线和一个圆。绘制的时候安卓的坐标系如下图所示。
在这里插入图片描述

- 点击下面的按钮则是对画布进行重绘,例如点击旋转按钮,是将画布以中点为中心顺时针旋转90度,然后在画箭头和圆。 主要绘制代码如下。

// 以坐标(mHalfWidth, mHalfHeight)为中心顺时针旋转画布90度,mHalfWidth, mHalfHeight分别为控件的一半宽和一半高。
canvas.rotate(90, mHalfWidth, mHalfHeight);
// 画箭头
drawUpArrow(canvas);
// 画圆
drawLeftUpCircle(canvas);

显示效果如下图:
在这里插入图片描述

分析总结:当我们旋转90后再画同样坐标的箭头和圆时,箭头方向和圆的位置已经改变了。这是因为旋转后画布的坐标系变成了如下图所示。
在这里插入图片描述

- 点击恢复按钮,则是先保存画布状态,再将画布以中点为中心顺时针旋转90度,然后画箭头,接着恢复画布至存档前的状态再画圆。

主要绘制代码如下。
// 保存画布状态
canvas.save();
// 以坐标(mHalfWidth, mHalfHeight)为中心顺时针旋转画布90度。
canvas.rotate(90, mHalfWidth, mHalfHeight);
// 画箭头
drawUpArrow(canvas);
// 恢复画布状态。
canvas.restore();
// 画圆
drawLeftUpCircle(canvas);

显示效果如下图:
在这里插入图片描述

分析总结:我们已经知道旋转后坐标系改变,所以箭头向左。但在绘制圆之前我们已经恢复了画布状态,此时的画布坐标系是旋转之前的。所以圆在左上角。

- 点击缩放按钮,是先缩放画布至0.5倍。然后画箭头在画圆。

主要绘制代码如下:
// 以默认原点进行缩放画布,X方向和Y方向均缩放至0.5倍。
canvas.scale(0.5f, 0.5f);
// 画箭头
drawUpArrow(canvas);
// 画圆
drawLeftUpCircle(canvas);

显示效果如下图:
在这里插入图片描述

分析总结:可以看到,缩放后再画,箭头和圆都跟着缩放。因为整个画布区域都跟着缩放了。

好的,以下是一个使用TextureView实现多点触摸控制的例子,包括移动、缩放旋转: ```java public class MultiTouchTextureView extends TextureView implements TextureView.SurfaceTextureListener { private float mPosX, mPosY; private float mLastTouchX, mLastTouchY; private float mScaleFactor = 1.f; private float mLastGestureX, mLastGestureY; private float mRotationDegrees = 0.f; public MultiTouchTextureView(Context context) { this(context, null, 0); } public MultiTouchTextureView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MultiTouchTextureView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setSurfaceTextureListener(this); } @Override public boolean onTouchEvent(MotionEvent event) { // Let the ScaleGestureDetector inspect all events. mScaleDetector.onTouchEvent(event); final int action = event.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { final float x = event.getX(); final float y = event.getY(); mLastTouchX = x; mLastTouchY = y; break; } case MotionEvent.ACTION_MOVE: { final float x = event.getX(); final float y = event.getY(); // Only move if the ScaleGestureDetector isn't processing a gesture. if (!mScaleDetector.isInProgress()) { final float dx = x - mLastTouchX; final float dy = y - mLastTouchY; mPosX += dx; mPosY += dy; invalidate(); } mLastTouchX = x; mLastTouchY = y; break; } case MotionEvent.ACTION_UP: { break; } case MotionEvent.ACTION_CANCEL: { break; } case MotionEvent.ACTION_POINTER_UP: { break; } } return true; } private ScaleGestureDetector mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() { @Override public boolean onScale(ScaleGestureDetector detector) { mScaleFactor *= detector.getScaleFactor(); // Don't let the object get too small or too large. mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); invalidate(); return true; } }); @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // Setup default size and position mPosX = width / 2.f; mPosY = height / 2.f; mLastGestureX = width / 2.f; mLastGestureY = height / 2.f; // Set the aspect ratio of the TextureView int aspectRatio = height > width ? height / width : width / height; setAspectRatio(aspectRatio); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { // Update default size and position when the TextureView size changes mPosX = width / 2.f; mPosY = height / 2.f; mLastGestureX = width / 2.f; mLastGestureY = height / 2.f; } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); // Move the canvas to the center of the TextureView canvas.translate(mPosX, mPosY); // Scale the canvas based on the scale factor canvas.scale(mScaleFactor, mScaleFactor); // Rotate the canvas based on the rotation degrees canvas.rotate(mRotationDegrees); // Draw your content here // ... canvas.restore(); } } ``` 这个例子中,我们使用了TextureView作为视图容器,通过触摸事件实现多点触摸控制。在onTouchEvent方法中,我们处理了移动的逻辑,通过ScaleGestureDetector处理缩放的逻辑。在onDraw方法中,我们先将画布移动到TextureView的中心点,然后根据移动、缩放旋转的参数对画布进行变换。你可以在onDraw方法中添加你自己的绘制逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值