I know how to make rectangle with rounded corners, like this
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="#FF0000"/>
<corners
android:radius="10000dp" />
</shape>
It looks like this:
But I want to make the inverse of that (center transparent and sides filled with color), which should look like this:
Thanks
Not proper way, but will get the result, try
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<padding
android:bottom="-100dp"
android:left="-100dp"
android:right="-100dp"
android:top="-100dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<corners android:radius="200dp" />
<stroke
android:width="100dp"
android:color="#FF0000" />
</shape>
</item>
</layer-list>
Based on: https://stackoverflow.com/a/36764393/1268507
Try with custom view:
public class CustomView extends View {
private Path mPath = new Path();
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
mPath.addRoundRect(0, 0, getWidth(), getHeight(), 1000, 1000, Path.Direction.CW);
mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
canvas.clipPath(mPath);
canvas.drawColor(Color.parseColor("#FF0000"));
}
}
Check this
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:left="5dp"
android:top="5dp"
android:right="5dp"
android:bottom="5dp">
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="#FF0000"/>
</shape>
</item>
<item
android:left="5dp"
android:top="5dp"
android:right="5dp"
android:bottom="5dp">
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="#FFFFFF"/>
<corners
android:radius="10000dp" />
</shape>
</item>
</layer-list>
I needed this as well so I created following Drawable class. To get the solution that is wanted for this question you can use it like following:
val radius = min(view.width, view.height)
val bg = InvertedRectDrawable(Color.RED, radius, 0)
view.background = bg
Code
import android.graphics.*
import android.graphics.drawable.Drawable
class InvertedRectDrawable(
private val color: Int,
private val cornerRadius: Int,
private val border: Int,
private val contentColor: Int? = null
) : Drawable() {
private var paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
this.color = this#InvertedRectDrawable.color
}
private var paint2: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
contentColor?.let {
this.color = it
}
}
private val path = Path()
override fun draw(canvas: Canvas) {
path.reset()
path.addRoundRect(
border.toFloat(),
border.toFloat(),
bounds.width().toFloat() - 2 * border.toFloat(),
bounds.height().toFloat() - 2 * border.toFloat(),
cornerRadius.toFloat(),
cornerRadius.toFloat(),
Path.Direction.CW
)
path.fillType = Path.FillType.INVERSE_EVEN_ODD
val s = canvas.save()
canvas.clipPath(path)
canvas.drawPaint(paint)
contentColor?.let {
canvas.restoreToCount(s)
path.fillType = Path.FillType.EVEN_ODD
canvas.clipPath(path)
canvas.drawPaint(paint2)
}
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
}
override fun getOpacity(): Int {
return PixelFormat.TRANSLUCENT
}
}
Related
I have a custom progress bar that looks like this:
Here's the .xml code I've used to create it:
background_drawable.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="#dimen/progress_bar_radial_inner_radius"
android:thickness="#dimen/progress_bar_radial_thickness"
android:shape="ring"
android:useLevel="false" >
<solid android:color="#color/main_color_alpha"/>
</shape>
progress_drawable.xml
<?xml version="1.0" encoding="UTF-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="270"
android:toDegrees="270">
<shape
android:innerRadius="#dimen/progress_bar_radial_inner_radius"
android:thickness="#dimen/progress_bar_radial_thickness"
android:shape="ring" >
<solid android:color="#color/main_color"/>
</shape>
</rotate>
What I want to get is a round corners for the ring that I use to show progress. Something that would look like this:
Does someone has any idea on how this can be achieved?
I was able to achieve this using a layer list, and adding a dot to each side of the line. The first one will be stuck at the top, while the second one will follow the progress as an inset is added inside a rotate element. You will have to adjust it according to the size of your progressbar layout though. Mine is 250dp x 250dp.
progress_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate android:fromDegrees="270" android:toDegrees="270">
<shape
android:innerRadiusRatio="2.55"
android:shape="ring"
android:thickness="15dp"
android:useLevel="true">
<solid android:color="#color/main_color" />
</shape>
</rotate>
</item>
<item android:bottom="211dp">
<shape
android:innerRadiusRatio="1000"
android:shape="ring"
android:thickness="7dp"
android:useLevel="false">
<solid android:color="#color/main_color" />
</shape>
</item>
<item>
<rotate>
<inset android:insetBottom="211dp">
<shape
android:innerRadiusRatio="1000"
android:shape="ring"
android:thickness="7dp"
android:useLevel="false">
<solid android:color="#color/main_color" />
</shape>
</inset>
</rotate>
</item>
</layer-list>
Well, I searched a lot about that before.
The only solution I found was a library on GitHub check here
Go through this code hope this will help you
>ProgressBarActivity.java
public class ProgressBarActivity extends AppCompatActivity {
private TextView txtProgress;
private ProgressBar progressBar;
private int pStatus = 0;
private Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_progress);
txtProgress = (TextView) findViewById(R.id.txtProgress);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
new Thread(new Runnable() {
#Override
public void run() {
while (pStatus <= 100) {
handler.post(new Runnable() {
#Override
public void run() {
progressBar.setProgress(pStatus);
txtProgress.setText(pStatus + " %");
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
pStatus++;
}
}
}).start();
}
}
> activity_progress.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.placio.android.custom_progressbar_circular.MainActivity" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_centerInParent="true"
android:layout_height="wrap_content">
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerInParent="true"
android:indeterminate="false"
android:max="100"
android:progress="0"
android:progressDrawable="#drawable/progress_drawable"
android:secondaryProgress="0" />
<TextView
android:id="#+id/txtProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/progressBar"
android:layout_centerInParent="true"
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</RelativeLayout>
> progress_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="-90"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="270" >
Ok, the easiest way I've found to do what I want is to draw progress arc on canvas instead of using progress_drawable.xml.
Here's my code in case someone has similar issue.
class RadialProgressBar : ProgressBar {
private val thickness = 28f
private val halfThickness = thickness / 2
private val startAngle = 270f
private var boundsF: RectF? = null
private lateinit var paint: Paint
constructor(context: Context?) : super(context) {
init()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}
private fun init() {
paint = Paint()
paint.isAntiAlias = true
paint.style = Paint.Style.STROKE
paint.strokeWidth = thickness
paint.strokeCap = Paint.Cap.ROUND
paint.color = ContextCompat.getColor(context, R.color.main_color)
progressDrawable = null
}
override fun draw(canvas: Canvas?) {
super.draw(canvas)
if (boundsF == null) {
boundsF = RectF(background.bounds)
boundsF?.inset(halfThickness, halfThickness)
}
canvas?.drawArc(boundsF, startAngle, progress * 3.60f, false, paint)
}
}
I created an Android gradient drawable where the top and bottom are black and the center is transparent:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:startColor="#android:color/black"
android:centerColor="#android:color/transparent"
android:endColor="#android:color/black"
android:angle="90"/>
</shape>
The rendered gradient looks like this:
As you can see, the black parts spread to most of the screen. I want the black to show only on a small portion of the top and bottom. Is there a way I can make the transparent center larger, or make the top and bottom black stripes smaller?
I tried playing around with some of the other XML attributes mentioned in the linked GradientDrawable documentation, yet none of them seem to make and difference.
For an XML only solution, you can create a layer-list with two separate gradient objects.
The following code creates two overlapping gradient objects and uses centerY with centerColor to offset the black section. Here, the centerY attributes are set to 0.9 and 0.1, so the black color is restricted to the top and bottom 10% of the view height.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient
android:angle="90"
android:centerColor="#android:color/transparent"
android:centerY="0.9"
android:endColor="#android:color/black"
android:startColor="#android:color/transparent" />
</shape>
</item>
<item>
<shape>
<gradient
android:angle="90"
android:centerColor="#android:color/transparent"
android:centerY="0.1"
android:endColor="#android:color/transparent"
android:startColor="#android:color/black" />
</shape>
</item>
</layer-list>
For API level 23 or higher, the following solution will also work, using android:height. This solution can work even if you don't know the total height of your view, as long as you know how large you want the gradient to be.
This code creates two separate gradients, each with a height of 60sp, and then uses android:gravity to float the gradients to the top and bottom of the view.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:height="60sp"
android:gravity="top">
<shape android:shape="rectangle">
<gradient
android:angle="90"
android:endColor="#android:color/black"
android:startColor="#android:color/transparent" />
</shape>
</item>
<item
android:height="65sp"
android:gravity="bottom">
<shape android:shape="rectangle">
<gradient
android:angle="90"
android:endColor="#android:color/transparent"
android:startColor="#android:color/black" />
</shape>
</item>
</layer-list>
Thank you #Luksprog for the code help, and #thenaoh for the start of the idea.
The above solutions work and it is nice that they are pure XML. If your gradient is showing with stripes, you may want to try a programmatic solution, like shown in #lelloman's answer, to create a smoother gradient.
Here is how it could be done with a custom Drawable. You can tune the LinearGradient as you want, and then set it as the view's background with view.setBackground(new CustomDrawable());.
public class CustomDrawable extends Drawable {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int[] colors;
private float[] positions;
public CustomDrawable() {
paint.setStyle(Paint.Style.FILL);
this.colors = new int[]{0xff000000, 0xffaaaaaa, 0xffffffff, 0xffaaaaaa, 0xff000000};
this.positions = new float[]{.0f, .2f, .5f, .8f, 1.f};
}
#Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
LinearGradient linearGradient = new LinearGradient(left, top,left, bottom, colors, positions, Shader.TileMode.CLAMP);
paint.setShader(linearGradient);
}
#Override
public void draw(#NonNull Canvas canvas) {
canvas.drawRect(getBounds(), paint);
}
#Override
public void setAlpha(#IntRange(from = 0, to = 255) int alpha) {
paint.setAlpha(alpha);
}
#Override
public void setColorFilter(#Nullable ColorFilter colorFilter) {
paint.setColorFilter(colorFilter);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
}
There is a solution, assuming that you know in advance the height of your view (let's say here 60dp):
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="40dp">
<shape android:shape="rectangle">
<gradient
android:type="linear"
android:angle="90"
android:startColor="#FFFFFF"
android:endColor="#000000"/>
</shape>
</item>
<item
android:top="20dp"
android:bottom="20dp"
android:gravity="center_vertical">
<shape android:shape="rectangle">
<solid android:color="#FFFFFF"/>
</shape>
</item>
<item
android:top="40dp"
android:gravity="bottom">
<shape android:shape="rectangle">
<gradient
android:type="linear"
android:angle="90"
android:startColor="#000000"
android:endColor="#FFFFFF"/>
</shape>
</item>
</layer-list>
But if you don't know the height in advance, another solution would be to make your own custom view, like this:
public class MyView extends ImageView
{
private Paint paint = null;
public MyView(Context context, AttributeSet attrs)
{
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint.setShader(getLinearGradient(0, getHeight()));
canvas.drawPaint(paint);
}
private LinearGradient getLinearGradient(float y0, float y1)
{
// colors :
int[] listeColors = new int[3];
listeColors[0] = 0xFF000000;
listeColors[1] = 0xFFFFFFFF;
listeColors[2] = 0xFFFFFFFF;
// positions :
float[] listPositions = new float[3];
listPositions[0] = 0;
listPositions[1] = 0.25F;
listPositions[2] = 1;
// gradient :
return new LinearGradient(0, y0, 0, y0 + (y1 - y0) / 2, listeColors, listPositions, Shader.TileMode.MIRROR);
}
}
Hope it helps.
I tried to draw a custom linear layout, But the problem I faced is I am not getting the round corner for the linear layout
public class RoundLinearLayout extends LinearLayout {
private float radius;
private Path path = new Path();
private RectF rect = new RectF();
public RoundLinearLayout(Context context)
{
super(context);
radius = 20;
// setWillNotDraw(false);
}
public RoundLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// init(context);
}
public RoundLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// init(context);
}
#Override
protected void onDraw(Canvas canvas) {
path.reset();
rect.set(0, 0, canvas.getWidth(), canvas.getHeight());
path.addRoundRect(rect, radius, radius, Path.Direction.CCW);
// Add 1px border RED here ?
path.close();
canvas.clipPath(path);
}
}
I really donno what went wrong.. Some please help me to sort this out.
I will suggest you to use simple CardView
use compile dependency
compile 'com.android.support:cardview-v7:21.0.+'
Example
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
.....
your other child layout goes here
</LinearLayout>
</android.support.v7.widget.CardView>
Use below xml in Drawables.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFF" />
<stroke
android:width="2dp"
android:color="#000" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
<corners
android:bottomLeftRadius="8dp"
android:bottomRightRadius="8dp"
android:radius="8dp"
android:topLeftRadius="8dp"
android:topRightRadius="8dp" />
</shape>
and set it as a Background of your LinearLayout.
You can choose your color and set background to your linear layout.
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="4dp" />
<solid android:color="#80000000" />
</shape>
I'm trying to make a circular progress bar on android and it seems pretty straightforward task , but I'm struggling with rounding the edges of the progress and secondary progress.
Is there a way to do that without making a custom view ? Using a corners radius ? or nine patch drawable ?
For this view (see attachement) I'm using a simple xml file
<item android:id="#android:id/progress">
<shape
android:useLevel="true"
android:innerRadius="#dimen/sixty_dp"
android:shape="ring"
android:thickness="#dimen/seven_dp">
<solid android:color="#477C5B"/>
<stroke android:width="1dip"
android:color="#FFFF"/>
</shape>
</item>
Just create class called MyProgress in your package .. and paste the following code..
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
public class MyProgress extends View {
private Paint mPrimaryPaint;
private Paint mSecondaryPaint;
private RectF mRectF;
private TextPaint mTextPaint;
private Paint mBackgroundPaint;
private boolean mDrawText = false;
private int mSecondaryProgressColor;
private int mPrimaryProgressColor;
private int mBackgroundColor;
private int mStrokeWidth;
private int mProgress;
private int mSecodaryProgress;
private int mTextColor;
private int mPrimaryCapSize;
private int mSecondaryCapSize;
private boolean mIsPrimaryCapVisible;
private boolean mIsSecondaryCapVisible;
private int x;
private int y;
private int mWidth = 0, mHeight = 0;
public MyProgress(Context context) {
super(context);
init(context, null);
}
public MyProgress(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MyProgress(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
void init(Context context, AttributeSet attrs) {
TypedArray a;
if (attrs != null) {
a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.MyProgress,
0, 0);
} else {
throw new IllegalArgumentException("Must have to pass the attributes");
}
try {
mDrawText = a.getBoolean(R.styleable.MyProgress_showProgressText, false);
mBackgroundColor = a.getColor(R.styleable.MyProgress_backgroundColor, android.R.color.darker_gray);
mPrimaryProgressColor = a.getColor(R.styleable.MyProgress_progressColor, android.R.color.darker_gray);
mSecondaryProgressColor = a.getColor(R.styleable.MyProgress_secondaryProgressColor, android.R.color.black);
mProgress = a.getInt(R.styleable.MyProgress_progress, 0);
mSecodaryProgress = a.getInt(R.styleable.MyProgress_secondaryProgress, 0);
mStrokeWidth = a.getDimensionPixelSize(R.styleable.MyProgress_strokeWidth, 20);
mTextColor = a.getColor(R.styleable.MyProgress_textColor, android.R.color.black);
mPrimaryCapSize = a.getInt(R.styleable.MyProgress_primaryCapSize, 20);
mSecondaryCapSize = a.getInt(R.styleable.MyProgress_secodaryCapSize, 20);
mIsPrimaryCapVisible = a.getBoolean(R.styleable.MyProgress_primaryCapVisibility, true);
mIsSecondaryCapVisible = a.getBoolean(R.styleable.MyProgress_secodaryCapVisibility, true);
} finally {
a.recycle();
}
mBackgroundPaint = new Paint();
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaint.setStyle(Paint.Style.STROKE);
mBackgroundPaint.setStrokeWidth(mStrokeWidth);
mBackgroundPaint.setColor(mBackgroundColor);
mPrimaryPaint = new Paint();
mPrimaryPaint.setAntiAlias(true);
mPrimaryPaint.setStyle(Paint.Style.STROKE);
mPrimaryPaint.setStrokeWidth(mStrokeWidth);
mPrimaryPaint.setColor(mPrimaryProgressColor);
mSecondaryPaint = new Paint();
mSecondaryPaint.setAntiAlias(true);
mSecondaryPaint.setStyle(Paint.Style.STROKE);
mSecondaryPaint.setStrokeWidth(mStrokeWidth - 2);
mSecondaryPaint.setColor(mSecondaryProgressColor);
mTextPaint = new TextPaint();
mTextPaint.setColor(mTextColor);
mRectF = new RectF();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRectF.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - getPaddingBottom());
mTextPaint.setTextSize(w / 5);
x = (w / 2) - ((int) (mTextPaint.measureText(mProgress + "%") / 2));
y = (int) ((h / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
mWidth = w;
mHeight = h;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPrimaryPaint.setStyle(Paint.Style.STROKE);
mSecondaryPaint.setStyle(Paint.Style.STROKE);
// for drawing a full progress .. The background circle
canvas.drawArc(mRectF, 0, 360, false, mBackgroundPaint);
// for drawing a secondary progress circle
int secondarySwipeangle = (mSecodaryProgress * 360) / 100;
canvas.drawArc(mRectF, 270, secondarySwipeangle, false, mSecondaryPaint);
// for drawing a main progress circle
int primarySwipeangle = (mProgress * 360) / 100;
canvas.drawArc(mRectF, 270, primarySwipeangle, false, mPrimaryPaint);
// for cap of secondary progress
int r = (getHeight() - getPaddingLeft() * 2) / 2; // Calculated from canvas width
double trad = (secondarySwipeangle - 90) * (Math.PI / 180d); // = 5.1051
int x = (int) (r * Math.cos(trad));
int y = (int) (r * Math.sin(trad));
mSecondaryPaint.setStyle(Paint.Style.FILL);
if (mIsSecondaryCapVisible)
canvas.drawCircle(x + (mWidth / 2), y + (mHeight / 2), mSecondaryCapSize, mSecondaryPaint);
// for cap of primary progress
trad = (primarySwipeangle - 90) * (Math.PI / 180d); // = 5.1051
x = (int) (r * Math.cos(trad));
y = (int) (r * Math.sin(trad));
mPrimaryPaint.setStyle(Paint.Style.FILL);
if (mIsPrimaryCapVisible)
canvas.drawCircle(x + (mWidth / 2), y + (mHeight / 2), mPrimaryCapSize, mPrimaryPaint);
if (mDrawText)
canvas.drawText(mProgress + "%", x, y, mTextPaint);
}
public void setDrawText(boolean mDrawText) {
this.mDrawText = mDrawText;
invalidate();
}
public void setBackgroundColor(int mBackgroundColor) {
this.mBackgroundColor = mBackgroundColor;
invalidate();
}
public void setSecondaryProgressColor(int mSecondaryProgressColor) {
this.mSecondaryProgressColor = mSecondaryProgressColor;
invalidate();
}
public void setPrimaryProgressColor(int mPrimaryProgressColor) {
this.mPrimaryProgressColor = mPrimaryProgressColor;
invalidate();
}
public void setStrokeWidth(int mStrokeWidth) {
this.mStrokeWidth = mStrokeWidth;
invalidate();
}
public void setProgress(int mProgress) {
this.mProgress = mProgress;
invalidate();
}
public void setSecondaryProgress(int mSecondaryProgress) {
this.mSecodaryProgress = mSecondaryProgress;
invalidate();
}
public void setTextColor(int mTextColor) {
this.mTextColor = mTextColor;
invalidate();
}
public void setPrimaryCapSize(int mPrimaryCapSize) {
this.mPrimaryCapSize = mPrimaryCapSize;
invalidate();
}
public void setSecondaryCapSize(int mSecondaryCapSize) {
this.mSecondaryCapSize = mSecondaryCapSize;
invalidate();
}
public boolean isPrimaryCapVisible() {
return mIsPrimaryCapVisible;
}
public void setIsPrimaryCapVisible(boolean mIsPrimaryCapVisible) {
this.mIsPrimaryCapVisible = mIsPrimaryCapVisible;
}
public boolean isSecondaryCapVisible() {
return mIsSecondaryCapVisible;
}
public void setIsSecondaryCapVisible(boolean mIsSecondaryCapVisible) {
this.mIsSecondaryCapVisible = mIsSecondaryCapVisible;
}
public int getSecondaryProgressColor() {
return mSecondaryProgressColor;
}
public int getPrimaryProgressColor() {
return mPrimaryProgressColor;
}
public int getProgress() {
return mProgress;
}
public int getBackgroundColor() {
return mBackgroundColor;
}
public int getSecodaryProgress() {
return mSecodaryProgress;
}
public int getPrimaryCapSize() {
return mPrimaryCapSize;
}
public int getSecondaryCapSize() {
return mSecondaryCapSize;
}
}
and add the following line in res->values->attr.xml under a tag and build it
<declare-styleable name="MyProgress">
<attr name="showProgressText" format="boolean" />
<attr name="progress" format="integer" />
<attr name="secondaryProgress" format="integer" />
<attr name="progressColor" format="color" />
<attr name="secondaryProgressColor" format="color" />
<attr name="backgroundColor" format="color" />
<attr name="primaryCapSize" format="integer" />
<attr name="secodaryCapSize" format="integer" />
<attr name="primaryCapVisibility" format="boolean" />
<attr name="secodaryCapVisibility" format="boolean" />
<attr name="strokeWidth" format="dimension" />
<attr name="textColor" format="color" />
</declare-styleable>
that's it ....
and to use in your layout ..
<Your_Package_Name.MyProgress
android:padding="20dp"
android:id="#+id/timer1"
app:strokeWidth="10dp"
app:progress="30"
app:secondaryProgress="50"
app:backgroundColor="#android:color/black"
app:progressColor="#android:color/holo_blue_bright"
app:secondaryProgressColor="#android:color/holo_blue_dark"
app:primaryCapSize="30"
app:secodaryCapSize="40"
app:primaryCapVisibility="true"
app:secodaryCapVisibility="true"
android:layout_width="200dp"
android:layout_height="200dp" />
You can also change all property progrmatically using setMethods()...
feel free to ask anything ..
best of luck
[Update 23-01-2016]
finally I uploaded code on github.
You can refer it from here
https://github.com/msquare097/MProgressBar
Now You can use this ProgressBar by simply writing following line in your app build.gradle file. You don't have to copy above code.
compile 'com.msquare.widget.mprogressbar:mprogressbar:1.0.0'
I was able to achieve this using a layer list, and adding a dot to each side of the line. The first one will be stuck at the top, while the second one will follow the progress as an inset is added inside a rotate element. You will have to adjust it according to the size of your progressbar layout though. Mine is 250dp x 250dp.
progress_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate android:fromDegrees="270" android:toDegrees="270">
<shape
android:innerRadiusRatio="2.55"
android:shape="ring"
android:thickness="15dp"
android:useLevel="true">
<solid android:color="#color/main_color" />
</shape>
</rotate>
</item>
<item android:bottom="211dp">
<shape
android:innerRadiusRatio="1000"
android:shape="ring"
android:thickness="7dp"
android:useLevel="false">
<solid android:color="#color/main_color" />
</shape>
</item>
<item>
<rotate>
<inset android:insetBottom="211dp">
<shape
android:innerRadiusRatio="1000"
android:shape="ring"
android:thickness="7dp"
android:useLevel="false">
<solid android:color="#color/main_color" />
</shape>
</inset>
</rotate>
</item>
</layer-list>
I don't have a secondary process here, but you should be able to adjust it for that as well.
A simple and efficient class extending View to draw circular progress, with rounded corners as an option. Progress color, background color, stroke width are also customizable. As seen in my other answer.
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.View
import androidx.annotation.FloatRange
class CircularProgressView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
private val progressPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
}
private val backgroundPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
}
private val rect = RectF()
private val startAngle = -90f
private val maxAngle = 360f
private val maxProgress = 100
private var diameter = 0f
private var angle = 0f
override fun onDraw(canvas: Canvas) {
drawCircle(maxAngle, canvas, backgroundPaint)
drawCircle(angle, canvas, progressPaint)
}
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
diameter = Math.min(width, height).toFloat()
updateRect()
}
private fun updateRect() {
val strokeWidth = backgroundPaint.strokeWidth
rect.set(strokeWidth, strokeWidth, diameter - strokeWidth, diameter - strokeWidth)
}
private fun drawCircle(angle: Float, canvas: Canvas, paint: Paint) {
canvas.drawArc(rect, startAngle, angle, false, paint)
}
private fun calculateAngle(progress: Float) = maxAngle / maxProgress * progress
fun setProgress(#FloatRange(from = 0.0, to = 100.0) progress: Float) {
angle = calculateAngle(progress)
invalidate()
}
fun setProgressColor(color: Int) {
progressPaint.color = color
invalidate()
}
fun setProgressBackgroundColor(color: Int) {
backgroundPaint.color = color
invalidate()
}
fun setProgressWidth(width: Float) {
progressPaint.strokeWidth = width
backgroundPaint.strokeWidth = width
updateRect()
invalidate()
}
fun setRounded(rounded: Boolean) {
progressPaint.strokeCap = if (rounded) Paint.Cap.ROUND else Paint.Cap.BUTT
invalidate()
}
}
you can add a circle/oval shape on the end & begin side of the ring, just like the below code
a layer-list drawable contain two circle/oval shape drawable and a ring shape drawable
round_progress_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="294px"
android:left="540px"
android:right="48px"
android:top="294px">
<shape
android:innerRadius="6px"
android:shape="oval">
<solid android:color="#FFFFFF"/>
</shape>
</item>
<item>
<shape
android:innerRadius="240px"
android:shape="ring"
android:thickness="12px">
<size
android:width="600px"
android:height="600px"/>
<solid android:color="#FFFFFF"/>
</shape>
</item>
<item>
<rotate>
<layer-list>
<item
android:bottom="294px"
android:left="540px"
android:right="48px"
android:top="294px">
<shape
android:innerRadius="6px"
android:shape="oval">
<solid android:color="#FFFFFF"/>
</shape>
</item>
</layer-list>
</rotate>
</item>
</layer-list>
round_progress_animation_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="true">
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="5000"
android:propertyName="ImageLevel"
android:repeatCount="infinite"
android:valueFrom="0"
android:valueTo="10000"
android:valueType="intType">
</objectAnimator>
</item>
<item>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="0"
android:propertyName="ImageLevel"
android:valueFrom="0"
android:valueTo="0"
android:valueType="intType">
</objectAnimator>
</item>
</selector>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimaryDark"
tools:context=".MainActivity">
<ImageView
android:stateListAnimator="#animator/round_progress_animation_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/round_progress_drawable" />
</FrameLayout>
I tried to tackle this for arbitrary size progress bars and if you can ensure the progress bar is a square and you don't use margins, this should work but it doesn't. Android 10 renders it sometimes correctly sometime it doesn't. Especially progress values > 50% tend to break badly. It is not a working solution but might give someone an idea. There are still issues with the gradient fill. The end color doesn't match it. But if you use solid colors, it should work fine. Also the rounded cap causes the bar to show larger value range. My idea was to replace the ring with a shape that is 5dp x 2.5dp rectangle in background color with clipped half a circle. You would need to use different rotations for the start and end:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate
android:fromDegrees="270"
android:toDegrees="270">
<shape
android:innerRadiusRatio="2.4"
android:shape="ring"
android:thickness="5dp"
android:useLevel="true"><!-- this line fixes the issue for lollipop api 21 -->
<gradient
android:angle="0"
android:endColor="#color/yellow"
android:startColor="#color/orange"
android:type="sweep"
android:useLevel="false" />
</shape>
</rotate>
</item>
<item
android:gravity="top">
<shape
android:innerRadiusRatio="1000"
android:shape="ring"
android:thickness="2.5dp"
android:useLevel="false">
<solid android:color="#color/orange" />
<size android:height="5dp"/>
</shape>
</item>
<item>
<rotate>
<scale android:scaleGravity="top|center_horizontal"
android:scaleHeight="150%"
android:scaleWidth="150%">
<shape
android:innerRadiusRatio="1000"
android:shape="ring"
android:thickness="2.5dp"
android:useLevel="false">
<solid android:color="#color/yellow" />
</shape>
</scale>
</rotate>
</item>
</layer-list>
I'm trying to draw a border on a HorizontalScrollView programmatically, and fill the inside with a diferent colour. I've tried different aproaches, with no success. I can only draw one thing at a time... Here's the last code i've tried.
private void applyViewBorder(View layout, String borderColor,
String fillColor, int borderWidth) {
if (fillColor == null || borderColor == null)
return;
RectShape rect = new RectShape();
ShapeDrawable rectShapeDrawable = new ShapeDrawable(rect);
Paint paint = rectShapeDrawable.getPaint();
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(borderWidth);
paint.setColor(Color.parseColor(borderColor));
if (android.os.Build.VERSION.SDK_INT < 16) {
layout.setBackgroundDrawable(rectShapeDrawable);
} else {
layout.setBackground(rectShapeDrawable);
}
paint.setStyle(Style.FILL);
paint.setColor(Color.parseColor(fillColor));
if (android.os.Build.VERSION.SDK_INT < 16) {
layout.setBackgroundDrawable(rectShapeDrawable);
} else {
layout.setBackground(rectShapeDrawable);
}
}
I've already searched in stack overflow, with no success too...
Thanks in advance. :)
what you can try is
Create XML called border.xml in project drawable folder as below :
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#FF0000" />
</shape>
</item>
<item android:left="5dp" android:right="5dp" android:top="5dp" >
<shape android:shape="rectangle">
<solid android:color="#000000" />
</shape>
</item>
</layer-list>
and than
yourshorizontalscrollview.setBackgroundDrawable(getResources().getDrawable(R.drawable.border));
or use this one :
yourshorizontalscrollview.setBackground(getResources().getDrawable(R.drawable.border));