基础知识部分
- Android的View的坐标系是从左到右,从上到下。左上角坐标(0,0)
- 自定义style样式
- onSizeChanged
- onDraw
- OnTouchEvent
Code
- style
<declare-styleable name="EmbeddedCompassView">
<attr name="outSideRadiusStrokerColor" format="color" />
<attr name="arrowColor" format="color" />
<attr name="stickColor" format="color" />
</declare-styleable>
// 处理
TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.EmbeddedCompassView);
mOutSideRadiusStrokeColor = arr.getColor(R.styleable.EmbeddedCompassView_outSideRadiusStrokerColor, Color.DKGRAY);
mArrowColor = arr.getColor(R.styleable.EmbeddedCompassView_arrowColor, Color.DKGRAY);
mStickColor = arr.getColor(R.styleable.EmbeddedCompassView_stickColor, Color.LTGRAY);
arr.recycle();
- onSizeChanged
mWidth = w;
mHeight = h;
int contW = w - getPaddingLeft() - getPaddingRight();
int contH = h - getPaddingTop() - getPaddingBottom();
mCenterX = getPaddingLeft() + contW/2; // 中心点x
mCenterY = getPaddingTop() + contH/2; // 中心点y
mOutsideRadius = (int)(Math.min(w, h) * 0.5 - 4); // 外圆半径
mThresholdRadius = (int) (mOutsideRadius * 0.98); // 万向轮活动半径
mStickRadius = (int) (mOutsideRadius * 0.685); // 万向轮半径
- onDraw
canvas.drawCircle(mCenterX, mCenterY, mOutsideRadius, mOutSideCirclePaint); // 绘制外圆
if (isSticking) { // 滑动
int offsetMaxDistance = (mThresholdRadius-mStickRadius);
int offsetCurr = Math.max(Math.abs(positionX-mCenterX), Math.abs(positionY-mCenterY));
int alpha = Math.max((int)(255*offsetCurr*1.0/offsetMaxDistance), 10); //万向轮按下色渐变
mStickCirclePaint.setAlpha(alpha);
canvas.drawCircle(positionX, positionY, mStickRadius, mStickCirclePaint); // 绘制万向轮
onDrawArrow(canvas, positionX, positionY); // 绘制箭头
} else {
onDrawArrow(canvas, mCenterX, mCenterY); // 绘制箭头
}
// onDrawArrow
Path path = new Path();
int arrowA = 15;
int arrowB = 30;
// left
path.moveTo(positionX-mStickRadius+arrowB, positionY-arrowA);
path.lineTo(positionX-mStickRadius+arrowA, positionY);
path.lineTo(positionX-mStickRadius+arrowB, positionY+arrowA);
canvas.drawPath(path, mOutSideCirclePaint);
// right
path.moveTo(positionX+mStickRadius-arrowB, positionY-arrowA);
path.lineTo(positionX+mStickRadius-arrowA, positionY);
path.lineTo(positionX+mStickRadius-arrowB, positionY+arrowA);
canvas.drawPath(path, mOutSideCirclePaint);
//top
path.moveTo(positionX-arrowA, positionY-mStickRadius+arrowB);
path.lineTo(positionX, positionY-mStickRadius+arrowA);
path.lineTo(positionX+arrowA, positionY-mStickRadius+arrowB);
canvas.drawPath(path, mOutSideCirclePaint);
//bottom
path.moveTo(positionX-arrowA, positionY+mStickRadius-arrowB);
path.lineTo(positionX, positionY+mStickRadius-arrowA);
path.lineTo(positionX+arrowA, positionY+mStickRadius-arrowB);
canvas.drawPath(path, mOutSideCirclePaint);
滑动点范围[0~(mThresholdRadius-mStickRadius)] & TouchEvent
// 判断是否落在圆内.
private boolean isInOutSideCircle(int positionX, int positionY) {
int pow2 = (int)( Math.pow((positionX-mCenterX), 2) + Math.pow((positionY-mCenterY), 2));
int distance = (int) Math.sqrt(pow2);
return ((distance+mStickRadius) <= mThresholdRadius);
}
// OnTouchEvent
// 仅初始点在万向轮活动范围内才做处理,外界进入忽略.
int downX = (int)event.getX();
int downY = (int)event.getY();
super.onTouchEvent(event);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
if (isInOutSideCircle(downX, downY)) {
isSticking = true;
positionX = downX;
positionY = downY;
}
break;
case MotionEvent.ACTION_MOVE:
if (isSticking) {
if (isInOutSideCircle(downX, downY)) {
positionX = downX;
positionY = downY;
} else {
double dist = Math.sqrt((downX - mCenterX) * (downX - mCenterX) + (downY - mCenterY) * (downY - mCenterY));
// 格局角度cos来计算,找到最近合适的点.
positionX = (int) (((downX - mCenterX) * (mThresholdRadius - mStickRadius)) / dist + mCenterX);
positionY = (int) (((downY - mCenterY) * (mThresholdRadius - mStickRadius)) / dist + mCenterY);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (isSticking) {
isSticking = false;
invalidate();
}
break;
}
if (isSticking) {
invalidate();
if (listener != null) {
double dist = Math.sqrt((positionX - mCenterX) * (positionX - mCenterX) + (positionY - mCenterY) * (positionY - mCenterY));
double angle = Math.acos((positionX-mCenterX)/dist);
listener.onAngleChanged(angle); // 角度变化通知.[0~pi]
}
}
return true;
已上传GitHub: https://github.com/shuqianlan/weather/tree/master/app/src/main/java/com/ilifesmart/compass
网友评论