I am inspired by the the new Material Design animations and I worked to create a similar drawable that is used in new support v7 Action Bar Drawer Toggle.
I created a CustomDrawable. All I actually did is that I created a Play triangle on canvas and pause logo on the left of the left margin of the visible canvas. I rotate the canvas according to the progress and restore it. Then I used Xfermode to crop the rotated result into a circle.
I cant find the solution to the problem.
The problem is that the xFermode is not applied to the 180 degree rotated result(after calling canvas.restore()).
Here is the code of Activity.
public class MainActivity extends Activity{
ImageView iv;
CustomDrawable drawable = new CustomDrawable();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
iv = (ImageView) findViewById(R.id.button);
iv.setLayerType(View.LAYER_TYPE_HARDWARE, null);
iv.setBackgroundDrawable(drawable);
iv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
float[] values = { 0, 1 };
if (drawable.getProgress() != 0) {
values[0] = drawable.getProgress();
values[1] = 0;
}
ObjectAnimator animator = ObjectAnimator.ofFloat(drawable,
"progress", values);
animator.setDuration(2000);
animator.start();
}
});
}
}
And the code for the CustomDrawable
public class CustomDrawable extends Drawable {
private float mProgress = 0;
private Paint mPaint = new Paint();
private Path mPath = new Path();
private final float rootTwo = (float) Math.sqrt(2);
private final float rootThree = (float) Math.sqrt(3);
private float radius = 0;
private float side = 0;
private Point[] triangle = new Point[3];
Paint xferpaint = new Paint();
Canvas cropper;
Bitmap bitmap;
Interpolator interpolator = new AnticipateOvershootInterpolator();
private float width;
Rect rec1, rec2;
public CustomDrawable() {
mPaint.setAntiAlias(true);
mPaint.setStyle(Style.FILL);
xferpaint.setColor(Color.RED);
xferpaint.setStyle(Style.FILL);
xferpaint.setAntiAlias(true);
}
#Override
public void draw(Canvas canvas) {
canvas.getClipBounds(bound);
boundsf.set(bound);
if (radius == 0) {
radius = Math.min(bound.centerX(), bound.centerY());
radius -= 5;
bitmap = Bitmap.createBitmap(bound.width(), bound.height(),
Config.ARGB_8888);
cropper = new Canvas(bitmap);
cropper.drawCircle(bound.centerX(), bound.centerY(), radius,
xferpaint);
xferpaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
side = rootTwo * radius;
triangle[0] = new Point(
(int) (bound.centerX() + (side / rootThree)),
bound.centerY());
triangle[1] = new Point(bound.centerX()
- (int) (side / (2 * rootThree)), bound.centerY()
- (int) (side / 2));
triangle[2] = new Point(bound.centerX()
- (int) (side / (2 * rootThree)), bound.centerY()
+ (int) (side / 2));
width = side / 4;
rec1 = new Rect((int) (-bound.centerX() - (3 * width / 2)),
(int) (bound.centerY() - (side / 2)),
(int) (-bound.centerX() - (width / 2)),
(int) (bound.centerY() + (side / 2)));
rec2 = new Rect((int) (-bound.centerX() + (width / 2)),
(int) (bound.centerY() - (side / 2)),
(int) (-bound.centerX() + (3 * width / 2)),
(int) (bound.centerY() + (side / 2)));
}
mPath.rewind();
mPath.moveTo(triangle[0].x, triangle[0].y);
mPath.lineTo(triangle[1].x, triangle[1].y);
mPath.lineTo(triangle[2].x, triangle[2].y);
mPath.close();
mPaint.setColor(Color.parseColor("#378585"));
canvas.drawPaint(mPaint);
mPaint.setColor(Color.parseColor("#FF0400"));
canvas.rotate(180 * interpolator.getInterpolation(mProgress), 0,
bound.centerY());
canvas.drawPath(mPath, mPaint);
canvas.drawRect(rec1, mPaint);
canvas.drawRect(rec2, mPaint);
canvas.restore();
canvas.drawBitmap(bitmap, 0, 0, xferpaint);
}
#Override
public int getOpacity() {
return mPaint.getAlpha();
}
#Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter filter) {
mPaint.setColorFilter(filter);
}
public float getProgress() {
return mProgress;
}
public void setProgress(float progress) {
mProgress = progress;
invalidateSelf();
}
}
this is your simplified Drawable:
class CustomDrawable extends Drawable implements ValueAnimator.AnimatorUpdateListener {
private float mProgress = 0;
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Path mPath;
private Path mClipPath;
public CustomDrawable() {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(0xffFF0400);
}
#Override
protected void onBoundsChange(Rect bounds) {
mClipPath = new Path();
int cx = bounds.centerX();
int cy = bounds.centerY();
int radius = Math.min(cx, cy) - 5;
mClipPath.addCircle(cx, cy, radius, Path.Direction.CCW);
final float rootTwo = (float) Math.sqrt(2);
final float rootThree = (float) Math.sqrt(3);
float side = rootTwo * radius;
mPath = new Path();
mPath.moveTo(cx + (side / rootThree), cy);
mPath.lineTo(cx - side / (2 * rootThree), cy - side / 2);
mPath.lineTo(cx - side / (2 * rootThree), cy + side / 2);
mPath.close();
float width = side / 4;
addRect(-cx - (3 * width / 2), cy - (side / 2), width, side);
addRect(-cx + (width / 2), cy - (side / 2), width, side);
}
private void addRect(float l, float t, float dx, float dy) {
mPath.addRect(l, t, l + dx, t + dy, Path.Direction.CCW);
}
#Override
public void draw(Canvas canvas) {
canvas.clipPath(mClipPath);
canvas.drawColor(0xff378585);
Rect bounds = getBounds();
canvas.rotate(mProgress, 0, bounds.centerY());
canvas.drawPath(mPath, mPaint);
}
public void switchIcons() {
float[] values = { 0, 180 };
if (mProgress != 0) {
values[0] = mProgress;
values[1] = 0;
}
ValueAnimator animator = ValueAnimator.ofFloat(values).setDuration(2000);
animator.setInterpolator(new AnticipateOvershootInterpolator());
animator.addUpdateListener(this);
animator.start();
}
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mProgress = (Float) animation.getAnimatedValue();
invalidateSelf();
}
#Override
public int getOpacity() {
return mPaint.getAlpha();
}
#Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter filter) {
mPaint.setColorFilter(filter);
}
}
EDIT: this is draw() method without Canvas.clipPath and without creating a "mask" Bitmap:
#Override
public void draw(Canvas canvas) {
canvas.drawColor(0xff378585);
canvas.save();
canvas.rotate(mProgress, 0, cy);
canvas.drawPath(mPath, mPaint);
canvas.restore();
canvas.saveLayer(null, mDstInPaint, 0);
canvas.drawCircle(cx, cy, radius, mPaint);
canvas.restore();
}
where: mDstInPaint is a Paint object with xfermode set in Drawable's constructor:
mDstInPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
Related
I am trying to make a circular seek bar with discrete values.
like this
I have already seen this question previously posted but didn't get any insight.I also have tried a number of circular seekbar examples from GitHub but most of them are with continuous values. can anyone help to build one from scratch or to make the available ones working for discrete values?
Thanks in advance.
You can create it with using paint : See my below attached code for the same.
public class ProgressCircle extends View {
private final RectF mOval = new RectF();
private float mSweepAngle = 0;
private int startAngle = 90;
private int angleGap = 4;
private Bitmap icon;
float mEndAngle = 1.0f;
Paint progressPaint = new Paint();
TextPaint textPaint = new TextPaint();
Paint incompletePaint = new Paint();
Paint percentagePaint = new Paint();
private float strokeWidth = 30.0f;
public ProgressCircle(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ProgressCircle,
0, 0);
int textColor;
float textSize;
int progressColor;
int incompleteColor;
try {
textColor = a.getColor(R.styleable.ProgressCircle_android_textColor, Color.BLUE);
textSize = a.getDimension(R.styleable.ProgressCircle_android_textSize, 100);
strokeWidth = a.getDimension(R.styleable.ProgressCircle_strokeWidth, 30.0f);
progressColor = a.getColor(R.styleable.ProgressCircle_progressColor, Color.RED);
incompleteColor = a.getColor(R.styleable.ProgressCircle_incompleteProgressColor, Color.GRAY);
} finally {
a.recycle();
}
progressPaint.setColor(progressColor);
progressPaint.setStrokeWidth(strokeWidth);
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(textColor);
textPaint.setTextSize(textSize);
Typeface tf = Typeface.create("Roboto Condensed Light", Typeface.BOLD);
textPaint.setTypeface(tf);
percentagePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
percentagePaint.setColor(textColor);
percentagePaint.setTextSize(textSize / 3);
incompletePaint.setColor(incompleteColor);
incompletePaint.setStrokeWidth(strokeWidth);
incompletePaint.setStyle(Paint.Style.STROKE);
incompletePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float currentAngleGap = mSweepAngle == 1.0f || mSweepAngle == 0 ? 0 : angleGap;
mOval.set(strokeWidth / 2, strokeWidth / 2, getWidth() - (strokeWidth / 2), getWidth() - (strokeWidth / 2));
canvas.drawArc(mOval, -startAngle + currentAngleGap, (mSweepAngle * 360) - currentAngleGap, false,
progressPaint);
mOval.set(strokeWidth / 2, strokeWidth / 2, getWidth() - (strokeWidth / 2), getWidth() - (strokeWidth / 2));
canvas.drawArc(mOval, mSweepAngle * 360- startAngle + currentAngleGap, 360 - (mSweepAngle * 360) - currentAngleGap, false,
incompletePaint);
drawText(canvas, textPaint, String.valueOf((int) (mSweepAngle * 100)), percentagePaint);
if(icon != null)
canvas.drawBitmap(icon, canvas.getWidth() / 2 - icon.getWidth() / 2, strokeWidth + (canvas.getHeight() / 15), null);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
private void drawText(Canvas canvas, Paint paint, String text, Paint percentagePaint) {
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
Rect percentageBounds = new Rect();
percentagePaint.getTextBounds("%", 0, 1, percentageBounds);
int x = (canvas.getWidth() / 2) - (bounds.width() / 2) - (percentageBounds.width() / 2);
int y = (canvas.getHeight() / 2) + (bounds.height() / 2);
canvas.drawText(text, x, y, paint);
canvas.drawText("%", x + bounds.width() + percentageBounds.width() / 2, y - bounds.height() + percentageBounds.height(), percentagePaint);
}
public void setTextColor(int color) {
textPaint.setColor(color);
}
public void setProgressColor(int color) {
progressPaint.setColor(color);
}
public void setIncompleteColor(int color) {
incompletePaint.setColor(color);
}
public void setProgress(float progress) {
if (progress > 1.0f || progress < 0) {
throw new RuntimeException("Value must be between 0 and 1: " + progress);
}
mEndAngle = progress;
this.invalidate();
}
public void startAnimation() {
ValueAnimator anim = ValueAnimator.ofFloat(mSweepAngle, mEndAngle);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
ProgressCircle.this.mSweepAngle = (Float) valueAnimator.getAnimatedValue();
ProgressCircle.this.invalidate();
}
});
anim.setDuration(10000);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
anim.start();
}
Output Screen :
See the Complete working example here
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 have already an 'ImageView' with these parameters:
android:layout_width="wrap_content"
android:layout_height="wrap_content"
and set custom Drawable :
public class HexDrawable extends Drawable {
private Path hexagonPath;
private float mWidth, mHeight;
private int mBackgroundColor;
private int mStrokeColor;
private int mStrokeWidth;
public HexDrawable(){
init();
}
public void setBackgroundColor(int color) {
mBackgroundColor = color;
}
public void setStrokeWidth(int width) {
mStrokeWidth = width;
}
public void setStrokeColor(int color) {
mStrokeColor = color;
}
#Override
public int getIntrinsicHeight() {
return 60;
}
#Override
public int getIntrinsicWidth() {
return 60;
}
private void init() {
hexagonPath = new Path();
mBackgroundColor = Color.BLUE;
mStrokeColor = Color.GREEN;
mStrokeWidth = 4;
}
private void calculatePath() {
float p = mStrokeWidth / 2;
float w = mWidth - 2 * p;
float h = mHeight - 2 * p;
float r = h / 2;
float a = (float) (r / Math.sqrt(3));
PointF X = new PointF(p + a + r / 2, p);
PointF Y = new PointF(p + a + r , p);
PointF A = new PointF(p + a, p + 0f);
PointF B = new PointF(p + 0f, p + r);
PointF C = new PointF(p + a, p + 2 * r);
PointF D = new PointF(p + w - a, p + 2 * r);
PointF E = new PointF(p + w, p + r);
PointF F = new PointF(p + w - a, p + 0);
hexagonPath.moveTo(Y.x, Y.y);
hexagonPath.lineTo(A.x, A.y);
hexagonPath.lineTo(B.x, B.y);
hexagonPath.lineTo(C.x, C.y);
hexagonPath.lineTo(D.x, D.y);
hexagonPath.lineTo(E.x, E.y);
hexagonPath.lineTo(F.x, F.y);
hexagonPath.lineTo(X.x, X.y);
}
#Override
protected void onBoundsChange(Rect bounds) {
mWidth = bounds.width();
mHeight = bounds.height();
calculatePath();
}
#Override
public void draw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(mStrokeColor); // set the color
paint.setStrokeWidth(mStrokeWidth); // set the size
paint.setDither(true); // set the dither to true
paint.setStyle(Paint.Style.STROKE); // set to STOKE
paint.setStrokeJoin(Paint.Join.ROUND); // set the join to round you want
paint.setStrokeCap(Paint.Cap.ROUND); // set the paint cap to round too
paint.setPathEffect(new CornerPathEffect(mStrokeWidth)); // set the path effect when they join.
paint.setAntiAlias(true);
canvas.drawPath(hexagonPath, paint);
canvas.clipPath(hexagonPath, Region.Op.INTERSECT);
canvas.drawColor(mBackgroundColor);
canvas.drawPath(hexagonPath, paint);
canvas.save();
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return 0;
}
}
It seems that ImageView use all width in this case.
How to implements Drawable correctly to use it with ImageView?
The root of the problem was clip mode.
Its better to use canvas.clipPath(hexagonPath, Region.Op.REPLACE);
Also, question example works well with ImageView, but after deep investigation I undestand, that at android 5.0 and above this drawable is used at drawableLeft in TextView.
Also its not need to override getIntrinsicHeight
Your code is absolutely correct:
This is how I fill ImageView (in onCreate() of activity):
((ImageView)findViewById(R.id.hexImageView)).setImageDrawable(new HexDrawable());
Layout of the Activity on the screenshot:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<ImageView
android:id="#+id/hexImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
If I replace wrap_content with value, hexagon as expected is changing its size.
Tested on Android 6.0 and 4.3.
Just one tip - instead of hardcoded values in getIntrinsicHeight() and getIntrinsicWidth(), it might be better to use dimens instead.
I used square progress bar library from Github android square progress bar, everything is working fine but i want to make the bar should fill my image.. anyone having idea regarding this.???
import java.text.DecimalFormat;
import net.yscs.android.square_progressbar.utils.CalculationUtil;
import net.yscs.android.square_progressbar.utils.PercentStyle;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
public class SquareProgressView extends View {
private double progress;
private final Paint progressBarPaint;
private final Paint outlinePaint;
private final Paint textPaint;
private float widthInDp = 10;
private float strokewidth = 0;
private Canvas canvas;
private boolean outline = false;
private boolean startline = false;
private boolean showProgress = false;
private PercentStyle percentSettings = new PercentStyle(Align.CENTER, 150,
true);
private boolean clearOnHundred = false;
public SquareProgressView(Context context) {
super(context);
progressBarPaint = new Paint();
progressBarPaint.setColor(context.getResources().getColor(
android.R.color.holo_green_dark));
progressBarPaint.setStrokeWidth(CalculationUtil.convertDpToPx(
widthInDp, getContext()));
progressBarPaint.setAntiAlias(true);
progressBarPaint.setStyle(Style.STROKE);
outlinePaint = new Paint();
outlinePaint.setColor(context.getResources().getColor(
android.R.color.black));
outlinePaint.setStrokeWidth(1);
outlinePaint.setAntiAlias(true);
outlinePaint.setStyle(Style.STROKE);
textPaint = new Paint();
textPaint.setColor(context.getResources().getColor(
android.R.color.black));
textPaint.setAntiAlias(true);
textPaint.setStyle(Style.STROKE);
}
public SquareProgressView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
progressBarPaint = new Paint();
progressBarPaint.setColor(context.getResources().getColor(
android.R.color.holo_green_dark));
progressBarPaint.setStrokeWidth(CalculationUtil.convertDpToPx(
widthInDp, getContext()));
progressBarPaint.setAntiAlias(true);
progressBarPaint.setStyle(Style.STROKE);
outlinePaint = new Paint();
outlinePaint.setColor(context.getResources().getColor(
android.R.color.black));
outlinePaint.setStrokeWidth(1);
outlinePaint.setAntiAlias(true);
outlinePaint.setStyle(Style.STROKE);
textPaint = new Paint();
textPaint.setColor(context.getResources().getColor(
android.R.color.black));
textPaint.setAntiAlias(true);
textPaint.setStyle(Style.STROKE);
}
public SquareProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
progressBarPaint = new Paint();
progressBarPaint.setColor(context.getResources().getColor(
android.R.color.holo_green_dark));
progressBarPaint.setStrokeWidth(CalculationUtil.convertDpToPx(
widthInDp, getContext()));
progressBarPaint.setAntiAlias(true);
progressBarPaint.setStyle(Style.STROKE);
outlinePaint = new Paint();
outlinePaint.setColor(context.getResources().getColor(
android.R.color.black));
outlinePaint.setStrokeWidth(1);
outlinePaint.setAntiAlias(true);
outlinePaint.setStyle(Style.STROKE);
textPaint = new Paint();
textPaint.setColor(context.getResources().getColor(
android.R.color.black));
textPaint.setAntiAlias(true);
textPaint.setStyle(Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
this.canvas = canvas;
super.onDraw(canvas);
strokewidth = CalculationUtil.convertDpToPx(widthInDp, getContext());
float scope = canvas.getWidth() + canvas.getHeight()
+ canvas.getHeight() + canvas.getWidth();
float percent = (scope / 100) * Float.valueOf(String.valueOf(progress));
float halfOfTheImage = canvas.getWidth() / 2;
if (outline) {
drawOutline();
}
if (isStartline()) {
drawStartline();
}
if (showProgress) {
drawPercent(percentSettings);
}
if (clearOnHundred && progress == 100.0) {
return;
}
Path path = new Path();
if (percent > halfOfTheImage) {
paintFirstHalfOfTheTop(canvas);
float second = percent - halfOfTheImage;
if (second > canvas.getHeight()) {
paintRightSide(canvas);
float third = second - canvas.getHeight();
if (third > canvas.getWidth()) {
paintBottomSide(canvas);
float forth = third - canvas.getWidth();
if (forth > canvas.getHeight()) {
paintLeftSide(canvas);
float fifth = forth - canvas.getHeight();
if (fifth == halfOfTheImage) {
paintSecondHalfOfTheTop(canvas);
} else {
path.moveTo(strokewidth, (strokewidth / 2));
path.lineTo(strokewidth + fifth, (strokewidth / 2));
canvas.drawPath(path, progressBarPaint);
}
} else {
path.moveTo((strokewidth / 2), canvas.getHeight()
- strokewidth);
path.lineTo((strokewidth / 2), canvas.getHeight()
- forth);
canvas.drawPath(path, progressBarPaint);
}
} else {
path.moveTo(canvas.getWidth() - strokewidth,
canvas.getHeight() - (strokewidth / 2));
path.lineTo(canvas.getWidth() - third, canvas.getHeight()
- (strokewidth / 2));
canvas.drawPath(path, progressBarPaint);
}
} else {
path.moveTo(canvas.getWidth() - (strokewidth / 2), strokewidth);
path.lineTo(canvas.getWidth() - (strokewidth / 2), strokewidth
+ second);
canvas.drawPath(path, progressBarPaint);
}
} else {
path.moveTo(halfOfTheImage, strokewidth / 2);
path.lineTo(halfOfTheImage + percent, strokewidth / 2);
canvas.drawPath(path, progressBarPaint);
}
}
private void drawStartline() {
Path outlinePath = new Path();
outlinePath.moveTo(canvas.getWidth() / 2, 0);
outlinePath.lineTo(canvas.getWidth() / 2, strokewidth);
canvas.drawPath(outlinePath, outlinePaint);
}
private void drawOutline() {
Path outlinePath = new Path();
outlinePath.moveTo(0, 0);
outlinePath.lineTo(canvas.getWidth(), 0);
outlinePath.lineTo(canvas.getWidth(), canvas.getHeight());
outlinePath.lineTo(0, canvas.getHeight());
outlinePath.lineTo(0, 0);
canvas.drawPath(outlinePath, outlinePaint);
}
public void paintFirstHalfOfTheTop(Canvas canvas) {
Path path = new Path();
path.moveTo(canvas.getWidth() / 2, strokewidth / 2);
path.lineTo(canvas.getWidth() + strokewidth, strokewidth / 2);
canvas.drawPath(path, progressBarPaint);
}
public void paintRightSide(Canvas canvas) {
Path path = new Path();
path.moveTo(canvas.getWidth() - (strokewidth / 2), strokewidth);
path.lineTo(canvas.getWidth() - (strokewidth / 2), canvas.getHeight());
canvas.drawPath(path, progressBarPaint);
}
public void paintBottomSide(Canvas canvas) {
Path path = new Path();
path.moveTo(canvas.getWidth() - strokewidth, canvas.getHeight()
- (strokewidth / 2));
path.lineTo(0, canvas.getHeight() - (strokewidth / 2));
canvas.drawPath(path, progressBarPaint);
}
public void paintLeftSide(Canvas canvas) {
Path path = new Path();
path.moveTo((strokewidth / 2), canvas.getHeight() - strokewidth);
path.lineTo((strokewidth / 2), 0);
canvas.drawPath(path, progressBarPaint);
}
public void paintSecondHalfOfTheTop(Canvas canvas) {
Path path = new Path();
path.moveTo(strokewidth, (strokewidth / 2));
path.lineTo(canvas.getWidth() / 2, (strokewidth / 2));
canvas.drawPath(path, progressBarPaint);
}
public double getProgress() {
return progress;
}
public void setProgress(double progress) {
this.progress = progress;
this.invalidate();
}
public void setColor(int color) {
progressBarPaint.setColor(color);
this.invalidate();
}
/**
* #return the border
*/
public float getWidthInDp() {
return widthInDp;
}
/**
* #return the border
*/
public void setWidthInDp(int width) {
this.widthInDp = width;
progressBarPaint.setStrokeWidth(CalculationUtil.convertDpToPx(
widthInDp, getContext()));
this.invalidate();
}
public boolean isOutline() {
return outline;
}
public void setOutline(boolean outline) {
this.outline = outline;
this.invalidate();
}
public boolean isStartline() {
return startline;
}
public void setStartline(boolean startline) {
this.startline = startline;
this.invalidate();
}
private void drawPercent(PercentStyle setting) {
textPaint.setTextAlign(setting.getAlign());
if (setting.getTextSize() == 0) {
textPaint.setTextSize((canvas.getHeight() / 10) * 4);
} else {
textPaint.setTextSize(setting.getTextSize());
}
String percentString = new DecimalFormat("###").format(getProgress());
if (setting.isPercentSign()) {
percentString = percentString + percentSettings.getCustomText();
}
textPaint.setColor(percentSettings.getTextColor());
canvas.drawText(
percentString,
canvas.getWidth() / 2,
(int) ((canvas.getHeight() / 2) - ((textPaint.descent() + textPaint
.ascent()) / 2)), textPaint);
}
public boolean isShowProgress() {
return showProgress;
}
public void setShowProgress(boolean showProgress) {
this.showProgress = showProgress;
this.invalidate();
}
public void setPercentStyle(PercentStyle percentSettings) {
this.percentSettings = percentSettings;
this.invalidate();
}
public PercentStyle getPercentStyle() {
return percentSettings;
}
public void setClearOnHundred(boolean clearOnHundred) {
this.clearOnHundred = clearOnHundred;
this.invalidate();
}
public boolean isClearOnHundred() {
return clearOnHundred;
}
}
above code is used to create square shape progressbar.
Thanks in Advance.
I thought about adding this possibility to the library a few weeks ago. But the problem is, that I'm using a Path to calculate the progress around the image. And it seems to be difficult to work with paths and arcs when you only draw parts of it (the progress). But I did a bit of research and found these two ways on how you can draw rounded corners in general:
Canvas - drawRoundRect
This (drawRoundRect) is a method that the Canvas offers. But the problem here is that you need to give the method a rectangular which then gets rounded corners. As I said above, I'm using a path to draw the progress. So as far as I can see, you can't use that for my library code to add the possibility for rounded corners.
Path - addArc / arcTo
The path itself has two methods (addArc / arcTo) to work with arcs. But the problem is, that if you use them to apply some rounded corners to the path, you then have the problem of displaying the right percentage. Because you need a different way of calculating how far the arcs can go. So this would be a possibility, but it needs a rewrite/extension of my current progress-calculation.
Maybe there is another solution that I haven't thought off. But for the moment there is no supported way in the library to solve this. But you can try it with the two arc-methods from above. If you want that I (or somebody else) takes a try to add this to the library, please add a new issue to the github repository.
I have a class (SpotDetails) which includes a fragment which is drawn programically. Untill now i've had the fragment drawing class (WindRose) as a child of the main class.
What i would like to do is to move the WindRose class into a AsynTask for better User Experience. Now the Application is suffering from too much work on the main thread.
Code to implement the WindRose :
WindRose windRose = new WindRose(SpotDetails.this);
//Add a new windRose (Which is created under)
FrameLayout.addView(windRose);
WindRose code :
public class WindRose extends View {
public WindRose(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float height = (float) getHeight();
float width = (float) getWidth();
float radius;
if (width > height) {
radius = height / 2;
} else {
radius = width / 2;
}
// radius = (height )/ 2;
Path path = new Path();
path.addCircle(width, height, radius, Path.Direction.CCW);
// / 2
Resources resources = getResources();
int color = resources.getColor(R.color.green_back);
Paint paint = new Paint();
paint.setColor(color);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL);
float center_x, center_y;
center_x = width / 2;
center_y = height / 2;
final RectF oval = new RectF();
//Formulas :
//SD = Start Degree
//ED = End Degree
//If cakepiece passes 0 (East)
//SD, 360-(SD+ED)
//Else :
//SD, (ED-SD)
oval.set(center_x - radius, center_y - radius, center_x + radius, center_y + radius);
if (End > Start) {
canvas.drawArc(oval, Start, (End - Start), true, paint);
} else if (End < Start) {
canvas.drawArc(oval, Start, ((360 - Start) + End), true, paint);
}
}
}
Im not sure if i explained things right so please tell me if things are unclear :)
I have tried to do this :
public class WindRose extends Activity {
float Start, End;
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public View DrawRose (Context context){
this.context = context;
WindRoseDrawer windRoseDrawer = new WindRoseDrawer(context);
return null; //What should i return here ?
}
private class DrawWindRose extends AsyncTask<String, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(String... strings) {
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
public class WindRoseDrawer extends View {
public WindRoseDrawer(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float height = (float) getHeight();
float width = (float) getWidth();
float radius;
if (width > height) {
radius = height / 2;
} else {
radius = width / 2;
}
// radius = (height )/ 2;
Path path = new Path();
path.addCircle(width, height, radius, Path.Direction.CCW);
// / 2
Resources resources = getResources();
int color = resources.getColor(R.color.green_back);
Paint paint = new Paint();
paint.setColor(color);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL);
float center_x, center_y;
center_x = width / 2;
center_y = height / 2;
final RectF oval = new RectF();
//Formulas :
//SD = Start Degree
//ED = End Degree
//If cakepiece passes 0 (East)
//SD, 360-(SD+ED)
//Else :
//SD, (ED-SD)
oval.set(center_x - radius, center_y - radius, center_x + radius, center_y + radius);
if (End > Start) {
canvas.drawArc(oval, Start, (End - Start), true, paint);
} else if (End < Start) {
canvas.drawArc(oval, Start, ((360 - Start) + End), true, paint);
}
}
}
}
But how should i implement this back to the SpotDetails ? And what should i return from DrawRose ?
You should draw only in the UI thread. You can't draw in the background if using the Draw inheritance method from View. Better to use a SurfaceView with lock / unlock canvas. It will use an optimized algorithm which allows for background drawing.
#Override
public void run() {
while(locker){
//checks if the lockCanvas() method will be success,and if not, will check this statement again
if(!holder.getSurface().isValid()){
continue;
}
/** Start editing pixels in this surface.*/
Canvas canvas = holder.lockCanvas();
//ALL PAINT-JOB MAKE IN draw(canvas); method.
draw(canvas);
// End of painting to canvas. system will paint with this canvas,to the surface.
holder.unlockCanvasAndPost(canvas);
}
}