How to create an imageView rounded by circle progress bar - android

I am trying to draw a circle progress bar around an circle image. I have tried to create my own "CircleImageView" Class which extends "ImageView"
my onDraw() method
protected void onDraw(Canvas canvas) {
canvas.drawCircle(mCenter[0], mCenter[1], mRadius, ringPaint);
progress = 30; //for testing
float angle = 360 * progress / maxProgress;
canvas.drawArc(mOval, mStartAngle, angle, false, progressPaint);
super.onDraw(canvas);
}
onMeasure method()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
final int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int min = Math.min(width, height);
setMeasuredDimension(width, height);
// Get image matrix values and place them in an array
float[] f = new float[9];
getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
Log.e("DBG", "[" + origW + "," + origH + "] -> [" + actW + "," + actH + "] & scales: x=" + scaleX + " y=" +
scaleY);
mCenter = new int[]{width / 2, height / 2};
mRadius = (int) mCenter[0] - actW - progressStrokeWidth / 2;
mOval.set(mCenter[0] - mRadius, mCenter[1] - mRadius, mCenter[0] + mRadius, mCenter[1] + mRadius); //mOval is an instance of RectF
}
init() method
private void init(Context context, AttributeSet attrs) {
mContext = context;
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CircleProgressAttr);
progressStrokeWidth = typedArray.getInteger(R.styleable.CircleProgressAttr_progressRingSize, 20);
mRingColor = typedArray.getInt(R.styleable.CircleProgressAttr_progressRingColor, R.color.ringColor);
mProgressColor = typedArray.getInt(R.styleable.CircleProgressAttr_progressColor, R.color.tab_underline_color);
mStartAngle = -90;
maxProgress = 100;
mOval = new RectF();
ringPaint = new Paint();
progressPaint = new Paint();
ringPaint.setColor(mRingColor);
ringPaint.setStyle(Paint.Style.STROKE);
ringPaint.setStrokeWidth(progressStrokeWidth);
ringPaint.setAntiAlias(true);
progressPaint.setColor(mProgressColor);
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setStrokeWidth(progressStrokeWidth);
progressPaint.setAntiAlias(true);
}
I have inserted the CircleView into a RelativeLayout with the following XML-code
<com.kevinwang.simpleplayer.CircleImageView
android:id="#+id/cd_img"
android:src="#drawable/cd01"
android:layout_above="#id/play_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"/>
I ran the app, but the progressbar didn't show, is there anything wrong with my code ?

Related

How can I use an image on the side of a drawable layer list

My UI designer has come up with a design like this for a progress bar:
The numbers should be the progress and max.
The circle image will change. On some of the designs there is also an image next to the 1500.
Whenever I try to do this on a layer-list I always end up with the image in the middle of the drawable. Ideally I would prefer to use vector graphics but I can use png if needed. I haven't even looked at putting the numbers there yet.
So how can this be accomplished?
Thank you.
Using canvas seems to me easier for this case. Here a short sample i could come up with right now:
public class Loading extends View {
private final static float VIEW_HEIGHT = 80;
private RectF viewRectangle;
private RectF progressRectangle;
private float cornersRadius;
private Paint progressBarPaint;
private Paint progressTextPaint;
private Paint containerBarPaint;
private Paint containerTextPaint;
private final int progressMaxValue = 1500;
private float containerTextY;
private float containerTextX;
private final float whiteCircleRadius = 14;
private final float progressTextPadding = 16;
private float progress;
public Loading(Context context) {
super(context);
progress = 0.5f;
progressTextPaint = new Paint(LINEAR_TEXT_FLAG|SUBPIXEL_TEXT_FLAG|ANTI_ALIAS_FLAG);
progressTextPaint.setColor(Color.WHITE);
progressTextPaint.setStyle(Paint.Style.FILL_AND_STROKE);
progressTextPaint.setTextSize(28);
progressTextPaint.setStrokeWidth(1f);
containerTextPaint = new Paint(progressTextPaint);
containerTextPaint.setColor(Color.BLACK);
progressBarPaint = new Paint(ANTI_ALIAS_FLAG);
progressBarPaint.setColor(Color.BLUE);
progressBarPaint.setStyle(Paint.Style.FILL);
containerBarPaint = new Paint(ANTI_ALIAS_FLAG);
containerBarPaint.setColor(Color.GRAY);
containerBarPaint.setStyle(Paint.Style.FILL);
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
int horizontalPadding = getPaddingLeft() + getPaddingRight();
int verticalPadding = getPaddingTop() + getPaddingBottom();
float measuredHeight = height - verticalPadding;
float targetHeight = measuredHeight < VIEW_HEIGHT ? measuredHeight : VIEW_HEIGHT;
float containerWidth = width - horizontalPadding;
viewRectangle = new RectF(getPaddingLeft(), getPaddingTop(), containerWidth, targetHeight);
progressRectangle = new RectF(viewRectangle);
cornersRadius = targetHeight / 2;
String maxValueText = String.valueOf(progressMaxValue);
Rect containerTextBounds = new Rect();
containerTextPaint.getTextBounds(maxValueText, 0, maxValueText.length(), containerTextBounds);
containerTextX = viewRectangle.right - containerTextBounds.width() - cornersRadius / 2;
containerTextY = viewRectangle.centerY() - containerTextBounds.exactCenterY();
}
#Override
protected void onDraw(Canvas canvas) {
float progressWidth = viewRectangle.right * progress;
float cornerDiameter = cornersRadius * 2;
progressRectangle.right = progressWidth > cornerDiameter ? progressWidth : cornerDiameter;
canvas.drawRoundRect(viewRectangle, cornersRadius, cornersRadius, containerBarPaint);
canvas.drawRoundRect(progressRectangle, cornersRadius, cornersRadius, progressBarPaint);
canvas.drawCircle(progressRectangle.right - cornersRadius, progressRectangle.centerY(), whiteCircleRadius, progressTextPaint);
canvas.drawText(String.valueOf(progressMaxValue), containerTextX, containerTextY, containerTextPaint);
String progressText = String.valueOf((int) (progressMaxValue * progress));
float progressTextWidth = progressTextPaint.measureText(progressText);
if (progressTextWidth < progressRectangle.right * 2) {
float requiredProgressTextSpace = cornersRadius + whiteCircleRadius + progressTextPadding + progressTextWidth;
canvas.drawText(progressText, progressRectangle.right - requiredProgressTextSpace, containerTextY, progressTextPaint);
}
}
}
It's far from perfect and not very flexible, but a good place to start with. If you need some comments here, feel free to ask. Here the render of the code above:

Diplaying bitmap in custom view with scale type center inside

I have customview for displaying two bitmaps but the problem is it is scaling to center crop but I don't want to crop bitmap instead I want to display full bitmap in canvas.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
mLayoutWidth = MeasureSpec.getSize(widthMeasureSpec);
mLayoutHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(mLayoutWidth, mLayoutHeight);
mBitmap = scaleCenterCrop(mBitmap, mLayoutWidth, mLayoutHeight);
overlayDefault = FastBlur.doBlur(mBitmap, 20, false);
overlayDefault = scaleCenterCrop(overlayDefault, mLayoutWidth,
mLayoutHeight);
overlay = mBitmap.copy(Config.ARGB_8888, true);
overlay = scaleCenterCrop(overlay, mLayoutWidth, mLayoutHeight);
cx = (mLayoutWidth - mBitmap.getWidth()) >> 1;
cy = (mLayoutHeight - mBitmap.getHeight()) >> 1;
c2 = new Canvas(overlay);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final Rect bitmapRect = ImageViewUtil.getBitmapRectCenterInside(
mBitmap.getWidth(), mBitmap.getHeight(), mLayoutWidth, mLayoutHeight);
setBitmapRect(bitmapRect);
invalidate();
}
public Bitmap scaleCenterCrop(Bitmap original, int deviceHeight,
int deviceWidth) {
int old_width = original.getWidth();
int old_height = original.getHeight();
float scale = Math.max((float) deviceHeight / old_height,
(float) deviceWidth / old_width);
float newWidth = scale * old_width;
float newHeight = scale * old_height;
float left = (deviceWidth - newWidth) / 2;
float top = (deviceHeight - newHeight) / 2;
RectF rectF = new RectF(left, top, left + newWidth, top + newHeight);
Bitmap scaled = Bitmap.createBitmap(deviceWidth, deviceHeight,
original.getConfig());
Canvas canvas = new Canvas(scaled);
canvas.drawBitmap(original, null, rectF, null);
return scaled;
}

How to align center a bitmap?

i want my bitmap in the center of my screen..i m trying it that way but its not working...
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.compass);
int w = canvas.getWidth();
int h = canvas.getHeight();
int bw=myBitmap.getWidth();
int bh=myBitmap.getHeight();
int cx = w / 2;
int cy = h / 2;
Display d = getWindowManager().getDefaultDisplay();
int x = d.getWidth();
int y = d.getHeight();
int dx = x / 2;
int dw = y /2;
canvas.translate(cx, cy);
if (mValues != null) {
canvas.rotate(-mValues[0]);
}
int centreX = (x - bw) /2;
int centreY = (cy - bh) /2;
//canvas.drawPath(mPath, mPaint);
canvas.drawBitmap(myBitmap, centreX,centreY, null);
Here's the code you need in your view:
private int mWidth;
private int mHeight;
private float mAngle;
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
mWidth = View.MeasureSpec.getSize(widthMeasureSpec);
mHeight = View.MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(mWidth, mHeight);
}
#Override protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.compass);
// Here's the magic. Whatever way you do it, the logic is:
// space available - bitmap size and divide the result by two.
// There must be an equal amount of pixels on both sides of the image.
// Therefore whatever space is left after displaying the image, half goes to
// left/up and half to right/down. The available space you get by subtracting the
// image's width/height from the screen dimensions. Good luck.
int cx = (mWidth - myBitmap.getWidth()) >> 1; // same as (...) / 2
int cy = (mHeight - myBitmap.getHeight()) >> 1;
if (mAngle > 0) {
canvas.rotate(mAngle, mWidth >> 1, mHeight >> 1);
}
canvas.drawBitmap(myBitmap, cx, cy, null);
}
Screenshot just for fun: http://imgur.com/EYpMJ
(Diagonal lines not part of the code posted here)
EDIT: Added NickT's solution.
EDIT 2: Changed mvalues[0] to mAngle and made it conditional. Changed divide by 2 operations to bitshifts. Remove rotation code if you don't need it.
You can try this :
int width = containerBitmap.getWidth();
int height = containerBitmap.getHeight();
float centerX = (width - centeredBitmap.getWidth()) * 0.5f;
float centerY = (height- centeredBitmap.getHeight()) * 0.5f;
You can use it to draw a bitmap at the center of another bitmap.

Pass drawable image to TypedArray in Android

I am trying to implement a segmentated radio button from here: https://github.com/makeramen/android-segmentedradiobutton but I need to set the image programmatically and not in XML.
This is the source of the custom RadioButton:
public class CenteredImageButton extends RadioButton {
Drawable image;
public CenteredImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CompoundButton, 0, 0);
image = a.getDrawable(1);
setButtonDrawable(android.R.id.empty);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (image != null) {
image.setState(getDrawableState());
// scale image to fit inside button
int imgHeight = image.getIntrinsicHeight();
Log.d("IMAGEHEIGHT", "imageWidth is " + imgHeight);
int imgWidth = image.getIntrinsicWidth();
Log.d("IMAGEWIDTH", "imageWidth is " + imgWidth);
int btnWidth = getWidth();
Log.d("BUTTONWIDTH", "buttonWidth is " + btnWidth);
int btnHeight = getHeight();
Log.d("BUTTONHEIGHT", "buttonHeight is " + btnHeight);
float scale;
if (imgWidth <= btnWidth && imgHeight <= btnHeight) {
scale = 1.0f;
} else {
scale = Math.min((float) btnWidth / (float) imgWidth,
(float) btnHeight / (float) imgHeight);
}
Log.d("SCALE", "scale is " + scale);
int dx = (int) ((btnWidth - imgWidth * scale) * 0.5f + 0.5f);
Log.d("DX", "dx is " + dx);
int dy = (int) ((btnHeight - imgHeight * scale) * 0.5f + 0.5f);
Log.d("DY", "dy is " + dy);
image.setBounds(dx, dy, (int) (dx + imgWidth * scale),
(int) (dy + imgHeight * scale));
image.draw(canvas);
}
}
I am setting the drawable in another file like this:
private void setButtonImageProperties(RadioButton button,Drawable drawable){
button.setGravity(Gravity.CENTER);
Resources resources = this.context.getResources();
float dipValue = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
60, resources.getDisplayMetrics());
float dipValue1 = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 80, resources.getDisplayMetrics());
button.setMinHeight((int) dipValue);
button.setMinWidth((int) dipValue1);
button.setButtonDrawable(drawable);
}
Please anyone, advise. I really need helping hand. Thanks.
You pretty much need to add a setImage method to CenteredImageButton:
public void setImage(Drawable newImage) {
image = newImage;
}
And just call it later in your main code:
button.setImage(drawable);
See this Gist to see the method inline: https://gist.github.com/1470789
I also noticed you changed the name of my class from CenteredRadioImageButton to CenteredImageButton. If you're not actually using this for the RadioButton-like behavior, I would suggest using a standard ImageButton
(I am the maintainer of SegmentedRadioButton)

Image Edges Pixelated Depending on Position Android

I have a dial that I display wind direction in and the arrow displays well in some positions, but others its edges are pixelated. Here is the code to render the image:
public class DialView extends View {
private Context mContext;
private Bitmap mArrow;
private WeatherDataModel mWdm;
private float iters = 10.0f;
private static float previousAngle = 0.0f;
private int mHourIndex = 0;
private boolean isHourly = false;
private final int XLARGE = 0x4;
public DialView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
int screenLayout = mContext.getApplicationContext().getResources().getConfiguration().screenLayout;
mArrow = Utilities.applyFilter(context, BitmapFactory.decodeResource(context.getResources(), R.drawable.wind_arrow));
}
#Override
public void onDraw(Canvas canvas) {
float degrees = 0.0f;
degrees = (!isHourly) ? cardinalToDegrees(mWdm) : cardinalToDegrees(mWdm.hourly.get(mHourIndex));
Bitmap bit;
int originY = getHeight() / 2;
int originX = getWidth() / 2;
int r = originY > originX ? getWidth() * 8 / 27 : getHeight() * 8 / 27;
int x, y;
Matrix matrix = new Matrix();
degrees = (previousAngle * (iters / 10.0f) + degrees * (10.0f - iters) / 10.0f);
//Log.d(DEBUG_TAG, "Previous angle = " + previousAngle + " degrees" + degrees);
matrix.postRotate(degrees - 90.f);
bit = Bitmap.createBitmap(mArrow, 0, 0, mArrow.getWidth(), mArrow.getHeight(), matrix, false);
x = (int)(Math.cos(Math.PI * degrees / 180.0f) * r) + originX - (bit.getWidth() / 2);
y = (int)(Math.sin(Math.PI * degrees / 180.0f) * r) + originY - (bit.getHeight() / 2);
//Log.d(DEBUG_TAG, "x: " + x + " y: " + y);
canvas.drawBitmap(bit, x, y, null);
if (iters > 0) {
invalidate();
iters--;
}
previousAngle = degrees;
}
Here is the arrow good:
Here it is pixelated:
Any ideas how to handle this?
try to define a paint object and enable AntiAlias
like this:
mPaint.setAntiAlias(true);
canvas.drawBitmap(bit, x, y, mPaint);

Categories

Resources