I'm trying to figure out how to fill an View clockwise.
I figured out how to rotate an image but not sure how to rotate and fill.
Used this class to create a circle:
public class Circle extends View {
private static final int START_ANGLE_POINT = 270;
private final Paint paint;
private RectF rect;
private float angle;
public Circle(Context context, AttributeSet attrs) {
super(context, attrs);
final int strokeWidth = 60;
Point[] points = new Point[3];
points[0] = new Point(7, 13);
points[1] = new Point(13, 19);
points[2] = new Point(21, 9);
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
//Circle color
paint.setColor(Theme.darkRedColour());
//Initial Angle (optional, it can be zero)
angle = 0;
}
#Override
protected void onDraw(Canvas canvas) {
if (rect == null) {
DisplayMetrics metrics = App.getAppContext().getResources().getDisplayMetrics();
int densityDpi = (int)metrics.density;
densityDpi = 3;
int canvasW = getWidth();
int canvasH = getHeight();
Point centerOfCanvas = new Point(canvasW / 2, canvasH / 2);
int rectW = 100; // * (densityDpi - 1);
int rectH = 100; // * (densityDpi - 1);
int left = centerOfCanvas.x - (rectW / 2);
int top = centerOfCanvas.y - (rectH / 2);
int right = centerOfCanvas.x + (rectW / 2);
int bottom = centerOfCanvas.y + (rectH / 2);
rect = new RectF(left, top, right, bottom);
}
super.onDraw(canvas);
canvas.drawArc(rect, START_ANGLE_POINT, angle, true, paint);
}
public float getAngle() {
return angle;
}
public void setAngle(float angle) {
this.angle = angle;
}
}
Then used an AnimiationListener and updated the angle.
Related
I have a custom view(Circle) which is partly filled with arch(red color). Here is the picture https://gyazo.com/72e19cb97fd9f2adac2259c3855cf136.
I want to divide my custom view into sections, and when the button is clicked I draw an arch. 1 click 1/5 is covered with arch, 2nd click 2/5, etc...till 5.
How do I fill my view with red Arch when i press Increment button?(I don't understand the drawing part)
Here is what I have already tried - My CustomView class:
public class MySimpleView extends View {
private static final int PAINT_FLAGS = Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG;
private static final int STROKE_WIDTH = 40;
private static final int SECTIONS = 5;
private Paint basePaint, degreesPaint, centerPaint, rectPaint;
private RectF rect;
private int centerX, centerY, radius;
private int fullArchSliceLength;
private int colorArchLineLength;
public MySimpleView(Context context) {
super(context);
init();
}
public MySimpleView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MySimpleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
rectPaint = new Paint(PAINT_FLAGS);
rectPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
rectPaint.setStyle(Paint.Style.FILL);
centerPaint = new Paint(PAINT_FLAGS);
centerPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
centerPaint.setStyle(Paint.Style.FILL);
basePaint = new Paint(PAINT_FLAGS);
basePaint.setStyle(Paint.Style.STROKE);
basePaint.setStrokeWidth(STROKE_WIDTH);
basePaint.setColor(ContextCompat.getColor(getContext(), R.color.darkGrey));
degreesPaint = new Paint(PAINT_FLAGS);
degreesPaint.setStyle(Paint.Style.STROKE);
degreesPaint.setStrokeCap(Paint.Cap.ROUND);
degreesPaint.setStrokeJoin(Paint.Join.ROUND);
degreesPaint.setStrokeWidth(STROKE_WIDTH);
degreesPaint.setColor(Color.RED);
fullArchSliceLength = 360 / SECTIONS;
colorArchLineLength = fullArchSliceLength - 2;
}
public void swapColor() {
degreesPaint.setColor(degreesPaint.getColor() == Color.RED ? Color.GREEN :
Color.RED);
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (rect == null) {
centerX = getMeasuredWidth() / 2;
centerY = getMeasuredHeight() / 2;
radius = Math.min(centerX, centerY);
int startTop = STROKE_WIDTH / 2;
int startLeft = STROKE_WIDTH / 2;
int endBottom = 2 * radius - startTop;
int endRight = 2 * radius - startTop;
rect = new RectF(startTop, startLeft, endRight, endBottom);
}
canvas.drawRect(rect, rectPaint);
canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH / 2, basePaint);
// TODO: 2019-04-26 LOOK HERE
for (int i = 3; i < SECTIONS; i++) {
canvas.drawArc(rect, i * fullArchSliceLength,colorArchLineLength,
false, degreesPaint);
}
// TODO: 2019-04-26 LOOK HERE
// canvas.drawArc(rect, 0F, 90F, false, degreesPaint);
canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH, centerPaint);
}
}
public class MySimpleView extends View {
private static final int PAINT_FLAGS = Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG;
private static final int STROKE_WIDTH = 40;
private static final int SECTIONS = 5;
private Paint basePaint, degreesPaint, centerPaint, rectPaint;
private RectF rect;
private int centerX, centerY, radius;
private int fullArchSliceLength;
private int colorArchLineLength;
private int currentSections = 1;
public MySimpleView(Context context) {
super(context);
init();
}
public MySimpleView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MySimpleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
rectPaint = new Paint(PAINT_FLAGS);
rectPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
rectPaint.setStyle(Paint.Style.FILL);
centerPaint = new Paint(PAINT_FLAGS);
centerPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
centerPaint.setStyle(Paint.Style.FILL);
basePaint = new Paint(PAINT_FLAGS);
basePaint.setStyle(Paint.Style.STROKE);
basePaint.setStrokeWidth(STROKE_WIDTH);
basePaint.setColor(ContextCompat.getColor(getContext(), android.R.color.darker_gray));
degreesPaint = new Paint(PAINT_FLAGS);
degreesPaint.setStyle(Paint.Style.STROKE);
degreesPaint.setStrokeCap(Paint.Cap.ROUND);
degreesPaint.setStrokeJoin(Paint.Join.ROUND);
degreesPaint.setStrokeWidth(STROKE_WIDTH);
degreesPaint.setColor(Color.RED);
fullArchSliceLength = 360 / SECTIONS;
colorArchLineLength = fullArchSliceLength - 2;
}
//just a simple increment function
public void increment() {
if (currentSections < SECTIONS) {
currentSections++;
postInvalidate();
}
}
public void swapColor() {
degreesPaint.setColor(degreesPaint.getColor() == Color.RED ? Color.GREEN :
Color.RED);
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (rect == null) {
centerX = getMeasuredWidth() / 2;
centerY = getMeasuredHeight() / 2;
radius = Math.min(centerX, centerY);
int startTop = STROKE_WIDTH / 2;
int startLeft = STROKE_WIDTH / 2;
int endBottom = 2 * radius - startTop;
int endRight = 2 * radius - startTop;
rect = new RectF(startTop, startLeft, endRight, endBottom);
}
canvas.drawRect(rect, rectPaint);
canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH / 2, basePaint);
/*
startAngle is set to 270 so it will start at the top.
0 is right
90 bottom
180 left
270 top
*/
canvas.drawArc(rect, 270, currentSections * colorArchLineLength, false, degreesPaint);
canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH, centerPaint);
}
}
Basically your logic was a bit wrong. When calling drawArc the first parameter will be the startAngle of your line (meaning does the line start on top, left, right, bottom of the circle). I have written in comments what each degree corresponds to. The sweepAngle is how many degrees you are drawing (which you had already calculated correctly). Hope it works as you would expect it!
I am using magnifier view for my application which is a library I got from here :
https://github.com/nomanr/android-image-magnifier
I have modified this class to extend FrameLayout (It was ImageView before) to work on my FrameLayout.
It's working well except the painted canvas view stays back of all the views which are added in that custom view.
How to bring that canvas on front of those added views?
Custom view class that i am using :
public class ImageMagnifier extends FrameLayout {
private PointF zoomPos;
private boolean zooming = false;
private Matrix matrix;
private Paint paint;
private Bitmap bitmap;
private BitmapShader shader;
private int sizeOfMagnifier = 300;
int cheight, cwidth;
Context con;
C c = C.getInstance();
public ImageMagnifier(Context context) {
super(context);
init();
con=context;
}
public ImageMagnifier(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
con=context;
}
public ImageMagnifier(Context context, AttributeSet attrs) {
super(context, attrs);
init();
con=context;
}
private void init() {
zoomPos = new PointF(0, 0);
matrix = new Matrix();
paint = new Paint();
cwidth = c.Width * 109 / 1280;
cheight = cwidth * 134 / 109;
}
public void otherTouch(int x, int y) {
if (x > c.getwidth1(28) && x < c.getwidth1(921) && y > c.getheight1(135) && y < c.getheight1(560)) {
zoomPos.x = x - 10;
zoomPos.y = y - 75;
zooming = true;
this.invalidate();
} else {
RemoveMagnifire();
}
}
public void RemoveMagnifire() {
zooming = false;
this.invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!zooming) {
buildDrawingCache();
} else {
bitmap = getDrawingCache();
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
paint.setShader(shader);
matrix.reset();
matrix.postScale(2f, 2f, zoomPos.x-10, zoomPos.y+60);
paint.getShader().setLocalMatrix(matrix);
int width = c.Width;
int height = c.Height;
float leftX = zoomPos.x - ((width * 100) / 1280);
float topY = zoomPos.y - ((height * 250) / 720);
float rightX = zoomPos.x + ((width * 100) / 1280);
float bottomY = zoomPos.y - ((height * 100) / 720);
canvas.drawRect(leftX , topY, rightX, bottomY, paint);
}
}
}
A ViewGroup draws its child Views in the dispatchDraw() method. We just need to move the magnifier drawing to after that happens.
The fix is simple. Move everything after the super call in onDraw() to after the super call in dispatchDraw().
...
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Removed
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (!zooming) {
buildDrawingCache();
}
else {
bitmap = getDrawingCache();
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
matrix.reset();
matrix.postScale(2f, 2f, zoomPos.x - 10, zoomPos.y + 60);
paint.getShader().setLocalMatrix(matrix);
int width = c.Width;
int height = c.Height;
float leftX = zoomPos.x - ((width * 100) / 1280);
float topY = zoomPos.y - ((height * 250) / 720);
float rightX = zoomPos.x + ((width * 100) / 1280);
float bottomY = zoomPos.y - ((height * 100) / 720);
canvas.drawRect(leftX , topY, rightX, bottomY, paint);
}
}
...
You can just remove the onDraw() override, if you no longer need it for anything else.
I am trying to implement Oval path animation, I want to show path animation using image, I tried https://github.com/matthewrkula/AnimatedPathView but it's not work for oval. I also tried below code for oval path but it is shows circle, Anyone have an idea? Thanks in advance!!!
MyAnimation.java
public class MyAnimation extends Animation {
private View view;
private float cx, cy; // center x,y position of circular path
private float prevX, prevY; // previous x,y position of image during animation
private float r; // radius of circle
private float prevDx, prevDy;
/**
* #param view - View that will be animated
* #param r - radius of circular path
*/
public MyAnimation(View view, float r){
this.view = view;
this.r = r;
}
#Override
public boolean willChangeBounds() {
return true;
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
// calculate position of image center
int cxImage = width / 2;
int cyImage = height / 1;
cx = view.getLeft() + cxImage;
cy = view.getTop() + cyImage;
// set previous position to center
prevX = cx;
prevY = cy;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if(interpolatedTime == 0){
t.getMatrix().setTranslate(prevDx, prevDy);
return;
}
float angleDeg = (interpolatedTime * 360f + 90) % 360;
float angleRad = (float) Math.toRadians(angleDeg);
// r = radius, cx and cy = center point, a = angle (radians)
float x = (float) (cx + r * Math.cos(angleRad));
float y = (float) (cy + r * Math.sin(angleRad));
float dx = prevX - x;
float dy = prevY - y;
prevX = x;
prevY = y;
prevDx = dx;
prevDy = dy;
t.getMatrix().setTranslate(dx, dy);
}
}
PathAnimation.java
image = (ImageView) findViewById(R.id.image);
image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Animation anim = new MyAnimation(image, 300);
anim.setDuration(1000);
image.startAnimation(anim);
}
});
I have found the solution after many tried using this custom class
AnimationView.java
public class AnimationView extends View {
Paint paint;
long animationDuration = 10000;
int framesPerSecond = 60;
Bitmap bm;
int bm_offsetX, bm_offsetY;
Path animPath;
PathMeasure pathMeasure;
float pathLength;
float step; //distance each step
float distance; //distance moved
float[] pos;
float[] tan;
Matrix matrix;
public AnimationView(Context context) {
super(context);
initMyView();
}
public AnimationView(Context context, AttributeSet attrs) {
super(context, attrs);
initMyView();
}
public AnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initMyView();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void initMyView(){
paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
bm_offsetX = bm.getWidth()/2;
bm_offsetY = bm.getHeight()/2;
animPath = new Path();
animPath.moveTo(100, 100);
animPath.addArc(new RectF(1, 100, 300, 600), 1, 800);
animPath.close();
pathMeasure = new PathMeasure(animPath, false);
pathLength = pathMeasure.getLength();
Toast.makeText(getContext(), "pathLength: " + pathLength, Toast.LENGTH_LONG).show();
step = 1;
distance = 0;
pos = new float[2];
tan = new float[2];
matrix = new Matrix();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(animPath, paint);
if(distance < pathLength){
pathMeasure.getPosTan(distance, pos, tan);
matrix.reset();
float degrees = (float)(Math.atan2(tan[1], tan[0])*180.0/Math.PI);
matrix.postRotate(degrees, bm_offsetX, bm_offsetY);
matrix.postTranslate(pos[0]-bm_offsetX, pos[1]-bm_offsetY);
canvas.drawBitmap(bm, matrix, null);
distance += step;
}else{
distance = 0;
}
invalidate();
}
}
and put into xml
<com.example.android.mydemo.animation.pathanimation.AnimationView
android:layout_width="match_parent"
android:layout_height="450dp" />
I need to make something like this:
I'd try to draw this using Canvas.drawArc(...) but I failed.
Can anyone help me?
I created a view that can draw the shape that you want.
It starts by creating a path for one of the quarters, and rotating the canvas by 90 degrees to draw the path 4 times. The Paint used to draw the Path is determined by the progress / maxProgress.
I used a second path to denote the region of the canvas to clip out when drawing, so that there are empty spaces between the quarters.
Finally, the text can be drawn in the middle after restoring the canvas rotation and clipping.
public class CustomProgressView extends View {
private int progress;
private int maxProgress;
private float arcWidth;
private float arcPadding;
private Paint paintPositive;
private Paint paintNegative;
private Paint paintText;
private Path path;
private Path clipPath;
private ProgressListener listener;
public CustomProgressView(Context context) {
super(context);
init();
}
public CustomProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
arcWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25, getResources().getDisplayMetrics());
arcPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, getResources().getDisplayMetrics());
paintPositive = new Paint();
paintPositive.setColor(Color.RED);
paintPositive.setStyle(Paint.Style.FILL_AND_STROKE);
paintPositive.setAntiAlias(true);
paintNegative = new Paint();
paintNegative.setColor(Color.BLUE);
paintPositive.setStyle(Paint.Style.FILL_AND_STROKE);
paintNegative.setAntiAlias(true);
paintText = new Paint();
paintText.setColor(Color.BLACK);
paintText.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 24, getResources().getDisplayMetrics()));
progress = 0;
maxProgress = 10;
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
float diameter = Math.min(getWidth(), getHeight());
RectF ovalOuter = new RectF(0, 0, diameter, diameter);
RectF ovalInner = new RectF(ovalOuter.left + arcWidth, ovalOuter.top + arcWidth, ovalOuter.right - arcWidth, ovalOuter.bottom - arcWidth);
path = new Path();
path.moveTo(ovalOuter.centerX(), ovalOuter.top);
path.addArc(ovalOuter, 270, 90);
path.lineTo(ovalInner.right, ovalInner.centerY());
path.addArc(ovalInner, 0, -90);
path.lineTo(ovalOuter.centerX(), ovalOuter.top);
clipPath = new Path();
clipPath.addRect(ovalOuter.left, ovalOuter.centerY() - arcPadding / 2, ovalOuter.right, ovalOuter.centerY() + arcPadding / 2, Path.Direction.CW);
clipPath.addRect(ovalOuter.centerX() - arcPadding / 2, ovalOuter.top, ovalOuter.centerX() + arcPadding / 2, ovalOuter.bottom, Path.Direction.CW);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float perc = (float) progress / (float) maxProgress;
int state = 0;
if (perc < 0.25) {
state = 1;
} else if (perc < 0.5) {
state = 2;
} else if (perc < 0.75) {
state = 3;
} else {
state = 4;
}
RectF bounds = new RectF();
path.computeBounds(bounds, true);
// Draw Circle
canvas.save();
// Clip padding
canvas.clipPath(clipPath, Region.Op.DIFFERENCE);
canvas.drawPath(path, state == 1 ? paintPositive : paintNegative);
canvas.rotate(90, bounds.left, bounds.bottom);
canvas.drawPath(path, state == 2 ? paintPositive : paintNegative);
canvas.rotate(90, bounds.left, bounds.bottom);
canvas.drawPath(path, state == 3 ? paintPositive : paintNegative);
canvas.rotate(90, bounds.left, bounds.bottom);
canvas.drawPath(path, state == 4 ? paintPositive : paintNegative);
canvas.restore();
// Draw Progress
String text = String.valueOf(progress);
Rect textBounds = new Rect();
paintText.getTextBounds(text, 0, text.length(), textBounds);
float x = bounds.left - textBounds.width() / 2;
float y = bounds.bottom + textBounds.height() / 2;
canvas.drawText(text, x, y, paintText);
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
int oldProgress = this.progress;
this.progress = progress;
if (listener != null) {
listener.onProgressChanged(oldProgress, progress);
}
invalidate();
}
public int getMaxProgress() {
return maxProgress;
}
public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress;
invalidate();
}
public ProgressListener getListener() {
return listener;
}
public void setListener(ProgressListener listener) {
this.listener = listener;
}
public interface ProgressListener {
void onProgressChanged(int oldProgress, int newProgress);
}
}
I am trying to get the center coordinates of my custom view (a circle) so that I can draw a line from the those points. The custom view is inside a TableLayout which is itself inside a FrameLayout. However, I am having trouble - this code doesn't get it right. Any advice please?
The values given by this method are wrong on all devices. If I change the layout padding/margins etc. then the dots obviously move, but the point given by this method does not change.
public float[] getDotCenterLocationOnScreen() {
int[] location = new int[2];
getLocationOnScreen(location);
int xLoc = location[0];
int yLoc = location[1];
float xCenter = xLoc + (getWidth()/2);
float yCenter = yLoc - (getWidth()/2);
float[] dotCenterLocation = { xCenter, yCenter };
return dotCenterLocation;
}
Here is most of the code from my view class:
// Radius of Dot
int RADIUS;
private static final int START_RADIUS = 30;
// Value of which the Radius is multiplied by to get full width & height of
// the DotView
int sizeMultiplier = 4;
// Define other Objects
private Paint paint = new Paint();
float mTranslateX;
float mTranslateY;
public DotView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
paint.setStrokeWidth(6f);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setColor(Color.BLACK);
RADIUS = START_RADIUS;
}
public void setRadius(int radius) {
RADIUS = radius;
invalidate();
}
public int getRadius() {
return RADIUS;
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(mTranslateX, mTranslateY);
canvas.drawCircle(0, 0, RADIUS, paint);
canvas.restore();
}
public float[] getDotCenterLocationOnScreen() {
int[] location = new int[2];
getLocationOnScreen(location);
int xLoc = location[0];
int yLoc = location[1];
float xCenter = xLoc + (getWidth()/2);
float yCenter = yLoc - (getWidth()/2);
float[] dotCenterLocation = { xCenter, yCenter };
return dotCenterLocation;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int dia = START_RADIUS * sizeMultiplier; // Multiplying by 2 makes
// boundaries exactly the same size a dot.
int w = resolveSize(dia, widthMeasureSpec);
int h = resolveSize(dia, heightMeasureSpec);
setMeasuredDimension(w, h);
float radius = Math.min(w, h) / 2F;
mTranslateX = radius;
mTranslateY = radius;
}
}