public class LevelViewextends View {
/**
* 最大圈半径
*/
private float mLimitRadius =0;
boolean isoverDraw =true;
/**
* 气泡半径
*/
private float mBubbleRadius;
/**
* 最大限制圈颜色
*/
private int mLimitColor;
/**
* 限制圈宽度
*/
private float mLimitCircleWidth;
/**
* 气泡中心标准圆颜色
*/
private int mBubbleRuleColor;
/**
* 气泡中心标准圆宽
*/
private float mBubbleRuleWidth;
/**
* 气泡中心标准圆半径
*/
private float mBubbleRuleRadius;
/**
* 水平后的颜色
*/
private int mHorizontalColor;
/**
* 气泡颜色
*/
private int mBubbleColor;
private PaintmBubblePaint;
private PaintmLimitPaint;
private PaintmBubbleRulePaint;
/**
* 中心点坐标
*/
private PointFcenterPnt =new PointF();
/**
* 计算后的气泡点
*/
private PointFbubblePoint;
private double pitchAngle =0;
private double rollAngle =0;
private double doubley;
private double doublex;
private Vibratorvibrator;
Handlerhandler =new Handler() {
@Override
public void handleMessage(Message msg) {
//考虑气泡边界不超出限制圆,此处减去气泡的显示半径,做为最终的限制圆半径
float limitRadius =mLimitRadius -mBubbleRadius;
bubblePoint = convertCoordinate(rollAngle, pitchAngle, mLimitRadius);
outLimit(bubblePoint, limitRadius);
//坐标超出最大圆,取法向圆上的点
if (outLimit(bubblePoint, limitRadius)) {
onCirclePoint(bubblePoint, limitRadius);
}
invalidate();
handler.sendEmptyMessageDelayed(1000, 5);
if (doubley >rollAngle&&doubley-rollAngle>0.03) {
rollAngle +=0.01;
}else if (doubley
rollAngle -=0.01;
}
if (doublex >pitchAngle&&doublex-pitchAngle>0.03) {
pitchAngle +=0.01;
}else if (doublex
pitchAngle -=0.01;
}
}
};
public LevelView(Context context) {
super(context);
init(null, 0);
}
public LevelView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public LevelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
// Load attributes
handler.sendEmptyMessageDelayed(1000, 50);
final TypedArray a = getContext().obtainStyledAttributes(
attrs, R.styleable.LevelView, defStyle, 0);
mBubbleRuleColor = a.getColor(R.styleable.LevelView_bubbleRuleColor, mBubbleRuleColor);
mBubbleColor = a.getColor(R.styleable.LevelView_bubbleColor, mBubbleColor);
mLimitColor = a.getColor(R.styleable.LevelView_limitColor, mLimitColor);
mHorizontalColor = a.getColor(R.styleable.LevelView_horizontalColor, mHorizontalColor);
mLimitRadius = a.getDimension(R.styleable.LevelView_limitRadius, mLimitRadius);
mBubbleRadius = a.getDimension(R.styleable.LevelView_bubbleRadius, mBubbleRadius);
mLimitCircleWidth = a.getDimension(R.styleable.LevelView_limitCircleWidth, mLimitCircleWidth);
mBubbleRuleWidth = a.getDimension(R.styleable.LevelView_bubbleRuleWidth, mBubbleRuleWidth);
mBubbleRuleRadius = a.getDimension(R.styleable.LevelView_bubbleRuleRadius, mBubbleRuleRadius);
a.recycle();
mBubblePaint =new Paint();
mBubblePaint.setColor(mBubbleColor);
mBubblePaint.setStyle(Paint.Style.STROKE);
mBubblePaint.setAntiAlias(true);
mLimitPaint =new Paint();
mLimitPaint.setStyle(Paint.Style.STROKE);
mLimitPaint.setColor(mLimitColor);
mLimitPaint.setStrokeWidth(mLimitCircleWidth);
//抗锯齿
mLimitPaint.setAntiAlias(true);
mBubbleRulePaint =new Paint();
mBubbleRulePaint.setColor(mBubbleRuleColor);
mBubbleRulePaint.setStyle(Paint.Style.FILL);
mBubbleRulePaint.setStrokeWidth(mBubbleRuleWidth);
mBubbleRulePaint.setAntiAlias(true);
vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
calculateCenter(widthMeasureSpec, heightMeasureSpec);
}
private void calculateCenter(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.makeMeasureSpec(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
int height = MeasureSpec.makeMeasureSpec(heightMeasureSpec, MeasureSpec.UNSPECIFIED);
int center = Math.min(width, height) /2;
centerPnt.set(center, center);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
boolean isCenter = isCenter(bubblePoint);
int limitCircleColor = isCenter ?mHorizontalColor :mLimitColor;
int bubbleColor = isCenter ?mHorizontalColor :mBubbleColor;
if (this.isCenter !=null) {
this.isCenter.isCenter(isCenter);
}
mBubblePaint.setColor(bubbleColor);
mLimitPaint.setColor(limitCircleColor);
mBubbleRulePaint.setColor(bubbleColor);
canvas.drawCircle(centerPnt.x, centerPnt.y, mBubbleRuleRadius, mBubbleRulePaint);
canvas.drawCircle(centerPnt.x, centerPnt.y, mLimitRadius, mLimitPaint);
drawBubble(canvas);
}
private boolean isCenter(PointF bubblePoint) {
if (bubblePoint ==null) {
return false;
}
return Math.abs(bubblePoint.x -centerPnt.x) <20 && Math.abs(bubblePoint.y -centerPnt.y) <20;
}
private void drawBubble(Canvas canvas) {
if (bubblePoint !=null) {
canvas.drawCircle(bubblePoint.x, bubblePoint.y, mBubbleRadius, mBubblePaint);
}
}
/**
* Convert angle to screen coordinate point.
*
* @param rollAngle 横滚角(弧度)
* @param pitchAngle 俯仰角(弧度)
* @return
*/
private PointFconvertCoordinate(double rollAngle, double pitchAngle, double radius) {
double scale = radius / Math.toRadians(90);
//以圆心为原点,使用弧度表示坐标
double x0 = -(rollAngle * scale);
double y0 = -(pitchAngle * scale);
//使用屏幕坐标表示气泡点
double x =centerPnt.x - x0;
double y =centerPnt.y - y0;
return new PointF((float) x, (float) y);
}
/**
* @param rollAngle (弧度)
*/
public void setAngle(double rollAngle) {
this.doubley = rollAngle;
}
public void setpitchAngle(double pitchAngle){
this.doublex = pitchAngle;
}
/**
* 验证气泡点是否超过限制{@link #mLimitRadius}
*
* @param bubblePnt
* @return
*/
private boolean outLimit(PointF bubblePnt, float limitRadius) {
float cSqrt = (bubblePnt.x -centerPnt.x) * (bubblePnt.x -centerPnt.x)
+ (centerPnt.y - bubblePnt.y) * +(centerPnt.y - bubblePnt.y);
return cSqrt - limitRadius * limitRadius >0;
}
/**
* 计算圆心到 bubblePnt点在圆上的交点坐标
* 即超出圆后的最大圆上坐标
*
* @param bubblePnt 气泡点
* @param limitRadius 限制圆的半径
* @return
*/
private PointFonCirclePoint(PointF bubblePnt, double limitRadius) {
double azimuth = Math.atan2((bubblePnt.y -centerPnt.y), (bubblePnt.x -centerPnt.x));
azimuth = azimuth <0 ?2 * Math.PI + azimuth : azimuth;
//圆心+半径+角度 求圆上的坐标
double x1 =centerPnt.x + limitRadius * Math.cos(azimuth);
double y1 =centerPnt.y + limitRadius * Math.sin(azimuth);
bubblePnt.set((float) x1, (float) y1);
return bubblePnt;
}
public double getPitchAngle() {
return this.pitchAngle;
}
public double getRollAngle() {
return this.rollAngle;
}
OnViewIsCenterisCenter;
public void setOnViewIsCenter(OnViewIsCenter isCenter) {
this.isCenter = isCenter;
}
public interface OnViewIsCenter {
void isCenter(boolean iscenter);
}
}