Mp android chart, bar chart with bar gradient background - android

dear community, I'm facing the following problem, I'm creating a bar chart using the MP android Chart library available here: https://github.com/PhilJay/MPAndroidChart.
I'd like to set a gradient background for my bars, using the following code I was able to set the gradient background and get the following result.
val barDataSet = BarDataSet(dummyYValues, "DataSet 1")
barDataSet.axisDependency = YAxis.AxisDependency.RIGHT
val startColor = ContextCompat.getColor(context!!, R.color.top_graph)
val endColor = ContextCompat.getColor(context!!, R.color.bottom_graph)
val gradientColors: MutableList<GradientColor> = ArrayList()
gradientColors.add(GradientColor(endColor, startColor))
barDataSet.gradientColors = gradientColors
dataSets.add(barDataSet)
val finalData = BarData(dataSets)
finalData.barWidth =(0.3f)
And I'm trying to archive something like the following example
If you see the main difference is that the expected behavior displaying the gradient color in a uniform way, it means that it displays the color according to the graph not according to the values.
also, if anyone knows how to add the round corner at the top of the bars that would be helpful too.
Thanks.

I don't know the best solution but I copied the BarChart renderer class as:
public class RoundedBarChartRenderer extends BarLineScatterCandleBubbleRenderer{
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
//Use this to replace the line c.drawRect(.....)
RectF rectF = new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3]);
c.drawRoundRect(rectF,15f,15f, mRenderPaint);
}}
Use the above class like this:
chart.renderer = RoundedBarChartRenderer(
chart,
chart.animator,
chart.viewPortHandler
)

To create a gradient background for a bar chart, use the code below.
barDataSet.setGradientColor(Color.parseColor("#00FF5722"),Color.parseColor("#FFFF5722");
Then use this to curve the corners of the bar chart. I got this from another source and it works perfectly. Here is the code.
CustomBarChartRender barChartRender = new CustomBarChartRender(barchart,barchart.getAnimator(), barchart.getViewPortHandler());
barChartRender.setRadius(12);
barchart.setRenderer(barChartRender);
create a new class CustomBarChartRender and add this,
public class CustomBarChartRender extends BarChartRenderer {
private final RectF mBarShadowRectBuffer = new RectF();
private int mRadius;
public CustomBarChartRender(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
public void setRadius(int mRadius) {
this.mRadius = mRadius;
}
protected void drawDataSet(Canvas c, #NotNull IBarDataSet dataSet, int index) {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mBarBorderPaint.setColor(dataSet.getBarBorderColor());
mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth()));
mShadowPaint.setColor(dataSet.getBarShadowColor());
boolean drawBorder = dataSet.getBarBorderWidth() > 0f;
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
if (mChart.isDrawBarShadowEnabled()) {
mShadowPaint.setColor(dataSet.getBarShadowColor());
BarData barData = mChart.getBarData();
float barWidth = barData.getBarWidth();
float barWidthHalf = barWidth / 2.0f;
float x;
int i = 0;
double count = Math.min(Math.ceil((int) (double) ((float) dataSet.getEntryCount() * phaseX)), dataSet.getEntryCount());
while (i < count) {
BarEntry e = dataSet.getEntryForIndex(i);
x = e.getX();
mBarShadowRectBuffer.left = x - barWidthHalf;
mBarShadowRectBuffer.right = x + barWidthHalf;
trans.rectValueToPixel(mBarShadowRectBuffer);
if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right)) {
i++;
continue;
}
if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left))
break;
mBarShadowRectBuffer.top = mViewPortHandler.contentTop();
mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom();
c.drawRoundRect(mBarRect, mRadius, mRadius, mShadowPaint);
/*if (mRadius > 0)
c.drawRoundRect(mBarRect, mRadius, mRadius, mHighlightPaint);
else
c.drawRect(mBarRect, mHighlightPaint);*/
i++;
}
}
// initialize the buffer
BarBuffer buffer = mBarBuffers[index];
buffer.setPhases(phaseX, phaseY);
buffer.setDataSet(index);
buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));
buffer.setBarWidth(mChart.getBarData().getBarWidth());
buffer.feed(dataSet);
trans.pointValuesToPixel(buffer.buffer);
boolean isSingleColor = dataSet.getColors().size() == 1;
if (isSingleColor) {
mRenderPaint.setColor(dataSet.getColor());
}
int j = 0;
while (j < buffer.size()) {
if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) {
j += 4;
continue;
}
if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j]))
break;
if (!isSingleColor) {
// Set the color for the currently drawn value. If the index
// is out of bounds, reuse colors.
mRenderPaint.setColor(dataSet.getColor(j / 4));
}
if (dataSet.getGradientColor() != null) {
GradientColor gradientColor = dataSet.getGradientColor();
mRenderPaint.setShader(new LinearGradient(
buffer.buffer[j],
buffer.buffer[j + 3],
buffer.buffer[j],
buffer.buffer[j + 1],
gradientColor.getStartColor(),
gradientColor.getEndColor(),
android.graphics.Shader.TileMode.MIRROR));
}
if (dataSet.getGradientColors() != null) {
mRenderPaint.setShader(new LinearGradient(
buffer.buffer[j],
buffer.buffer[j + 3],
buffer.buffer[j],
buffer.buffer[j + 1],
dataSet.getGradientColor(j / 4).getStartColor(),
dataSet.getGradientColor(j / 4).getEndColor(),
Shader.TileMode.MIRROR));
}
Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3]), mRadius, mRadius);
c.drawPath(path2, mRenderPaint);
if (drawBorder) {
Path path = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3]), mRadius, mRadius);
c.drawPath(path, mBarBorderPaint);
}
j += 4;
}
}
#NotNull
private Path roundRect(#NotNull RectF rect, float rx, float ry) {
float top = rect.top;
float left = rect.left;
float right = rect.right;
float bottom = rect.bottom;
Path path = new Path();
if (rx < 0) rx = 0;
if (ry < 0) ry = 0;
float width = right - left;
float height = bottom - top;
if (rx > width / 2) rx = width / 2;
if (ry > height / 2) ry = height / 2;
float widthMinusCorners = (width - (2 * rx));
float heightMinusCorners = (height - (2 * ry));
path.moveTo(right, top + ry);
path.rQuadTo(0, -ry, -rx, -ry);//top-right corner
path.rLineTo(-widthMinusCorners, 0);
path.rQuadTo(-rx, 0, -rx, ry); //top-left corner
path.rLineTo(0, heightMinusCorners);
path.rLineTo(0, ry);
path.rLineTo(rx, 0);
path.rLineTo(widthMinusCorners, 0);
path.rLineTo(rx, 0);
path.rLineTo(0, -ry);
path.rLineTo(0, -heightMinusCorners);
path.close();//Given close, last lineto can be removed.
return path;
}}

Related

make edittext place in the middle using canvas and drawable

I learned about how to create an OTP from drawable using canvas. OTP screen
But I want to have a box instead of a plane lines at the bottom, and I want to have the text in the middle of the box, instead of 8 dp above the line. I made a few modification like adding a rectf and drawing using canvas. It was easy. But It will not be responsive on all the screens.
#Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
int availableWidth = getWidth() - getPaddingRight() - getPaddingLeft();
int availableHeight= getHeight()-getPaddingTop()-getPaddingBottom();
if (mSpace < 0) {
mCharSize = (availableWidth / (mNumChars * 2 - 1));
} else {
mCharSize = (availableWidth - (mSpace * (mNumChars - 1))) / mNumChars;
}
int startX = getPaddingLeft();
int bottom = getHeight() - getPaddingBottom();
//Text Width
Editable text = getText();
int textLength = text.length();
float[] textWidths = new float[textLength];
getPaint().getTextWidths(getText(), 0, textLength, textWidths);
Paint mPaint = new Paint();
RectF rectF;
int cornerRadius = 15;
for (int i = 0; i < mNumChars; i++) {
mPaint.setColor(getResources().getColor(R.color.colorViews));
mPaint.setAlpha(70);
mPaint.setStyle(Paint.Style.FILL);
rectF = new RectF(startX, availableHeight, startX + (int) mCharSize, bottom);
canvas.drawRoundRect(rectF,cornerRadius,cornerRadius,mPaint);
// canvas.drawLine(startX, bottom, startX + mCharSize, bottom, mPaint);
if (getText().length() > i) {
float middle = startX + mCharSize / 2;
//y= bottom-mLineSpacing
canvas.drawText(text, i, i + 1, middle - textWidths[0] / 2, bottom-mLineSpacing, getPaint());
}
if (mSpace < 0) {
startX += mCharSize * 2;
} else {
startX += mCharSize + mSpace;
}
}
}
And my xml file looks something like this
<com.example.android.EditTextOTP
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:cursorVisible="false"
android:digits="1234567890"
android:inputType="number"
android:maxLength="4"
android:textIsSelectable="false"
android:textSize="20sp"
android:background="#null"
android:paddingStart="40dp"
android:paddingEnd="40dp"
android:paddingTop="60dp"
app:layout_constraintTop_toBottomOf="#id/tv_otp_text"
app:layout_constraintStart_toStartOf="#id/gl_inside_bg"
app:layout_constraintEnd_toEndOf="#id/gl_vertical_end"
android:id="#+id/et_otp_box"
>
</com.example.android.EditTextOTP>
The above way works fine but I want to place the number as I type in the center of the BOX. not roughly on the top of the BOX. How can I improve this?

How tro draw fading path

How can I draw Path with fading (opacity or thicknes) line? Something like this.
I know there is LinearGradient shader for Paint, but it won't bend along the Path.
One possible solution might be to get points along the Path and just draw it by myself through the segments`. But I coouldn't find any method for that either.
I came up with the following code. The mos important thing is PathMeasure's getPosTan() method.
if (getGesturePath() != null) {
final short steps = 150;
final byte stepDistance = 5;
final byte maxTrailRadius = 15;
pathMeasure.setPath(getGesturePath(), false);
final float pathLength = pathMeasure.getLength();
for (short i = 1; i <= steps; i++) {
final float distance = pathLength - i * stepDistance;
if (distance >= 0) {
final float trailRadius = maxTrailRadius * (1 - (float) i / steps);
pathMeasure.getPosTan(distance, pathPos, null);
final float x = pathPos[0] + RandomUtils.nextFloat(0, 2 * trailRadius) - trailRadius;
final float y = pathPos[1] + RandomUtils.nextFloat(0, 2 * trailRadius) - trailRadius;
paint.setShader(new RadialGradient(
x,
y,
trailRadius > 0 ? trailRadius : Float.MIN_VALUE,
ColorUtils.setAlphaComponent(Color.GREEN, random.nextInt(0xff)),
Color.TRANSPARENT,
Shader.TileMode.CLAMP
));
canvas.drawCircle(x, y, trailRadius, paint);
}
}
}

Android Bitmap save without transparent area

I want to save bitmap without transparent area.
Bitmap has large transparent pixel.
So i want to remove that
How can i do this?
I cant add picture so explain with symbols.
I dont want to crop function.
I hope use filter
┌────────────────────────┐
│ transparent area
│ ┌────────┐
│ crop this
└────────┘
└────────────────────────┘
To find the non-transparent area of your bitmap, iterate across the bitmap in x and y and find the min and max of the non-transparent region. Then crop the bitmap to those co-ordinates.
Bitmap CropBitmapTransparency(Bitmap sourceBitmap)
{
int minX = sourceBitmap.getWidth();
int minY = sourceBitmap.getHeight();
int maxX = -1;
int maxY = -1;
for(int y = 0; y < sourceBitmap.getHeight(); y++)
{
for(int x = 0; x < sourceBitmap.getWidth(); x++)
{
int alpha = (sourceBitmap.getPixel(x, y) >> 24) & 255;
if(alpha > 0) // pixel is not 100% transparent
{
if(x < minX)
minX = x;
if(x > maxX)
maxX = x;
if(y < minY)
minY = y;
if(y > maxY)
maxY = y;
}
}
}
if((maxX < minX) || (maxY < minY))
return null; // Bitmap is entirely transparent
// crop bitmap to non-transparent area and return:
return Bitmap.createBitmap(sourceBitmap, minX, minY, (maxX - minX) + 1, (maxY - minY) + 1);
}
Crop transparent border with this github.
public static Bitmap crop(Bitmap bitmap) {
int height = bitmap.getHeight();
int width = bitmap.getWidth();
int[] empty = new int[width];
int[] buffer = new int[width];
Arrays.fill(empty, 0);
int top = 0;
int left = 0;
int bottom = height;
int right = width;
for (int y = 0; y < height; y++) {
bitmap.getPixels(buffer, 0, width, 0, y, width, 1);
if (!Arrays.equals(empty, buffer)) {
top = y;
break;
}
}
for (int y = height - 1; y > top; y--) {
bitmap.getPixels(buffer, 0, width, 0, y, width, 1);
if (!Arrays.equals(empty, buffer)) {
bottom = y;
break;
}
}
empty = new int[height];
buffer = new int[height];
Arrays.fill(empty, 0);
for (int x = 0; x < width; x++) {
bitmap.getPixels(buffer, 0, 1, x, 0, 1, height);
if (!Arrays.equals(empty, buffer)) {
left = x;
break;
}
}
for (int x = width - 1; x > left; x--) {
bitmap.getPixels(buffer, 0, 1, x, 0, 1, height);
if (!Arrays.equals(empty, buffer)) {
right = x;
break;
}
}
return Bitmap.createBitmap(bitmap, left, top, right - left + 1, bottom - top + 1);
}
I took #Alvaro Menezes's answer and improved it as a Kotlin extension function. I tweaked it a bit, changed some variable names for better readability and it adds more fixes to the issue mentioned by #Ahamadullah Saikat that throws an IllegalArgumentException
Note that reading pixels by line improve a lot the performances against reading this independently as the accepted answer suggest.
/**
* Trims a bitmap borders of a given color.
*
*/
fun Bitmap.trim(#ColorInt color: Int = Color.TRANSPARENT): Bitmap {
var top = height
var bottom = 0
var right = width
var left = 0
var colored = IntArray(width, { color })
var buffer = IntArray(width)
for (y in bottom until top) {
getPixels(buffer, 0, width, 0, y, width, 1)
if (!Arrays.equals(colored, buffer)) {
bottom = y
break
}
}
for (y in top - 1 downTo bottom) {
getPixels(buffer, 0, width, 0, y, width, 1)
if (!Arrays.equals(colored, buffer)) {
top = y
break
}
}
val heightRemaining = top - bottom
colored = IntArray(heightRemaining, { color })
buffer = IntArray(heightRemaining)
for (x in left until right) {
getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining)
if (!Arrays.equals(colored, buffer)) {
left = x
break
}
}
for (x in right - 1 downTo left) {
getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining)
if (!Arrays.equals(colored, buffer)) {
right = x
break
}
}
return Bitmap.createBitmap(this, left, bottom, right - left, top - bottom)
}
Following the official doc:
The new bitmap may be the same object as source, or a copy may have been made.
You should take into account when you execute .recycle() with the source bitmap.

android draw transparent gradient on canwas! Wrong work

I have a canvas!
1.On canvas i'm draw are graph (see below)!
2.Draw a points and lines between.
3.I'm was calculate are all points on a line, and draw little lines to bottom.
4.And draw gradient with are #e5fafd color!
private void drawGradient(Canvas canvas){
Paint gradientPainter = new Paint();
gradientPainter.setStrokeWidth(5);
gradientPainter.setShader(new LinearGradient(0, 0, 0, getHeight(), getResources().getColor(R.color.graph_gradient_start), Color.TRANSPARENT, Shader.TileMode.CLAMP));
for(int i = 0; i < ourCoords.size(); i++){
if(i != ourCoords.size() - 1){
final float x = ourCoords.get(i+1).getxCoord() - ourCoords.get(i).getxCoord();
final float y = ourCoords.get(i+1).getyCoord() - ourCoords.get(i).getyCoord();
float xT = ourCoords.get(i).getxCoord(), yT = ourCoords.get(i).getyCoord();
final float percentX = (float) (x / 100.0);
final float percentY = (float) (y / 100.0);
for(float j= (float) 0.1; j<100.0; j++){
float startX = (percentX * j) + xT;
float startY = (percentY * j) + yT;
float endX = startX;
float endY = startY + 400;
if(startX < (canvasWidth - marginLeft - 8)){
canvas.drawLine(startX, startY, endX, endY, gradientPainter);
}
}
}
drawPoints(canvas, ourCoords.get(i).getxCoord(), ourCoords.get(i).getyCoord(), 20);
}
}
I ended up getting a black gradient. Please help me and sorry for the mistakes.
See screen http://mepic.ru/view/?id=b1904b5d5b6d156e83206ab0c485d40a

How do I antialias the clip boundary on Android's canvas?

I'm using Android's android.graphics.Canvas class to draw a ring. My onDraw method clips the canvas to make a hole for the inner circle, and then draws the full outer circle over the hole:
clip = new Path();
clip.addRect(outerCircle, Path.Direction.CW);
clip.addOval(innerCircle, Path.Direction.CCW);
canvas.save();
canvas.clipPath(clip);
canvas.drawOval(outerCircle, lightGrey);
canvas.restore();
The result is a ring with a pretty, anti-aliased outer edge and a jagged, ugly inner edge:
What can I do to antialias the inner edge?
I don't want to cheat by drawing a grey circle in the middle because the dialog is slightly transparent. (This transparency isn't as subtle on on other backgrounds.)
As far as I know, you can't antialias clip regions.
I'd suggest using bitmap masking instead. Render the the pink, white, and light gray foreground to one bitmap, render the outer/inner circle mask (the grayscale alpha channel) to another bitmap, and then use Paint.setXfermode to render the foreground bitmap with the mask as its alpha channel.
An example can be found in the ApiDemos source code here.
I know this is not a general answer, but in this particular case, you could draw arcs with a thick stroke width, instead of the circles + mask.
I too had this same problem. I tried using Bitmap masking (xFermode) to fix the Aliasing, but it was heavy.
So for API < 19, I used the Bitmap masking way and for API >= 19, I used Path.Op. Instead of clipping the path and then drawing the shape. I did a REVERSE_DIFFERENCE of the path and the shape(which is of type Path). You can perform operations on Path from API 19 and above.
Works perfectly for me!
you can try the following code:
public class GrowthView extends View {
private static final String TAG = "GrowthView";
private int bgColor = Color.parseColor("#33485d");
private int valColor = Color.parseColor("#ecb732");
private int[] scores = new int[]{0, 10, 80, 180, 800, 5000, 20000, 50000, 100000};
private Context mContext;
private float w;
private float h;
private Paint bgPaint;
private Paint growthPaint;
private Paint textPaint;
private Paint clipPaint;
private Path bgPath;
private Path bgClipPath;
private Path growthPath;
private int growthValue = 0;
private float bgFullAngle = 240.0f;
private float gapAngle = bgFullAngle / (scores.length - 1);
private float gapRadius = 21.5f;//实际为21px 略大半个像素避免path无法缝合error
private float outerRadius = 240.0f;
private float innerRadius = outerRadius - gapRadius * 2;
private RectF outerRecF;
private RectF innerRecF;
private RectF leftBoundRecF;
private RectF rightBoundRecF;
public GrowthView(Context context) {
this(context, null);
}
public GrowthView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GrowthView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mContext = context;
init();
}
private void init() {
Xfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.DARKEN);
bgPaint = new Paint();
bgPaint.setStyle(Paint.Style.FILL);
bgPaint.setColor(bgColor);
bgPaint.setStrokeWidth(0.1f);
bgPaint.setAntiAlias(true);
growthPaint = new Paint();
growthPaint.setStyle(Paint.Style.FILL_AND_STROKE);
growthPaint.setColor(valColor);
growthPaint.setStrokeWidth(1f);
growthPaint.setAntiAlias(true);
clipPaint = new Paint();
clipPaint.setStyle(Paint.Style.FILL);
clipPaint.setColor(Color.WHITE);
clipPaint.setStrokeWidth(.1f);
clipPaint.setAntiAlias(true);
clipPaint.setXfermode(xFermode);
textPaint = new Paint();
textPaint.setTextSize(96);//todo comfirm the textSize
textPaint.setStrokeWidth(1f);
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setColor(valColor);
bgPath = new Path();
growthPath = new Path();
//todo 暂定中心点为屏幕中心
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
w = metrics.widthPixels;
h = metrics.heightPixels;
outerRecF = new RectF(w / 2 - outerRadius, h / 2 - outerRadius, w / 2 + outerRadius, h / 2 + outerRadius);
innerRecF = new RectF(w / 2 - innerRadius, h / 2 - innerRadius, w / 2 + innerRadius, h / 2 + innerRadius);
rightBoundRecF = new RectF(w / 2 + (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 - gapRadius,
h / 2 + (innerRadius + gapRadius) / 2 - gapRadius,
w / 2 + (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 + gapRadius,
h / 2 + (innerRadius + gapRadius) / 2 + gapRadius);
leftBoundRecF = new RectF(w / 2 - (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 - gapRadius,
h / 2 + (innerRadius + gapRadius) / 2 - gapRadius,
w / 2 - (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 + gapRadius,
h / 2 + (innerRadius + gapRadius) / 2 + gapRadius);
bgClipPath = new Path();
bgClipPath.arcTo(innerRecF, 150.0f, 359.9f, true);
bgClipPath.close();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//bg
float startAngle = 150.0f;
float endRecfFullAngle = 180.0f;
bgPath.arcTo(outerRecF, startAngle, bgFullAngle, true);
bgPath.arcTo(rightBoundRecF, 30.0f, endRecfFullAngle, true);
bgPath.arcTo(innerRecF, startAngle, bgFullAngle);
bgPath.arcTo(leftBoundRecF, -30.0f, endRecfFullAngle);
bgPath.rMoveTo(w / 2 - outerRadius * (float) Math.pow(3, 0.5) / 2, h / 2 + outerRadius / 2);
bgPath.setFillType(Path.FillType.WINDING);
bgPath.close();
//growth
if (getGrowthVal() != 0) {
float temp = getGrowthAngle(getGrowthVal());
growthPath.arcTo(outerRecF, startAngle, temp, true);
growthPath.arcTo(getDynamicRecF(getGrowthVal()), getDynamicOriginAngle(getGrowthVal()), endRecfFullAngle, true);
growthPath.arcTo(innerRecF, startAngle, temp);
growthPath.arcTo(leftBoundRecF, -30.0f, endRecfFullAngle);
growthPath.rMoveTo(w / 2 - outerRadius * (float) Math.pow(3, 0.5) / 2, h / 2 + outerRadius / 2);
growthPath.close();
}
canvas.drawText(formatVal(getGrowthVal()), w / 2, h / 2, textPaint);
canvas.clipPath(bgClipPath, Region.Op.DIFFERENCE);
canvas.drawPath(bgPath, bgPaint);
canvas.drawPath(growthPath, growthPaint);
canvas.drawPath(bgClipPath, clipPaint);
}
private float getDynamicOriginAngle(int growthVal) {
return growthVal <= 30 ? getGrowthAngle(growthVal) + 150 :
getGrowthAngle(growthVal) - 210;
}
private RectF getDynamicRecF(int growthVal) {
float dynamicAngle = getGrowthAngle(growthVal);
//动态圆心
float _w = w / 2 + (float) Math.sin(Math.toRadians(dynamicAngle - 120)) * (outerRadius - gapRadius);
float _y = h / 2 - (float) Math.sin(Math.toRadians(dynamicAngle - 30)) * (outerRadius - gapRadius);
return new RectF(_w - gapRadius, _y - gapRadius, _w + gapRadius, _y + gapRadius);
}
private int getGrowthVal() {
return this.growthValue;
}
public void setGrowthValue(int value) {
if (value < 0 || value > 100000) {
try {
throw new Exception("成长值不在范围内");
} catch (Exception e) {
Log.e(TAG, e.getMessage());
e.printStackTrace();
}
}
this.growthValue = value;
invalidate();
}
private float getGrowthAngle(int growthVal) {
return gapAngle * (getLevel(growthVal) - 1)
+ gapAngle * (growthVal - scores[getLevel(growthVal) - 1]) /
(scores[getLevel(growthVal)] - scores[getLevel(growthVal) - 1]);
}
private int getLevel(int score) {
return score < 0 ? -1 : score <= 10 ? 1 : score <= 80 ? 2 : score <= 180 ? 3 : score <= 800 ?
4 : score <= 5000 ? 5 : score <= 20000 ? 6 : score <= 50000 ? 7 : 8;
}
private String formatVal(int value) {
StringBuilder builder = new StringBuilder(String.valueOf(value));
return value < 1000 ? builder.toString() : builder.insert(builder.length() - 3, ',').toString();
}
}
Use the Xfermode Api with canvas.clipPath() may resolve this problem...
Result

Categories

Resources