android功能引导带三角,Android 三角形环绕运动问题处理

一、实现三角形环绕圆圈运动

【1】 中心点落在圆环上

【2】运行过程中自动调整箭头方向

172329b3ce582aef3af3c5ed5a5e0e34.gif

二、代码实现

/**

* 难点1: Path的线形处理

* 难点2:原图转换,原图如果不是正方形,必须转换填充,否则很难和中心点对齐,当然,旋转Canvas也是可以做到的,这里我们主要是通过matrix,学会图片变幻

* 难点3:图片旋转中心点查询

* 难点4:切线夹角推理,这里主要考察数学中的圆形的切线问题,(degree + 90)是切线和X轴正方向推理得到的

* 难点5:Matrix pre/post关系,变换图像分为2个阶段,pre是预处理阶段,post是绘制阶段,理论上让图片先在固定坐标体系下变换,再移动到指定的位置上展示,更让人能容易理解,否则在Post时坐标系一直是动的,可能产生其他问题

*/

public class CirclePathArrowView extends View implements ValueAnimator.AnimatorUpdateListener {

private static final boolean IS_DEBUG = true;

private final Paint mPathPaint;

private Bitmap arrowBitmap = null;

private ValueAnimator animator;

private float degree = 0;

private float phase = 0;

private float speed = dp2px(1);

public CirclePathArrowView(Context context) {

this(context, null);

}

public CirclePathArrowView(Context context, @Nullable AttributeSet attrs) {

this(context, attrs, 0);

}

public CirclePathArrowView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

mPathPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);

mPathPaint.setAntiAlias(true);

mPathPaint.setFilterBitmap(false);

mPathPaint.setStyle(Paint.Style.STROKE);

mPathPaint.setStrokeWidth(dp2px(1));

mPathPaint.setColor(0xaaffffff);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int heightMode = MeasureSpec.getMode(heightMeasureSpec);

int height = 0;

if (heightMode == MeasureSpec.UNSPECIFIED) {

height = (int) dp2px(120);

} else if (heightMode == MeasureSpec.AT_MOST) {

height = Math.min(getMeasuredHeight(), getMeasuredWidth());

} else {

height = MeasureSpec.getSize(heightMeasureSpec);

}

setMeasuredDimension(getMeasuredWidth(), height);

}

public float dp2px(float dp) {

return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

float centerX = getWidth() / 2;

float centerY = getHeight() / 2;

float radius = Math.min(getWidth(), getHeight()) / 3 - mPathPaint.getStrokeWidth();

mPathPaint.setPathEffect(new DashPathEffect(new float[]{40, 20}, phase));

Path path = new Path();

path.addCircle(centerX, centerY, radius, Path.Direction.CCW);

canvas.drawPath(path, mPathPaint);

canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG));

if (arrowBitmap != null && !arrowBitmap.isRecycled()) {

double radians = Math.toRadians(degree);

float bmpCenterX = arrowBitmap.getWidth() / 2;

float bmpCenterY = arrowBitmap.getHeight() / 2;

float arrowRadius = Math.max(bmpCenterX, bmpCenterY);

float x = (float) (radius * Math.cos(radians) + centerX);

float y = (float) (radius * Math.sin(radians) + centerY);

//这里变幻图像,主要解决2个问题:【1】原图不是正方形,【2】原图变幻问题

Bitmap bmp = Bitmap.createBitmap((int) arrowRadius * 2, (int) arrowRadius * 2, Bitmap.Config.ARGB_4444);

Canvas canvasBitmap = new Canvas(bmp);

//矩阵变幻以图片本身左上角为坐标原点,而不是Canvas坐标,因此使用Matrix

Matrix matrix = new Matrix();

//预处理,移动原图坐标系,让原图中心点对齐bmp中心点,计算x,y方向的偏移量

float dx = arrowRadius * 2 - bmpCenterX * 2;

float dy = arrowRadius * 2 - bmpCenterY * 2;

matrix.preTranslate(dx, dy);

//预处理,在新坐标系中,找到坐标原点到旋转中心的偏移量

float pX = arrowRadius - dx; //px,py 也是偏移量,不是绝对坐标

float pY = arrowRadius - dx;

matrix.preRotate(degree + 90, pX, pY);

canvasBitmap.drawBitmap(arrowBitmap, matrix, mPathPaint);

if (IS_DEBUG) {

canvas.drawBitmap(arrowBitmap, matrix, mPathPaint);

}

RectF rectF = new RectF();

rectF.left = x - arrowRadius;

rectF.right = x + arrowRadius;

rectF.top = y - arrowRadius;

rectF.bottom = y + arrowRadius;

int color = mPathPaint.getColor();

mPathPaint.setColor(Color.MAGENTA);

//将新图会知道矩形区域

canvas.drawBitmap(bmp, null, rectF, null);

canvas.drawRect(rectF, mPathPaint);

mPathPaint.setColor(color);

}

}

@Override

protected void onAttachedToWindow() {

super.onAttachedToWindow();

if (arrowBitmap == null || arrowBitmap.isRecycled()) {

arrowBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_cloud_ft_arrow);

}

if (animator != null) {

animator.cancel();

}

animator = ValueAnimator.ofFloat(0, 360).setDuration(5000);

animator.setInterpolator(new LinearInterpolator());

animator.setRepeatMode(ValueAnimator.RESTART);

animator.setRepeatCount(ValueAnimator.INFINITE);

animator.addUpdateListener(this);

animator.start();

}

@Override

protected void onDetachedFromWindow() {

super.onDetachedFromWindow();

if (arrowBitmap != null) {

arrowBitmap.recycle();

arrowBitmap = null;

}

if (animator != null) {

animator.cancel();

}

}

@Override

public void onAnimationUpdate(ValueAnimator animation) {

degree = (float) animation.getAnimatedValue();

invalidate();

phase += speed;

if (phase > Integer.MAX_VALUE) {

phase = phase % speed;

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值