I am trying to achieve Pie Progress without border
without center circle and it's progress .. So I tried multiple ways to achieve it in ProgressBar
Both my approach shows a border around the PIE
I simply want to remove the border Or I want the below circle to be of exact size.
1st Approach
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="#color/grey_lightest"/>
</shape>
</item>
<item>
<shape
android:innerRadiusRatio="100"
android:shape="ring"
android:thicknessRatio="2.5"
android:useLevel="true">
<gradient
android:centerColor="#color/orange_above_avg"
android:endColor="#color/orange_above_avg"
android:startColor="#color/orange_above_avg"
android:type="sweep"
android:useLevel="false" />
</shape>
</item>
</layer-list>
2nd Approach
I created seperate background and progressDrawable but still same result..
It's not exact solution to your problem but you can use customview to achieve it .
Here is a reference which I picked from some project (so an't give proper credit)
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import in.eightfolds.soundvision.R;
public class CustomProgress extends View {
private static final String TAG = "CustomProgress";
private float maxWidth, maxHight;
private PointF centerPoint;
private float radius;
float progressSize = 0f;
private Path path;
private RectF oval;
private Paint mcirclePaint;
private Paint mTextPaint;
private int progress = 0;
private Paint mBackCirclePaint;
private int paintStrokeWidth=40;
public CustomProgress(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CustomProgress(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomProgress(Context context) {
super(context);
init();
}
private void init() {
centerPoint = new PointF();
path = new Path();
mcirclePaint = new Paint();
mcirclePaint.setColor(Color.RED);
mcirclePaint.setStrokeWidth(paintStrokeWidth);
// paint.setDither(true);
mcirclePaint.setAntiAlias(true);
mcirclePaint.setStyle(Paint.Style.STROKE);
mBackCirclePaint = new Paint();
mBackCirclePaint.setColor(Color.DKGRAY);
mBackCirclePaint.setStrokeWidth(paintStrokeWidth);
// paint.setDither(true);
mBackCirclePaint.setAntiAlias(true);
mBackCirclePaint.setStyle(Paint.Style.STROKE);
oval = new RectF();
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
// mTextPaint.setColor(getResources().getColor(
// android.R.color.holo_blue_dark));
mTextPaint.setColor(Color.RED);
mTextPaint.setStrokeWidth(1);
mTextPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setTextSize(60);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(centerPoint.x, centerPoint.y, radius,
mBackCirclePaint);
// mcirclePaint.setColor(Color.RED);
canvas.drawArc(oval, -90, progressSize, false, mcirclePaint);
// paint.setTextSize(20);
// canvas.drawCircle(centerPoint.x, centerPoint.y, 2, mcirclePaint);
if (progress > 0) {
canvas.drawText(progress + "%", centerPoint.x, centerPoint.y
+ mTextPaint.descent(), mTextPaint);
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
Log.v(TAG, "" + changed);
if (getMeasuredWidth() < getMeasuredHeight()) {
maxWidth = getMeasuredWidth();
maxHight = getMeasuredHeight();
centerPoint.x = maxWidth / 2;
centerPoint.y = maxHight / 2;
} else {
maxWidth = getMeasuredHeight();
maxHight = getMeasuredWidth();
centerPoint.x = maxHight / 2;
centerPoint.y = maxWidth / 2;
}
setUp();
}
private void setUp() {
if (maxWidth > maxHight) {
radius = maxHight / 4;
} else {
radius = maxWidth / 4;
}
path.addCircle(maxWidth / 2,
maxHight / 2, radius,
Path.Direction.CW);
oval.set(centerPoint.x - radius,
centerPoint.y - radius,
centerPoint.x + radius,
centerPoint.y + radius);
}
public void upDateProgress(int progress) {
mcirclePaint.setColor(getResources().getColor(R.color.themeColor));
mTextPaint.setColor(getResources().getColor(R.color.themeColor));
this.progress = progress;
float i = 360f / 100f;
progressSize = (float) (i * progress);
Log.d(TAG, progress + "<----progress---->" + progressSize);
invalidate();
}
public int getProgress() {
return progress;
}
}
Use this in layout file and use upDateProgress to change progress .
Change widths and colors to your own . If you want text, then place a textview in center to this customview
Related
I'm new to android and I'm trying to make a circular progress bar with nice style.
Therefore I found a very simple solution how to make a circular progress bar, using shape="ring"
My code is
mainpage_layout.xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="250dp"
android:layout_height="250dp"
android:max="100"
android:progress="80"
android:rotation="-90"
android:layout_centerInParent="true"
android:progressDrawable="#drawable/circular_progress" />
</RelativeLayout>
circular_progress.xml:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:useLevel="true"
android:innerRadiusRatio="2.3"
android:shape="ring"
android:thickness="7dp" >
<solid android:color="#color/colorPrimary" />
</shape>
and its looks like this
I want to style it like this awesome progress bar :
Edit
This is the padded background for the progress bar that i want to make
to add some background to the progress bar and to make the edges of it be rounded instead of squared.
Is there is an easy solution to do that or should I must give up on using this shape="ring" thing.
Thank you very much,
Asaf.
You can use https://github.com/korre/android-circular-progress-bar
There you have a method useRoundedCorners you need to pass false to make it not round by-default it is round at the edge
There is a custom class you can actually take from that library(It is more than enough),
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
public class CircularProgressBar extends View {
private int mViewWidth;
private int mViewHeight;
private final float mStartAngle = -90; // Always start from top (default is: "3 o'clock on a watch.")
private float mSweepAngle = 0; // How long to sweep from mStartAngle
private float mMaxSweepAngle = 360; // Max degrees to sweep = full circle
private int mStrokeWidth = 20; // Width of outline
private int mAnimationDuration = 400; // Animation duration for progress change
private int mMaxProgress = 100; // Max progress to use
private boolean mDrawText = true; // Set to true if progress text should be drawn
private boolean mRoundedCorners = true; // Set to true if rounded corners should be applied to outline ends
private int mProgressColor = Color.BLACK; // Outline color
private int mTextColor = Color.BLACK; // Progress text color
private final Paint mPaint; // Allocate paint outside onDraw to avoid unnecessary object creation
public CircularProgressBar(Context context) {
this(context, null);
}
public CircularProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircularProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
initMeasurments();
drawOutlineArc(canvas);
if (mDrawText) {
drawText(canvas);
}
}
private void initMeasurments() {
mViewWidth = getWidth();
mViewHeight = getHeight();
}
private void drawOutlineArc(Canvas canvas) {
final int diameter = Math.min(mViewWidth, mViewHeight) - (mStrokeWidth * 2);
final RectF outerOval = new RectF(mStrokeWidth, mStrokeWidth, diameter, diameter);
mPaint.setColor(mProgressColor);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(mRoundedCorners ? Paint.Cap.ROUND : Paint.Cap.BUTT);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(outerOval, mStartAngle, mSweepAngle, false, mPaint);
}
private void drawText(Canvas canvas) {
mPaint.setTextSize(Math.min(mViewWidth, mViewHeight) / 5f);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setStrokeWidth(0);
mPaint.setColor(mTextColor);
// Center text
int xPos = (canvas.getWidth() / 2);
int yPos = (int) ((canvas.getHeight() / 2) - ((mPaint.descent() + mPaint.ascent()) / 2)) ;
canvas.drawText(calcProgressFromSweepAngle(mSweepAngle) + "%", xPos, yPos, mPaint);
}
private float calcSweepAngleFromProgress(int progress) {
return (mMaxSweepAngle / mMaxProgress) * progress;
}
private int calcProgressFromSweepAngle(float sweepAngle) {
return (int) ((sweepAngle * mMaxProgress) / mMaxSweepAngle);
}
/**
* Set progress of the circular progress bar.
* #param progress progress between 0 and 100.
*/
public void setProgress(int progress) {
ValueAnimator animator = ValueAnimator.ofFloat(mSweepAngle, calcSweepAngleFromProgress(progress));
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(mAnimationDuration);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mSweepAngle = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
animator.start();
}
public void setProgressColor(int color) {
mProgressColor = color;
invalidate();
}
public void setProgressWidth(int width) {
mStrokeWidth = width;
invalidate();
}
public void setTextColor(int color) {
mTextColor = color;
invalidate();
}
public void showProgressText(boolean show) {
mDrawText = show;
invalidate();
}
/**
* Toggle this if you don't want rounded corners on progress bar.
* Default is true.
* #param roundedCorners true if you want rounded corners of false otherwise.
*/
public void useRoundedCorners(boolean roundedCorners) {
mRoundedCorners = roundedCorners;
invalidate();
}
}
Then you can set the view in your xml like
<yourPackageName.CircularProgressBar
android:id="#+id/circularProgress"
android:layout_width="180dp"
android:layout_height="180dp"/>
And in your class you can call it like this,
CircularProgressBar circularProgressBar = (CircularProgressBar) findViewById(R.id.circularProgress);
circularProgressBar.setProgress(50);
circularProgressBar.setProgressColor(Color.BLUE);
By making a custom ImageView and override the onDraw method with the following will make the ImageView to have rounded corners. Reference
#Override
protected void onDraw(Canvas canvas) {
float radius = getContext().getResources().getDimension(R.dimen.round_corner_radius);
Path path = new Path();
RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
path.addRoundRect(rect, radius, radius, Path.Direction.CW);
canvas.clipPath(path);
super.onDraw(canvas);
}
How can I selectively make the round corners instead of making all four corners round. For example, only making the top left and top right corners round and leave the bottom corners intact. Here is a solution to do to through Bitmap. I am looking for doing it in this onDraw method and only using Path and RectF.
There is a Path#addRoundRect() overload that takes a float array of eight values wherein we can specify the x- and y-radius for each of the four corners. These values are in [x, y] pairs, starting at the top-left corner, and going clockwise around the rest. For those corners we want rounded, we set both values of the pair to the radius value, and leave them at zero for those we don't.
As an illustrative example, a simple method that will return a Path that can be used in your snippet:
private Path getPath(float radius, boolean topLeft, boolean topRight,
boolean bottomRight, boolean bottomLeft) {
final Path path = new Path();
final float[] radii = new float[8];
if (topLeft) {
radii[0] = radius;
radii[1] = radius;
}
if (topRight) {
radii[2] = radius;
radii[3] = radius;
}
if (bottomRight) {
radii[4] = radius;
radii[5] = radius;
}
if (bottomLeft) {
radii[6] = radius;
radii[7] = radius;
}
path.addRoundRect(new RectF(0, 0, getWidth(), getHeight()),
radii, Path.Direction.CW);
return path;
}
Per your example description, rounding the top-left and top-right corners:
#Override
protected void onDraw(Canvas canvas) {
float radius = getContext().getResources().getDimension(R.dimen.round_corner_radius);
Path path = getPath(radius, true, true, false, false);
canvas.clipPath(path);
super.onDraw(canvas);
}
As always, I would recommend keeping the onDraw() method as tight as possible, moving anything that doesn't absolutely have to be there elsewhere. The resource value for the radius, for instance, could be retrieved in the constructor, and kept in a field. Furthermore, the Path could be constructed only when necessary; i.e., when the View's size changes, or when the radius or chosen corners change.
Since I put together a simple custom ImageView to test this, I'll include it here, as it demonstrates the above points. This custom View also offers XML attributes that allow the corner radius and the rounded corners to be set in your layout.
public class RoundishImageView extends ImageView {
public static final int CORNER_NONE = 0;
public static final int CORNER_TOP_LEFT = 1;
public static final int CORNER_TOP_RIGHT = 2;
public static final int CORNER_BOTTOM_RIGHT = 4;
public static final int CORNER_BOTTOM_LEFT = 8;
public static final int CORNER_ALL = 15;
private static final int[] CORNERS = {CORNER_TOP_LEFT,
CORNER_TOP_RIGHT,
CORNER_BOTTOM_RIGHT,
CORNER_BOTTOM_LEFT};
private final Path path = new Path();
private int cornerRadius;
private int roundedCorners;
public RoundishImageView(Context context) {
this(context, null);
}
public RoundishImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundishImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundishImageView);
cornerRadius = a.getDimensionPixelSize(R.styleable.RoundishImageView_cornerRadius, 0);
roundedCorners = a.getInt(R.styleable.RoundishImageView_roundedCorners, CORNER_NONE);
a.recycle();
}
public void setCornerRadius(int radius) {
if (cornerRadius != radius) {
cornerRadius = radius;
setPath();
invalidate();
}
}
public int getCornerRadius() {
return cornerRadius;
}
public void setRoundedCorners(int corners) {
if (roundedCorners != corners) {
roundedCorners = corners;
setPath();
invalidate();
}
}
public boolean isCornerRounded(int corner) {
return (roundedCorners & corner) == corner;
}
#Override
protected void onDraw(Canvas canvas) {
if (!path.isEmpty()) {
canvas.clipPath(path);
}
super.onDraw(canvas);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setPath();
}
private void setPath() {
path.rewind();
if (cornerRadius >= 1f && roundedCorners != CORNER_NONE) {
final float[] radii = new float[8];
for (int i = 0; i < 4; i++) {
if (isCornerRounded(CORNERS[i])) {
radii[2 * i] = cornerRadius;
radii[2 * i + 1] = cornerRadius;
}
}
path.addRoundRect(new RectF(0, 0, getWidth(), getHeight()),
radii, Path.Direction.CW);
}
}
}
For the XML attributes to work, the following needs to be in your <resources>, which you can do by putting this file in your project's res/values/ folder, or adding to the one that might already be there.
attrs.xml
<resources>
<declare-styleable name="RoundishImageView">
<attr name="cornerRadius" format="dimension" />
<attr name="roundedCorners">
<flag name="topLeft" value="1" />
<flag name="topRight" value="2" />
<flag name="bottomRight" value="4" />
<flag name="bottomLeft" value="8" />
<flag name="all" value="15" />
</attr>
</declare-styleable>
</resources>
The cornerRadius is a dimension attribute, and should be specified as a dp or px value. The roundedCorners is a flag attribute, and multiple corners can be chosen using the pipe character, |. For example:
<com.mycompany.myapp.RoundishImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/riv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:src="#drawable/magritte"
app:cornerRadius="#dimen/round_corner_radius"
app:roundedCorners="topLeft|topRight" />
I also spent a half-day to solve the problem; the key point here is how to use mPath.arcTo make a corner. The basic knowledge is E direction is 0 degree, then the second param means which degree start; the third param means how many degrees to show.
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
public class RectangleConerView extends View {
private Path mPath;
private Paint mPaint;
private PathMeasure mPathMeasure;
private float mAnimatorValue;
private Path mDst;
private float mLength;
private float left = 300;
private float top = 200;
private float width = 800;
private float height = 300;
private float checkWidth = 100;
private float checkHeight = 60;
private float cornerRadius = 30;
public RectangleConerView(Context context) {
super(context);
}
public RectangleConerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public RectangleConerView(Context context, AttributeSet attrs)
{
super(context, attrs);
mPathMeasure = new PathMeasure();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPath = new Path();
mPath.moveTo(left + width, top + checkHeight/2);
//bottom-right
mPath.lineTo(left + width, top + height - cornerRadius);
mPath.arcTo(new RectF(left + width-cornerRadius, top + height - cornerRadius, left + width, top + height), 0, 90); //start degree is E direct, then CW 90 degree, which is the bottom-right corner.
//bottom-left
mPath.lineTo(left + cornerRadius, top + height);
mPath.arcTo(new RectF(left, top + height - cornerRadius, left + cornerRadius, top + height), 90, 90);//start degree is the S, then CW 90 degree, which is the bottom-left corner.
//top-left
mPath.lineTo(left, top + cornerRadius);
mPath.arcTo(new RectF(left, top, left + cornerRadius, top + cornerRadius), 180, 90);//start degree W
//top-right
mPath.lineTo(left + width - checkWidth/2, top);
mPathMeasure.setPath(mPath, false);
mLength = mPathMeasure.getLength();
mDst = new Path();
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatorValue = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.setDuration(1000);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.start();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDst.reset();
// 硬件加速的BUG
mDst.lineTo(0,0);
float stop = mLength * mAnimatorValue;
mPathMeasure.getSegment(0, stop, mDst, true);
canvas.drawPath(mDst, mPaint);
}
}
In my Android app, I have to customize a seekbar and I wonder how I can set a seekbar thumb above its progress line instead of center by default?
You can check it out Discrete Seekbar
seekBar.setMin(0);
seekBar.setMax(yourArray.length);
seekBar.setOnProgressChangeListener(new DiscreteSeekBar.OnProgressChangeListener() {
int onProgressChanged =0;
#Override
public void onProgressChanged(DiscreteSeekBar seekBar, int value, boolean fromUser) {
onProgressChanged = value;
}
#Override
public void onStartTrackingTouch(DiscreteSeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(DiscreteSeekBar seekBar) {
}
});
I looked for this info in many posts and get some ideas from there and here, and created SeekBayHint, which creates text of progress above arrow:
SeekBar
MainClass:
package your_package;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import your_package.R;
public class SeekBarHint extends android.support.v7.widget.AppCompatSeekBar {
private Paint mTextPaint;
private Rect mTextBounds = new Rect();
private Bitmap mBitmapIconArrowDown;
/** for this class yPosition must be as minHeight in layoutFile for this seekBar*/
private static int sTextYPositionIndent = 20;
private float mTextSizeDecrease = 1.75f;
public static void setTextYPositionIndent(int textYPositionIndent) {
sTextYPositionIndent = textYPositionIndent;
}
public SeekBarHint(Context context) {
super(context);
mBitmapIconArrowDown = BitmapFactory.decodeResource(context.getResources(),
R.drawable.progress_seek_bar_arrow_down);
mTextPaint = new Paint();
mTextPaint.setColor(getResources().getColor(R.color.colorPrimary));
}
public SeekBarHint(Context context, AttributeSet attrs) {
super(context, attrs);
mBitmapIconArrowDown = BitmapFactory.decodeResource(context.getResources(),
R.drawable.progress_seek_bar_arrow_down);
mTextPaint = new Paint();
mTextPaint.setColor(getResources().getColor(R.color.colorPrimary));
}
public SeekBarHint(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mBitmapIconArrowDown = BitmapFactory.decodeResource(context.getResources(),
R.drawable.progress_seek_bar_arrow_down);
mTextPaint = new Paint();
mTextPaint.setColor(getResources().getColor(R.color.colorPrimary));
}
#Override
protected synchronized void onDraw(Canvas canvas) {
// first draw the regular progress bar, then custom draw our textString
super.onDraw(canvas);
// now progress position and convert to textString.
String textString = Integer.toString(getProgress()) + "%";
// now get size of seek bar.
float width = getWidth();
float height = getHeight();
// set textString size.
mTextPaint.setTextSize(height / mTextSizeDecrease);
// get size of textString.
mTextPaint.getTextBounds(textString, 0, textString.length(), mTextBounds);
// calculate where to start printing textString.
float position = (width / getMax()) * getProgress();
// get start and end points of where textString will be printed.
float textXStart = position - mTextBounds.centerX();
float textXEnd = position + mTextBounds.centerX();
// check does not start drawing text outside seek bar.
if (textXStart < 0)
textXStart = 0;
if (textXEnd > width)
textXStart -= (textXEnd - width);
// calculate y textString print position.
float yPosition = (height / 2) - mTextBounds.centerY();
canvas.drawText(textString, textXStart, yPosition - sTextYPositionIndent, mTextPaint);
// arrow draw logic
// check does not start drawing arrow outside seek bar
int seekBarAbsoluteWidth = getWidth() - getPaddingLeft() - getPaddingRight();
int thumbPos = (getPaddingLeft() / 2) + (seekBarAbsoluteWidth * getProgress() / getMax());
// set height and width for new bitmap
int arrowHeight = Math.round(mTextBounds.height()/2f);
int arrowWidth = mTextBounds.width()/3;
Bitmap scaledBitmapIconArrowDown = Bitmap
.createScaledBitmap(mBitmapIconArrowDown, arrowWidth, arrowHeight, true);
canvas.drawBitmap(scaledBitmapIconArrowDown, thumbPos, yPosition, null);
}
}
To support different dimension use this code in OnCreateView (if seekBar used in fragment) or in OnCreate (if seekBar used in activity):
DisplayMetrics metrics = getResources().getDisplayMetrics();
int dpi = metrics.densityDpi;
if(dpi < 230){
SeekBarHint.setTextYPositionIndent(5);
} else if (dpi < 310){
SeekBarHint.setTextYPositionIndent(15);
} else if (dpi < 470){
SeekBarHint.setTextYPositionIndent(10);
}
Can any one suggest me how to do circle animation and progress button like Uber driver pickup request in Android. If you can provide some code that will be good.
You can refer to this.
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import com.circularseekbar.R;
public class CircularProgressBar extends View {
private Context mContext;
private Paint circleColor, innerColor, circleRing;
private int angle = 0, startAngle = 270, barWidth = 50, maxProgress = 100;
private int width, height, progress=1, tmpProgress=1, progressPercent;
private float innerRadius, outerRadius, adjustmentFactor=100;//The radius of the inner circle
private float cx, cy; //The circle's center X, Y coordinate
private float left, right, top, bottom, startPointX, startPointY, markPointX, markPointY;
private float dx, dy;//The X and Y coordinate for the top left corner of the marking drawable
private Bitmap progressMark, progressMarkPressed;
private RectF rect = new RectF();
{
circleColor = new Paint();
innerColor = new Paint();
circleRing = new Paint();
circleColor.setColor(Color.parseColor("#ff33b5e5")); // Set default
// progress
// color to holo
// blue.
innerColor.setColor(Color.BLACK); // Set default background color to
// black
circleRing.setColor(Color.GRAY);// Set default background color to Gray
circleColor.setAntiAlias(true);
innerColor.setAntiAlias(true);
circleRing.setAntiAlias(true);
circleColor.setStrokeWidth(25);
innerColor.setStrokeWidth(15);
circleRing.setStrokeWidth(10);
circleColor.setStyle(Paint.Style.FILL);
}
public CircularProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
}
public CircularProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
public CircularProgressBar(Context context) {
super(context);
mContext = context;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getWidth(); // Get View Width
height = getHeight();// Get View Height
int size = (width > height) ? height : width; // Choose the smaller
// between width and
// height to make a
// square
cx = width / 2; // Center X for circle
cy = height / 2; // Center Y for circle
outerRadius = size / 2; // Radius of the outer circle
innerRadius = outerRadius - barWidth; // Radius of the inner circle
left = cx - outerRadius; // Calculate left bound of our rect
right = cx + outerRadius;// Calculate right bound of our rect
top = cy - outerRadius;// Calculate top bound of our rect
bottom = cy + outerRadius;// Calculate bottom bound of our rect
startPointX = cx; // 12 O'clock X coordinate
startPointY = cy - outerRadius;// 12 O'clock Y coordinate
markPointX = startPointX;// Initial locatino of the marker X coordinate
markPointY = startPointY;// Initial locatino of the marker Y coordinate
rect.set(left, top, right, bottom); // assign size to rect
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawCircle(cx, cy, outerRadius, circleRing);
canvas.drawArc(rect, startAngle, angle, true, circleColor);
canvas.drawCircle(cx, cy, innerRadius, innerColor);
super.onDraw(canvas);
}
public int getAngle() {
return angle;
}
public void setAngle(int angle) {
//System.out.println("Angel "+angle);
this.angle = angle;
float donePercent = (((float) this.angle) / 360) * 100;
float progress = (donePercent / 100) * getMaxProgress();
setProgressPercent(Math.round(donePercent));
setProgress(Math.round(progress));
}
public int getBarWidth() {
return barWidth;
}
public void setBarWidth(int barWidth) {
this.barWidth = barWidth;
}
public int getMaxProgress() {
return maxProgress;
}
public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress;
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
if (this.progress != progress) {
this.progress = progress;
int newPercent = (this.progress * 100) / this.maxProgress;
int newAngle = (newPercent * 360) / 100;
this.setAngle(newAngle);
this.setProgressPercent(newPercent);
}
}
long mAnimStartTime;
Handler mHandler = new Handler();
Runnable mTick = new Runnable() {
public void run() {
invalidate();
update();
mHandler.postDelayed(this, 100); // 20ms == 60fps
}
};
public void update(){
if(IS_ACTIVE){
setProgress(tmpProgress);
if(tmpProgress>maxProgress){
stopAnimation();
}
tmpProgress++;
}
}
boolean IS_ACTIVE=false;
public void startAnimation() {
IS_ACTIVE=true;
tmpProgress=1;
mHandler.removeCallbacks(mTick);
mHandler.post(mTick);
}
public void stopAnimation() {
IS_ACTIVE=false;
progress=1;
mHandler.removeCallbacks(mTick);
}
public int getProgressPercent() {
return progressPercent;
}
public void setProgressPercent(int progressPercent) {
this.progressPercent = progressPercent;
}
public void setRingBackgroundColor(int color) {
circleRing.setColor(color);
}
public void setBackGroundColor(int color) {
innerColor.setColor(color);
}
public void setProgressColor(int color) {
circleColor.setColor(color);
}
public float getAdjustmentFactor() {
return adjustmentFactor;
}
public void setAdjustmentFactor(float adjustmentFactor) {
this.adjustmentFactor = adjustmentFactor;
}
}
I would like to draw an image as below using android drawable something as below:
I could do the arrow but when I add circle, the image gets distorted.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape>
<solid android:color="#android:color/transparent"/>
<size android:width="2dp" android:height="50dp"/>
</shape>
</item>
<item android:bottom="20dp">
<rotate
android:fromDegrees="45"
android:toDegrees="45">
<shape android:shape="rectangle">
<solid android:color="#F0AD4E"/>
<corners
android:radius="0dp"
android:bottomRightRadius="0dp"
android:bottomLeftRadius="0dp"/>
</shape>
</rotate>
</item>
<item android:top="20dp">
<rotate
android:fromDegrees="-45"
android:toDegrees="45">
<shape android:shape="rectangle">
<solid android:color="#F0AD4E"/>
<corners
android:radius="0dp"
android:topRightRadius="0dp"
android:topLeftRadius="0dp"/>
</shape>
</rotate>
</item>
Can anyone help me to complete the picture.
Thanks.
Don't use drawables for this, use a custom view and canvas.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Bojan on 17.5.2015.
*/
public class CircleRightArrow extends View {
private int circleColor = 0xFF505070;
private int arrowColor = 0xFF505070;
private int measuredSize;
private int strokeWidth;
private Paint mCirclePiant, mArrowPaint;
public CircleRightArrow(Context context) {
super(context);
init(context, null, 0);
}
public CircleRightArrow(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public CircleRightArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attributeSet, int defStyle) {
mCirclePiant = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePiant.setColor(circleColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mArrowPaint.setColor(arrowColor);
mArrowPaint.setStyle(Paint.Style.STROKE);
mArrowPaint.setStrokeCap(Paint.Cap.ROUND);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
int measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
measuredSize = Math.min(measuredHeight, measuredWidth);
strokeWidth = Math.round(measuredSize * 0.05f);
mCirclePiant.setStrokeWidth(strokeWidth);
mArrowPaint.setStrokeWidth(strokeWidth);
// Make a square
setMeasuredDimension(measuredSize + strokeWidth, measuredSize + strokeWidth);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (measuredSize <= 0) {
// Not much we can draw, can we
return;
}
float center = (measuredSize + strokeWidth) * 0.5f;
canvas.drawCircle(center, center, measuredSize * 0.5f, mCirclePiant);
canvas.drawLine(center + 0.2f * measuredSize, center, center - 0.1f * measuredSize, center + 0.2f * measuredSize, mArrowPaint);
canvas.drawLine(center + 0.2f * measuredSize, center, center - 0.1f * measuredSize, center - 0.2f * measuredSize, mArrowPaint);
}
}
Result of 50x50dp at 1920x1080p
Following Bojan's answer, I edited the view to redraw based on the touch events and added the OnClickListener part. Feel free to suggest any improvements.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by noorul.ahmed on 5/18/2015.
*/
public class CircleRightArrow extends View {
private int viewColor;
private int backgroundColor;
private int normalColor = 0xFFF0AD4E;
private int normalBackground = 0xFFFFFFFF;
private int touchBackground = 0xFFEC971F;
private int touchColor = 0xFFFFFFFF;
private int disabledBackground = 0xFFFFFFFF;
private int disabledColor = 0xFFCECECE;
private int measuredSize;
private int strokeWidth;
private boolean touched = false;
private float startX, startY, endX, endY;
private OnClickListener listener;
private Paint mCirclePiant, mArrowPaint;
public CircleRightArrow(Context context) {
super(context);
init(context, null, 0);
setInitialColor();
}
public CircleRightArrow(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
setInitialColor();
}
public CircleRightArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
setInitialColor();
}
private void init(Context context, AttributeSet attributeSet, int defStyle) {
mCirclePiant = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePiant.setColor(viewColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mArrowPaint.setColor(viewColor);
mArrowPaint.setStyle(Paint.Style.STROKE);
mArrowPaint.setStrokeCap(Paint.Cap.ROUND);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
int measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
measuredSize = Math.min(measuredHeight, measuredWidth);
strokeWidth = Math.round(measuredSize * 0.025f);
mCirclePiant.setStrokeWidth(strokeWidth);
mArrowPaint.setStrokeWidth(strokeWidth);
// Make a square
setMeasuredDimension(measuredSize + strokeWidth, measuredSize + strokeWidth);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (measuredSize <= 0) {
// Not much we can draw, can we
return;
}
float center = (measuredSize + strokeWidth) * 0.5f;
canvas.drawCircle(center, center, measuredSize * 0.5f, mCirclePiant);
canvas.drawLine(center + 0.2f * measuredSize, center, center - 0.1f * measuredSize, center + 0.2f * measuredSize, mArrowPaint);
canvas.drawLine(center + 0.2f * measuredSize, center, center - 0.1f * measuredSize, center - 0.2f * measuredSize, mArrowPaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN :
touched = true;
startX = event.getX();
startY = event.getY();
togglePaintColor(event);
postInvalidate();
return true;
case MotionEvent.ACTION_UP :
endX = event.getX();
endY = event.getY();
togglePaintColor(event);
postInvalidate();
float diffX = Math.abs(startX - endX);
float diffY = Math.abs(startY - endY);
if (diffX <= 5 && diffY <= 5 && touched ) {
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,1));
}
return true;
default:
return false;
}
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_UP) {
touched = false;
if(listener != null) listener.onClick(this);
}
return super.dispatchKeyEvent(event);
}
public void setListener(OnClickListener listener) {
this.listener = listener;
}
public void setInitialColor(){
if (isClickable()){
viewColor = normalColor;
backgroundColor = normalBackground;
}
else {
viewColor = disabledColor;
backgroundColor = disabledBackground;
this.listener = null;
}
mCirclePiant.setColor(viewColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint.setColor(viewColor);
}
#Override
public void setClickable(boolean isClickable){
super.setClickable(isClickable);
if (isClickable){
viewColor = normalColor;
backgroundColor = normalBackground;
}
else {
viewColor = disabledColor;
backgroundColor = disabledBackground;
this.listener = null;
}
mCirclePiant.setColor(viewColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint.setColor(viewColor);
postInvalidate();
}
private void togglePaintColor(MotionEvent event) {
if (isClickable()) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
viewColor = touchColor;
backgroundColor = touchBackground;
mCirclePiant.setColor(backgroundColor);
mCirclePiant.setStyle(Paint.Style.FILL);
mArrowPaint.setColor(viewColor);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
viewColor = normalColor;
backgroundColor = normalBackground;
mCirclePiant.setColor(viewColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint.setColor(viewColor);
}
}
}
}
The three button states are:
Normal State
Pressed State
Disabled state