Android View Invalidate optimization idea - android

I'm trying to make an animation on my android application.
I have a list of View I want to draw every 10 ms to have a smooth animations.
class CircularAnimationSector extends View{
private double scale;
private double circularPosition;
private double circularLength;
private double sectionRadiusRatio;
private double circularSpeed;
private int color;
private Paint paint = new Paint();
private ICircular iCircular;
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
Path path = new Path();
int width = this.getWidth();
// int height = this.getHeight();
Point center = new Point((int)(width / 2.0f), (int)(width / 2.0f));
int innerWidthRadius = (int) (this.iCircular.GetViewSize() / 2.0f);
float startRadius = (float)(innerWidthRadius * this.iCircular.GetStartRatio());
float endRadius = (float)(startRadius + this.scale * (this.sectionRadiusRatio - this.iCircular.GetStartRatio()) * innerWidthRadius);
float startAngle = (float)(this.circularPosition - this.circularLength / 2.0f);
float endAngle = (float)(this.circularPosition + this.circularLength / 2.0f);
Point p1 = new Point((int) (center.x + startRadius * Math.cos(startAngle)), (int) (center.y + startRadius * Math.sin(startAngle)));
Point p3 = new Point((int) (center.x + endRadius * Math.cos(endAngle)), (int) (center.y + endRadius * Math.sin(endAngle)));
// PATH DRAWING
path.moveTo((float) (p1.x), (float) (p1.y));
path.arcTo(new RectF(center.x - startRadius, center.y - startRadius, center.x + startRadius, center.y + startRadius), (float) (startAngle * 180 / Math.PI), (float) ((endAngle - startAngle) * 180 / Math.PI));
path.lineTo((float) (p3.x), (float) (p3.y));
path.arcTo(new RectF(center.x - endRadius, center.y - endRadius, center.x + endRadius, center.y + endRadius), (float) (endAngle * 180 / Math.PI), (float) ((startAngle - endAngle) * 180 / Math.PI));
path.lineTo((float) (p1.x), (float) (p1.y));
canvas.drawPath(path, this.paint);
this.circularPosition += this.circularSpeed;
}
}
But it seems that the animation is not as smooth as expected and the application lag a little.
Do you have any advice to optimize my animation ?
I just want to draw a view every 10 ms (for instance) and then upadte its position and draw it again around a circle.

Related

Android How to draw triangles inside a hexagon?

First thing I have drawn the three lines of the hexagon width specific width then I have drawn the six points using this formula section = 2 * PI / NumberOfPoints. Here is the pic of lines I have drawn:
what I want is to draw a triangle between every two lines which will be 6 triangles. Here is the pic when I drew the triangles:
My problem is I want the triangle to be drawn between the lines, not above them which mean the triangle should not overlaps the line.
Here is how I drew the lines:
for (int i = 1; i <= 6; i++) {
float eX = (float) (x + radius * Math.cos(section * i));
float eY = (float) (y + radius * Math.sin(section * i));
linesPaint.setShader(new LinearGradient(x, y, eX, eY, Color.BLACK, Color.TRANSPARENT, Shader.TileMode.MIRROR));
canvas.drawLine(x, y, eX, eY, linesPaint);
}
and here is how I drew the triangles
for (int i = 1; i <= 6; i++) {
TriangleColor triangleColor = triangleColors.get(i - 1);
trianglesPaint.setShader(new LinearGradient(0, 0, 0, getHeight(), triangleColor.firstColor, triangleColor.secondColor, Shader.TileMode.REPEAT));
float x1 = (float) (x + radius * Math.cos(section * i));
float y1 = (float) (y + radius * Math.sin(section * i));
float x2 = (float) (x + radius * Math.cos(section * (i + 1)));
float y2 = (float) (y + radius * Math.sin(section * (i + 1)));
trianglesPath.moveTo(x, y);
trianglesPath.lineTo(x1, y1);
trianglesPath.lineTo(x2, y2);
trianglesPath.lineTo(x, y);
canvas.drawPath(trianglesPath, trianglesPaint);
trianglesPath.reset();
}
Can anyone help me with the formula? Thanks in advance.
I assume that you have lines with thickness 2d
To avoid overwriting these lines, you can shift the central point for every triangle by dstance d/sin(30) = 2*d in direction of bisector between two lines
Perhaps also you need to diminish radius (length of triangle side along the line)
newradius = radius - d*sqrt(3) - d * sqrt(3)/3
for (int i = 1; i <= 6; i++) {
cx = x + 2 * d * Math.cos(section * (i + 0.5));
cy = x + 2 * d * Math.sin(section * (i + 0.5));
float x1 = (float) (cx + newradius * Math.cos(section * i)); /
//and similar for other coordinates
trianglesPath.moveTo(cx, cy);
trianglesPath.lineTo(x1, y1);
...

Fill arrow head goes off in line in canvas Android

The arrow head with stroke working fine in canvas, but the filled arrow head is not drawing at the right position ,Here is my code for draw arrow.
private void drawArrow(Point startPoint, Point endPoint, Paint paint, Canvas mCanvas) {
Path mPath = new Path();
float deltaX = endPoint.x - startPoint.x;
float deltaY = endPoint.y - startPoint.y;
//float frac = (float) 0.1;
int ARROWHEAD_LENGTH = 15;
float sideZ = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
float frac = ARROWHEAD_LENGTH < sideZ ? ARROWHEAD_LENGTH / sideZ : 1.0f;
float point_x_1 = startPoint.x + (float) ((1 - frac) * deltaX + frac * deltaY);
float point_y_1 = startPoint.y + (float) ((1 - frac) * deltaY - frac * deltaX);
float point_x_2 = endPoint.x;
float point_y_2 = endPoint.y;
float point_x_3 = startPoint.x + (float) ((1 - frac) * deltaX - frac * deltaY);
float point_y_3 = startPoint.y + (float) ((1 - frac) * deltaY + frac * deltaX);
mPath.moveTo(point_x_1, point_y_1);
mPath.lineTo(point_x_2, point_y_2);
mPath.lineTo(point_x_3, point_y_3);
//mPath.lineTo(point_x_1, point_y_1);
//mPath.lineTo(point_x_1, point_y_1);
mCanvas.drawPath(mPath, paint);
invalidate();
}
to resolve this issue, I have placed the end line to the center of triangle.
You can do it with this formula :
float endLineX = (point_x_1 + point_x_2 + point_x_3) / 3;
float endLineY = (point_y_1 + point_y_2 + point_y_3) / 3;
Voilà

How can I make quarter indicators in Android Wear Watch Face?

It's no secret. My math skills aren't that impressive.
I'm developing my own Wear Watchface. But I want to draw a Drawline to the center of the screen for every 10 minute place on the watch. (See the 500px analog example).
By using the Google example code I can achieve this but only an entire line from the side of the screen to the center. But I want it to be like 5% (or a fixed amount of pixels) in length of that like in the 500px watchface. This is Google's code for drawing the second dial:
float centerX = width / 2f;
float centerY = height / 2f;
float secRot = mTime.second / 30f * (float) Math.PI;
float secLength = centerX - 20;
float secX = (float) Math.sin(secRot) * secLength;
float secY = (float) -Math.cos(secRot) * secLength;
canvas.drawLine(centerX, centerY, centerX + secX, centerY + secY, mHandPaint);
By changing the mTime.second I can draw from a specific second.
Found the solution, here's a snippet:
int teller_minuut_stip = 0; //Current minute
float secRot_stip = teller_minuut_stip / 30f * (float) Math.PI;
float secRot_stip_klein = teller_minuut_stip / 30f * (float) Math.PI;
float secLength_stip = centerX - 10;
float secX = (float) Math.sin(secRot_stip) * secLength_stip;
float secY = (float) -Math.cos(secRot_stip) * secLength_stip;
float secLength_stip_klein = centerX - 140;
float secX_klein = (float) Math.sin(secRot_stip_klein) * secLength_stip_klein;
float secY_klein = (float) -Math.cos(secRot_stip_klein) * secLength_stip_klein;
float eindpunt_x = centerX + secX;
float eindpunt_y = centerY + secY;
float lijn_x = eindpunt_x - secX_klein;
float lijn_y = eindpunt_y - secY_klein;
canvas.drawLine(eindpunt_x, eindpunt_y, lijn_x, lijn_y, datePaint_cirkel_kleurtien);

android 3d vertical carousel view

I am using http://www.codeproject.com/Articles/146145/Android-3D-Carousel code to create a vertical carousel view.i can see the vertical carousel using below changes in the code but center item is not properly placed in the screen and if the list items size increased, diameter moves upwards.
private void setUpChild(CarouselImageView child, int index, float angleOffset) {
// Ignore any layout parameters for child, use wrap content
addViewInLayout(child, -1 /*index*/, generateDefaultLayoutParams());
child.setSelected(index == mSelectedPosition);
int h;
int w;
if (mInLayout)
{
h = (getMeasuredHeight() - getPaddingBottom()-getPaddingTop())/3;
w = getMeasuredWidth() - getPaddingLeft() - getPaddingRight()/3;
}
else
{
h = (getMeasuredHeight() - getPaddingBottom()-getPaddingTop())/3;
w = getMeasuredWidth() - getPaddingLeft() - getPaddingRight()/3;
}
child.setCurrentAngle(angleOffset);
// modify the diameter.
Calculate3DPosition(child, w*(getAdapter().getCount()/4), angleOffset);
// Measure child
child.measure(w, h);
int childLeft;
// Position vertically based on gravity setting
int childTop = calculateTop(child, true);
childLeft = 0;
child.layout(childLeft, childTop, w, h);
}
change in calculate3position function as below
float x = (float) (-diameter/2 * Math.cos(angleOffset) * 0.00001);
float z = diameter/2 * (1.0f - (float)Math.cos(angleOffset));
float y = (float) (diameter/2 * Math.sin(angleOffset)) + diameter/2 - child.getWidth();
child.setX(x);
child.setZ(z);
child.setY(y);
I think that this calculation:
float x = (float) (-diameter/2 * Math.cos(angleOffset) * 0.00001);
float z = diameter/2 * (1.0f - (float)Math.cos(angleOffset));
float y = (float) (diameter/2 * Math.sin(angleOffset)) + diameter/2 - child.getWidth();
should be this:
float x = 0.0f
float z = diameter/2.0f * (1.0f - (float)Math.cos(angleOffset));
float y = (diameter/2.0f * Math.sin(angleOffset)) + diameter/2.0f - child.getHeight()/2.0f;
Your x position should always be zero, and your y position should be based on the sin, and should be offset by 1/2 of the height of the child instead of 1/2 of the width.
Hello try this code and replace with this code in your Calculate3DPosition method
angleOffset = angleOffset * (float) (Math.PI / 180.0f);
float y = (float) (((diameter * 60) / 100) * Math.sin(angleOffset)) + ((diameter * 50) / 100);
float z = diameter / 2 * (1.0f - (float) Math.cos(angleOffset));
float x = (float) (((diameter * 5) / 100) * Math.cos(angleOffset) * 0.3);
child.setItemX(x);
child.setItemZ((z * 30) / 100);
child.setItemY(-(y));
its solve my problem please try this one

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