目前自己的项目用到图表。去github上看到MPAndroidChart很受欢迎,就下载下来了用了,随着项目的迭代,有些本身不具备的需求就来了。所以就花时间看了一下他的代码。非常感谢几个同事的帮忙。
mPathBuffer.moveTo(arcStartPointX, arcStartPointY);
mPathBuffer.arcTo(
circleBox,
startAngleOuter,
sweepAngleOuter);
mPathBuffer.lineTo(
center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD),
center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD));
mPathBuffer.arcTo(
mInnerRectBuffer,
endAngleInner,
-sweepAngleInner);
public void animateY(int durationMillis, Easing.EasingOption easing) {
if (android.os.Build.VERSION.SDK_INT < 11)
return;
ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f);
animatorY.setInterpolator(Easing.getEasingFunctionFromOption(easing));
animatorY.setDuration(durationMillis);
animatorY.addUpdateListener(mListener);
animatorY.start();
}
float sliceAngle = drawAngles[j];
float phaseY = mAnimator.getPhaseY();
float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY;
float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY;
当动画更新回调AnimatorUpdateListener被不断触发时。就不断通知View执行ondraw,sweepAngle不断改变,达到动画效果。
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
* value implies that that value is the one being animated to. Two values imply starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called setName()
, where name
is
* the value of the propertyName
parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
public boolean onTouch(View v, MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setGestureStartAngle(x, y);
break;
case MotionEvent.ACTION_MOVE:
updateGestureRotation(x, y);
mChart.invalidate();
break;
}
return true;
}
public void updateGestureRotation(float x, float y) {
/*Log.e("wjq","mChart.getAngleForPoint(x, y) = " + mChart.getAngleForPoint(x, y));
Log.e("wjq","mStartAngle = " + mStartAngle);*/
mChart.setRotationAngle(mChart.getAngleForPoint(x, y) - mStartAngle);
}
public float getAngleForPoint(float x, float y) {
MPPointF c = getCenterOffsets();
double tx = x - c.x, ty = y - c.y;
double length = Math.sqrt(tx * tx + ty * ty);
double r = Math.acos(ty / length);
float angle = (float) Math.toDegrees(r);
if (x > c.x)
angle = 360f - angle;
// add 90° because chart starts EAST
angle = angle + 90f;
// neutralize overflow
if (angle > 360f)
angle = angle - 360f;
MPPointF.recycleInstance(c);
return angle;
}
坐标点落四个象限时,所求角如下图示(角1为所求值,角2为反余弦值):
第四象限: 90 - angle
第三象限: 90+angle
第二象限:90+angle
第一象限:360-angle+90
if (getQuadrant(x, y) == 1 || getQuadrant(x, y) == 4)
{
mStartAngle += end - start;
mTmpAngle += end - start;
} else
// 二、三象限,色角度值是付值
{
mStartAngle += start - end;
mTmpAngle += start - end;
}
protected void drawDataSet(Canvas c, IPieDataSet dataSet) {
float rotationAngle = mChart.getRotationAngle();
final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY;
float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD);
float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD);
}
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
stopDeceleration();
resetVelocity();
sampleVelocity(x, y);
break;
case MotionEvent.ACTION_MOVE:
sampleVelocity(x, y);
break;
case MotionEvent.ACTION_UP:
stopDeceleration();
sampleVelocity(x, y);
mDecelerationAngularVelocity = calculateVelocity();
mDecelerationLastTime = AnimationUtils.currentAnimationTimeMillis();
Utils.postInvalidateOnAnimation(mChart);
break;
}
}
private void sampleVelocity(float touchLocationX, float touchLocationY) {
long currentTime = AnimationUtils.currentAnimationTimeMillis();
_velocitySamples.add(new AngularVelocitySample(currentTime, mChart.getAngleForPoint(touchLocationX, touchLocationY)));
// Remove samples older than our sample time - 1 seconds
for (int i = 0, count = _velocitySamples.size(); i < count - 2; i++) {
if (currentTime - _velocitySamples.get(i).time > 1000) {
_velocitySamples.remove(0);
i--;
count--;
} else {
break;
}
}
}
private float calculateVelocity() {
if (_velocitySamples.isEmpty())
return 0.f;
AngularVelocitySample firstSample = _velocitySamples.get(0);
AngularVelocitySample lastSample = _velocitySamples.get(_velocitySamples.size() - 1);
// Look for a sample that's closest to the latest sample, but not the same, so we can deduce the direction
AngularVelocitySample beforeLastSample = firstSample;
for (int i = _velocitySamples.size() - 1; i >= 0; i--) {
beforeLastSample = _velocitySamples.get(i);
if (beforeLastSample.angle != lastSample.angle) {
break;
}
}
// Calculate the sampling time
float timeDelta = (lastSample.time - firstSample.time) / 1000.f;
if (timeDelta == 0.f) {
timeDelta = 0.1f;
}
// Calculate clockwise/ccw by choosing two values that should be closest to each other,
// so if the angles are two far from each other we know they are inverted "for sure"
boolean clockwise = lastSample.angle >= beforeLastSample.angle;
if (Math.abs(lastSample.angle - beforeLastSample.angle) > 270.0) {
clockwise = !clockwise;
}
// Now if the "gesture" is over a too big of an angle - then we know the angles are inverted, and we need to move them closer to each other from both sides of the 360.0 wrapping point
if (lastSample.angle - firstSample.angle > 180.0) {
firstSample.angle += 360.0;
} else if (firstSample.angle - lastSample.angle > 180.0) {
lastSample.angle += 360.0;
}
// The velocity
float velocity = Math.abs((lastSample.angle - firstSample.angle) / timeDelta);
// Direction?
if (!clockwise) {
velocity = -velocity;
}
return velocity;
}
(1)判断正反转
public void computeScroll() {
if (mDecelerationAngularVelocity == 0.f)
return; // There's no deceleration in progress
final long currentTime = AnimationUtils.currentAnimationTimeMillis();
mDecelerationAngularVelocity *= mChart.getDragDecelerationFrictionCoef();
final float timeInterval = (float) (currentTime - mDecelerationLastTime) / 1000.f;
mChart.setRotationAngle(mChart.getRotationAngle() + mDecelerationAngularVelocity * timeInterval);
mDecelerationLastTime = currentTime;
if (Math.abs(mDecelerationAngularVelocity) >= 0.001)
Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by Google
else
stopDeceleration();
}