Irregular shading in Graph - android

I tried to replicate a donut chart code that I found in the net. The code is as follows
public class DonutChart extends View{
private float radius;
SharedPreferences prefs;
Paint paint;
Paint shadowPaint;
int a,b,c;
Path myPath;
Path shadowPath;
RectF outterCircle;
RectF innerCircle;
RectF shadowRectF;
public DonutChart(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.DonutChart,
0, 0
);
try {
radius = a.getDimension(R.styleable.DonutChart_radius, 20.0f);
} finally {
a.recycle();
}
paint = new Paint();
paint.setDither(true);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setAntiAlias(true);
paint.setStrokeWidth(radius / 14.0f);
shadowPaint = new Paint();
shadowPaint.setColor(0xf0000000);
shadowPaint.setStyle(Paint.Style.STROKE);
shadowPaint.setAntiAlias(true);
shadowPaint.setStrokeWidth(6.0f);
shadowPaint.setMaskFilter(new BlurMaskFilter(4, BlurMaskFilter.Blur.SOLID));
myPath = new Path();
shadowPath = new Path();
outterCircle = new RectF();
innerCircle = new RectF();
shadowRectF = new RectF();
float adjust = (.019f*radius);
shadowRectF.set(adjust, adjust, radius*2-adjust, radius*2-adjust);
adjust = .038f * radius;
outterCircle.set(adjust, adjust, radius*2-adjust, radius*2-adjust);
adjust = .276f * radius;
innerCircle.set(adjust, adjust, radius * 2 - adjust, radius * 2 - adjust);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw shadow
paint.setShader(null);
float adjust = (.0095f*radius);
paint.setShadowLayer(8, adjust, -adjust, 0xaa000000);
drawDonut(canvas, paint, 0, 359.9f);
//Orange
setGradient(0xffEF6632,0xffEF6632);
drawDonut(canvas,paint, 0,b);
//Blue
setGradient(0xff00CCDA,0xff00CCDA);
drawDonut(canvas, paint, 60,a);
// blue
// setGradient(0xff4AB6C1,0xff2182AD);
// drawDonut(canvas, paint, 120, 60);
// Grey
setGradient(0xff557687,0xff557687);
drawDonut(canvas, paint, 180,c);
}
public void drawDonut(Canvas canvas, Paint paint, float start,float sweep){
myPath.reset();
myPath.arcTo(outterCircle, start, sweep, false);
myPath.arcTo(innerCircle, start+sweep, -sweep, false);
myPath.close();
canvas.drawPath(myPath, paint);
}
public void setGradient(int sColor, int eColor){
paint.setShader(new RadialGradient(radius, radius, radius - 5,
new int[]{sColor, eColor},
new float[]{.6f, .95f}, TileMode.CLAMP));
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int desiredWidth = (int) radius*2;
int desiredHeight = (int) radius*2;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//70dp exact
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
}else if (widthMode == MeasureSpec.AT_MOST) {
//wrap content
width = Math.min(desiredWidth, widthSize);
} else {
width = desiredWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(desiredHeight, heightSize);
} else {
height = desiredHeight;
}
//MUST CALL THIS
setMeasuredDimension(width, height);
}
public void getData(int x,int y){
invalidate();
a=((x*360)/10);
b=(y*360)/10;
c=((10-(x+y))*360)/10;
String s1,s2,s3;
s1=String.valueOf(a);
s2=String.valueOf(b);
s3=String.valueOf(c);
Toast.makeText(getContext(),"Inside Chart "+s1+" "+s2+" "+s3 +" "+String.valueOf(x),Toast.LENGTH_SHORT).show();
}
}
The problem is when I render the graph on my device it gives me a weird shadow like this:
or like this:
What is causing this and how to rectify it?

This is actually caused because only the value of the line length is changed whereas the starting point is the same so sometimes they tend to overlap.
This can be solved by changing the code as follows
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw shadow
paint.setShader(null);
float adjust = (.0095f*radius);
paint.setShadowLayer(8, adjust, -adjust, 0xaa000000);
drawDonut(canvas, paint, 0, 359.9f);
//Orange
setGradient(0xffEF6632,0xffEF6632);
drawDonut(canvas,paint, 0,b);
//Blue
setGradient(0xff00CCDA,0xff00CCDA);
drawDonut(canvas, paint, b,a);
// Grey
setGradient(0xff557687,0xff557687);
drawDonut(canvas, paint, a+b,c);
}

Related

Animated view is translated behind oval view

Here is the current behaviour.
I want the oval view to overlap the animation view. The 'shoulders' need to be hidden inside that oval view.
I'm already using FrameLayout and also tried to bringChildToFront, use translation, elevation but nothing seems to work.
If you need more details to have a bigger picture of the problem please let me know.
Layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parentLinearLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/id_face" />
<a.b.c.OvalAnimationView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Draw logic:
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != 0 && h != 0) {
if (horizontalMargin == 0) {
horizontalMargin = getMeasuredWidth() / WIDTH_FACTOR;
}
if (verticalMargin == 0) {
verticalMargin = 0.11f * getMeasuredHeight();
}
fullScreenRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
firstArc = new RectF(
horizontalMargin,
verticalMargin,
getMeasuredWidth() - horizontalMargin,
getMeasuredHeight() - verticalMargin);
secondArc = new RectF(
horizontalMargin,
verticalMargin,
getMeasuredWidth() - horizontalMargin,
getMeasuredHeight() - verticalMargin);
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(firstArc, 0, 360, true, borderPaint);
canvas.drawArc(secondArc,0, 360, true, eraser);
}
public void init(int borderColor) {
this.horizontalMargin = 0;
this.verticalMargin = 0;
borderPaint = new Paint();
borderPaint.setColor(borderColor);
borderPaint.setStrokeWidth(35);
borderPaint.setStyle(Paint.Style.STROKE);
eraser = new Paint();
eraser.setColor(Color.TRANSPARENT);
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
eraser.setAntiAlias(true);
}
Animation:
private void moveViewToScreenCenter( View view )
{
DisplayMetrics dm = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
int statusBarOffset = dm.heightPixels - lLayout.getMeasuredHeight();
int originalPos[] = new int[2];
view.getLocationOnScreen( originalPos );
int xDest = dm.widthPixels/2;
xDest -= (view.getMeasuredWidth()/2);
int yDest = dm.heightPixels/2 - (view.getMeasuredHeight()/2) - statusBarOffset;
AnimationSet animSet = new AnimationSet(true);
animSet.setFillAfter(true);
animSet.setDuration(2500);
animSet.setInterpolator(new AccelerateInterpolator());
TranslateAnimation translateAnimation = new TranslateAnimation( 0, xDest - originalPos[0] , 0, yDest - originalPos[1]);
animSet.addAnimation(translateAnimation);
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.70f, 1.0f, 0.70f, ScaleAnimation.RELATIVE_TO_PARENT, .5f, ScaleAnimation.RELATIVE_TO_PARENT, .4f);
animSet.addAnimation(scaleAnimation);
animSet.setZAdjustment(-1);
new Handler().postDelayed(new Runnable() {
public void run() {
iFaceListener.onFaceAnimationFinished();
}
}, animSet.getDuration());
view.startAnimation(animSet);
}
Fixed it by myself.
Code for posteriority:
public class OvalAnimationView extends View {
private Paint transparentPaint = new Paint();
private Paint tPaint = new Paint();
private Paint eraser = new Paint();
private Paint borderPaint = new Paint();
private float horizontalMargin;
private float verticalMargin;
private Canvas temp;
private float WIDTH_FACTOR = 15f;
private Rect fullScreenRect;
private RectF firstArc, secondArc;
private Bitmap bitmap;
public OvalAnimationView(Context context) {
super(context);
}
public OvalAnimationView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public OvalAnimationView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != 0 && h != 0) {
if (horizontalMargin == 0) {
horizontalMargin = getMeasuredWidth() / WIDTH_FACTOR;
}
if (verticalMargin == 0) {
verticalMargin = 0.11f * getMeasuredHeight();
}
fullScreenRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
firstArc = new RectF(
horizontalMargin,
verticalMargin,
getMeasuredWidth() - horizontalMargin,
getMeasuredHeight() - verticalMargin);
secondArc = new RectF(
horizontalMargin,
verticalMargin,
getMeasuredWidth() - horizontalMargin,
getMeasuredHeight() - verticalMargin);
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
public void init(int borderColor) {
this.horizontalMargin = 0;
this.verticalMargin = 0;
temp = new Canvas();
transparentPaint = new Paint();
transparentPaint.setColor(Color.BLACK);
transparentPaint.setStyle(Paint.Style.FILL);
tPaint = new Paint();
tPaint.setColor(Color.TRANSPARENT);
tPaint.setStyle(Paint.Style.FILL);
borderPaint = new Paint();
borderPaint.setColor(borderColor);
borderPaint.setStrokeWidth(35);
borderPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (bitmap == null) {
createWindowFrame();
}
canvas.drawBitmap(bitmap, 0, 0, null);
}
protected void createWindowFrame() {
bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas osCanvas = new Canvas(bitmap);
RectF outerRectangle = new RectF(0, 0, getWidth(), getHeight());
osCanvas.drawRect(fullScreenRect, transparentPaint);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.TRANSPARENT);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
float centerX = getWidth() / 2;
float centerY = getHeight() / 2;
float radius = 50;
osCanvas.drawCircle(centerX, centerY, radius, paint);
osCanvas.drawArc(firstArc, 0, 360, true, paint);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
bitmap = null;
}
}

Part of custom view not rendering at runtime?

I've written a custom view that generate a Google Map Marker at runtime, code below:
public class CustomMapMarker extends View {
Paint paint;
Path bodyPath;
Path dotPath;
private Paint paint2;
private int backGroundColor;
private int foreGroundColor;
Matrix scaleMatrix;
public CustomMapMarker(Context context)
{
super(context);
init(context);
}
public CustomMapMarker(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomMapMarker);
try {
backGroundColor = a.getColor(R.styleable.CustomMapMarker_markerBackgroundColor, Color.parseColor("#4180e0"));
foreGroundColor = a.getColor(R.styleable.CustomMapMarker_dotBackgroundColor, Color.WHITE);
}
finally {
a.recycle();
}
init(context);
}
public CustomMapMarker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
scaleMatrix = new Matrix();
bodyPath = new Path();
dotPath = new Path();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(backGroundColor);
paint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
paint2.setStyle(Paint.Style.FILL);
paint2.setColor(foreGroundColor);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public void onDraw(Canvas canvas)
{
bodyPath.setFillType(Path.FillType.EVEN_ODD);
bodyPath.moveTo(132, 416);
bodyPath.cubicTo(123, 370, 107, 332, 87, 297);
bodyPath.cubicTo(72, 270, 55, 246, 39, 221);
bodyPath.cubicTo(33, 212, 29, 203, 24, 194);
bodyPath.cubicTo(14,177,5,156, 6, 130);
bodyPath.cubicTo(6,104,14,83,25,66);
bodyPath.cubicTo(42,38,72,15,112,9);
bodyPath.cubicTo(145, 4, 176,12,197,25);
bodyPath.cubicTo(215, 36, 229,49,239,66);
bodyPath.cubicTo(250, 83, 258, 103,258,129);
bodyPath.cubicTo(259, 143, 256, 155, 253, 166);
bodyPath.cubicTo(250, 176, 245, 185, 241, 194);
bodyPath.cubicTo(232, 212, 221, 229, 210, 246);
bodyPath.cubicTo(177, 296, 146, 347, 132, 416);
bodyPath.close();
dotPath.setFillType(Path.FillType.EVEN_ODD);
dotPath.arcTo(82, 85, 182, 185, 270, 360, true);
dotPath.close();
bodyPath.addPath(dotPath);
RectF drawableRect = new RectF(0, 0, 265, 412);
RectF viewRect = new RectF(0, 0, getWidth(), getHeight());
scaleMatrix.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.CENTER);
bodyPath.transform(scaleMatrix);
dotPath.transform(scaleMatrix);
canvas.drawPath(bodyPath, paint);
canvas.drawPath(dotPath, paint2);
}
public void setBackgroundColor(int mColor) {
this.backGroundColor = mColor;
this.invalidate();
}
public void setForeGroundColor(int mColor) {
this.foreGroundColor = mColor;
this.invalidate();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = 255;
int desiredHeight = 412;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(desiredWidth, widthSize);
} else {
width = desiredWidth;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(desiredHeight, heightSize);
} else {
height = desiredHeight;
}
setMeasuredDimension(width, height);
}
}
Which in the XML preview, displays as intended:
However, at runtime, embedding this marker somewhere in the app to test with, the white dot is not visible?
Would anyone with more experience with custom views be able to shed some light on this? I presume it's something to do with the way i'm scaling the path to make it fit any size?
I finally found some time to look at your problem.
1) Use addArc() instead of arcTo(). If all you want is a circle, don't use a path, use canvas.drawCircle(). I'd do that.
2) Do not create objects in onDraw(). Use onSizeChanged() to create your pathes, that's the time when you know how big your view is. You can then calculate your path depending on width and height of your view. Thus you don't need to scale anything. In onDraw() you just draw.
#Override
public void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
bodyPath = getBodyPath(width, height);
dotPath = getDotPath(width, height);
}
#Override
public void onDraw(Canvas canvas)
{
canvas.save();
if(bodyPath != null) {
canvas.drawPath(bodyPath, backgroundPaint);
}
if(dotPath != null) {
canvas.drawPath(dotPath, foregroundPaint);
}
//or just a circle
//canvas.drawCircle(getWidth()/2, getHeight()/3,Math.min(getWidth(),getHeight())/5,foregroundPaint);
canvas.restore();
}
getBodyPath() e.g. looks like this
private Path getOutlinePath(float w, float h) {
Path bodyPath = new Path();
bodyPath.setFillType(Path.FillType.EVEN_ODD);
bodyPath.moveTo(w* 0.5f, h);
bodyPath.cubicTo(w * 0.4f, h * 0.8f, w * 0.3f, h * 0.7f, w * 0.25f, h * 0.6f);
bodyPath.cubicTo(w * 0.2f, h * 0.5f, w * 0.1f, h * 0.5f, 0, h * 0.3f);
bodyPath.cubicTo(0, h * 0.25f, w * 0.25f, 0, w * 0.5f, 0);
bodyPath.cubicTo(w * 0.75f, 0, w, h * 0.25f, w, h * 0.5f);
bodyPath.close();
return bodyPath;
}
Yes it is an ugly path, but you can see how I use the width and height of the view to calculate the control points. That way you don't need your scale matrix and you don't need onMeasure(). The path will fit any size.

Android canvas: lines and ovals scale, but paths don't?

I'm using a square shaped canvas in my application and my goal is to draw everything between 0.0/0.0 (top left corner) and 1.0/1.0 (bottom right corner) so that I can scale it up later on, depending on the screen size.
This works fine for some of the basic drawing methods, but not for paths. My onDraw-method:
#Override
protected void onDraw(Canvas canvas) {
canvas.scale(getWidth(), getWidth());
canvas.drawOval(outerRect, linePaint);
canvas.drawLine(0.5f, 0.05f, 0.5f, 0.95f, linePaint);
canvas.drawPath(trianglePath, linePaint);
}
Definitions of outerRect and trianglePath:
outerRect = new RectF(0.05f, 0.05f, 0.95f, 0.95f);
trianglePath = new Path();
trianglePath.moveTo(0.05f, 0.05f);
trianglePath.lineTo(0.95f, 0.05f);
trianglePath.lineTo(0.5f, 0.95f);
trianglePath.close();
As you can see, there's no triangle in the result:
Why is that?
edit: the whole view
public class PlaygroundView extends View {
private Paint linePaint;
private RectF outerRect;
private Path trianglePath;
private final int preferredSize = 300;
public PlaygroundView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
outerRect = new RectF(0.05f, 0.05f, 0.95f, 0.95f);
trianglePath = new Path();
trianglePath.reset();
trianglePath.moveTo(0.05f, 0.05f);
trianglePath.lineTo(0.95f, 0.05f);
trianglePath.lineTo(0.5f, 0.95f);
trianglePath.close();
linePaint = new Paint();
linePaint.setColor(Color.WHITE);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setStrokeWidth(0.01f);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
setMeasuredDimension(chosenDimension, chosenDimension);
}
private int chooseDimension(int mode, int size) {
if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
return size;
} else { // (mode == MeasureSpec.UNSPECIFIED)
return preferredSize;
}
}
#Override
protected void onDraw(Canvas canvas) {
canvas.scale(getWidth(), getWidth());
canvas.drawOval(outerRect, linePaint);
canvas.drawLine(0.5f, 0.05f, 0.5f, 0.95f, linePaint);
canvas.drawPath(trianglePath, linePaint);
}
}
edit #2:
Well, this is strange. A newly created emulator instance shows the circle and the line, but not the triangle, whereas the IDE preview shows the triangle, but not the circle and the line. Both based on the same code which I rebuilt several times...

how can i show a square image in a circle?

I'm trying to mimic something from the iPhone version of my app. I have a square image and I want to show it in a circle with a white border around it. like this
Is there a way I can do this?
You can achieve this effect, or something very close to it, using a custom Drawable class, containing a Paint object with a BitmapShader that renders the image as a texture. This is the code I'm using (slightly adapted from Romain's Guy post, which uses the same technique to draw images with rounded corners).
class CircularDrawable extends Drawable
{
private float mCircleRadius;
private final RectF mBackgroundRect = new RectF();
private final Paint mBackgroundPaint;
private final BitmapShader mBitmapShader;
private final Paint mPaint;
private final int mMargin;
CircularDrawable(Bitmap bitmap, int margin, int backgroundColor)
{
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(mBitmapShader);
mMargin = margin;
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(backgroundColor);
}
#Override
protected void onBoundsChange(Rect bounds)
{
super.onBoundsChange(bounds);
mBackgroundRect.set(bounds);
mCircleRadius = Math.min(bounds.width() / 2 - mMargin, bounds.height() / 2 - mMargin);
}
#Override
public void draw(Canvas canvas)
{
canvas.drawRect(mBackgroundRect, mBackgroundPaint);
canvas.drawCircle(mBackgroundRect.width() / 2, mBackgroundRect.height() / 2, mCircleRadius, mPaint);
}
#Override
public int getOpacity()
{
return PixelFormat.TRANSLUCENT;
}
#Override
public void setAlpha(int alpha)
{
mPaint.setAlpha(alpha);
mBackgroundPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter cf)
{
mPaint.setColorFilter(cf);
mBackgroundPaint.setColorFilter(cf);
}
}
Having the bitmap you want to draw, just build a CircularDrawable from it with
new CircularDrawable(bitmap, margin, Color.WHITE);
Try this.
public class CircularImageView extends ImageView {
private int borderWidth;
private int viewWidth;
private int viewHeight;
private Bitmap image;
private Paint paint;
private Paint paintBorder;
private BitmapShader shader;
public CircularImageView(final Context context) {
this(context, null);
}
public CircularImageView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.circularImageViewStyle);
}
public CircularImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// init paint
paint = new Paint();
paint.setAntiAlias(true);
paintBorder = new Paint();
paintBorder.setAntiAlias(true);
// load the styled attributes and set their properties
TypedArray attributes = context.obtainStyledAttributes(attrs,
R.styleable.CircularImageView, defStyle, 0);
if (attributes.getBoolean(R.styleable.CircularImageView_border, true)) {
setBorderWidth(attributes.getColor(
R.styleable.CircularImageView_border_width, 4));
setBorderColor(attributes.getInt(
R.styleable.CircularImageView_border_color, Color.WHITE));
}
if (attributes.getBoolean(R.styleable.CircularImageView_shadow, false))
addShadow();
}
public void setBorderWidth(int borderWidth) {
this.borderWidth = borderWidth;
this.invalidate();
}
public void setBorderColor(int borderColor) {
if (paintBorder != null)
paintBorder.setColor(borderColor);
this.invalidate();
}
public void addShadow() {
setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.BLACK);
}
#SuppressLint("DrawAllocation")
#Override
public void onDraw(Canvas canvas) {
// load the bitmap
BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
if (bitmapDrawable != null)
image = bitmapDrawable.getBitmap();
// init shader
if (image != null) {
shader = new BitmapShader(Bitmap.createScaledBitmap(image,
canvas.getWidth(), canvas.getHeight(), false),
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
int circleCenter = viewWidth / 2;
// circleCenter is the x or y of the view's center
// radius is the radius in pixels of the cirle to be drawn
// paint contains the shader that will texture the shape
canvas.drawCircle(circleCenter + borderWidth, circleCenter
+ borderWidth, circleCenter + borderWidth - 4.0f,
paintBorder);
canvas.drawCircle(circleCenter + borderWidth, circleCenter
+ borderWidth, circleCenter - 4.0f, paint);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec, widthMeasureSpec);
viewWidth = width - (borderWidth * 2);
viewHeight = height - (borderWidth * 2);
setMeasuredDimension(width, height);
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = viewWidth;
}
return result;
}
private int measureHeight(int measureSpecHeight, int measureSpecWidth) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpecHeight);
int specSize = MeasureSpec.getSize(measureSpecHeight);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = viewHeight;
}
return (result + 2);
}
}
I'd make a custom view and just draw what you want to the canvas- draw the border, then the white circle, then the image.It's a couple of easy canvas calls. If you need to clip the image to a circular area, just set a clipping Region before doing the image draw.

Get the coordinates of the center of a view

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;
}
}

Categories

Resources