REQUIREMENT
How do I create a view looking like this.
I would like to draw a view on the screen which is a line broken into segments showing values percentage of the whole view. My requirements are
IDEAS/THINGS I HAVE TRIED
(1) Custom view rendering 3 rectangles side by side
I have tried a custom view which renders 3 rectangles side by side. But these obviously have square corners.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int viewHeight = 50;
canvas.drawrect(0, 0, 60, viewHeight, paint); // A
canvas.drawrect(60, 0, 120, viewHeight, paint); // B
canvas.drawrect(120,0, 180, viewHeight, paint); // C
}
(2) Shape with rounded corners
I know I can use a Shape to define a rectangle with rounded corners using the following, but this is a single color.
<shape xmlns:android="http://schemas.android.com/apk/res/android">
...
<corners
android:radius="4dp" />
....
</shape>
(3) Layer-list
From Android rectangle shape with two different color, I see I can use a layer-list to specify each item in the shape to have different colors.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#F86F05" />
</shape>
</item>
<item android:top="10dp">
<shape android:shape="rectangle">
<size
android:width="30dp"
android:height="30dp" />
<solid android:color="#B31F19" />
</shape>
</item>
</layer-list>
(4) Layer-list with corners??
Can I add the "corners" tag to the whole layer-list to get the rounded main corners? I assume not, and that the corners part has to be in the "Item"s shape tags.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<corners
android:radius="4dp" />
<item>
<shape android:shape="rectangle">
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#F86F05" />
</shape>
</item>
<item android:top="10dp">
<shape android:shape="rectangle">
<size
android:width="30dp"
android:height="30dp" />
<solid android:color="#B31F19" />
</shape>
</item>
</layer-list>
SUMMARY
This last one is getting closer to my requirement however
UPDATE: HOW DO I ADD ELEVATION/GREY BORDER
Thank you to "@0X0nosugar" for your solution. I am now wanting to add an elevation or a slight grey border as one of the colors is faily light and close to the background color. When I add the following I get a rectangular shadow which looks terrible with the curved corners.
android:elevation="2dp"
android:outlineProvider="bounds"
I would like it to appear like below
A rounded rectangle is the shape obtained by taking the convex hull of four equal circles of radius and placing their centers at the four corners of a rectangle with side lengths and . A filled rounded rectangle with (or. ) is called a stadium.
Rectangles with rounded corners usually have gaps between the columns of labels and a selvedge all the way around the sheet, which means that there is generally a wider range of sizes available as standard products.
Microsoft calls it a Round diagonal corner rectangle. Here are the names of various rectangles (one can confirm this for oneself by mouse-hovering over these shapes in e.g. Powerpoint or Word): Follow this answer to receive notifications. edited Sep 12, 2018 at 20:52.
You can create a custom View
which will draw rectangles on a clipped part of the Canvas
:
public class RoundedCornersSegmentedView extends View {
private Paint paintA, paintB, paintC;
private float cornerRadius;
private float measuredWidth, measuredHeight;
private RectF rect = new RectF(0, 0, 0,0);
private Path rectPath = new Path();
public RoundedCornersSegmentedView(Context context) {
super(context);
init();
}
public RoundedCornersSegmentedView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public RoundedCornersSegmentedView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setWillNotDraw(false);
// add this so Canvas.clipPath() will give the desired result also for devices running Api level lower than 17,
// see https://stackoverflow.com/a/30354461/5015207
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
paintA = new Paint(Paint.ANTI_ALIAS_FLAG);
paintA.setColor(Color.GREEN);
paintA.setStyle(Paint.Style.FILL);
paintB = new Paint(Paint.ANTI_ALIAS_FLAG);
paintB.setColor(Color.YELLOW);
paintB.setStyle(Paint.Style.FILL);
paintC = new Paint(Paint.ANTI_ALIAS_FLAG);
paintC.setColor(Color.MAGENTA);
paintC.setStyle(Paint.Style.FILL);
// with <dimen name="corner_radius">60dp</dimen> in res/values/dimens.xml
cornerRadius = getResources().getDimensionPixelSize(R.dimen.corner_radius);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
measuredWidth = right - left;
measuredHeight = bottom - top;
rect.set(0, 0, measuredWidth, measuredHeight);
rectPath.reset();
rectPath.addRoundRect(rect, cornerRadius, cornerRadius, Path.Direction.CW);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.clipPath(rectPath);
canvas.drawRect(0,0,measuredWidth/3f, measuredHeight, paintA);
canvas.drawRect(measuredWidth/3f,0,2 * measuredWidth/3f, measuredHeight, paintB);
canvas.drawRect(2 * measuredWidth/3f,0,measuredWidth, measuredHeight, paintC);
}
}
If you want to add some kind of semi-transparent edge, you can use a Paint
with a transparent color and fill type Paint.Style.STROKE
and draw a rounded rectangle.
Paint shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// material color "Blue Gray 400",
// see https://material.io/design/color/the-color-system.html
shadowPaint.setColor(Color.argb(30, 120, 144, 156));
shadowPaint.setStyle(Paint.Style.STROKE);
shadowPaint.setStrokeWidth(30);
The rectangle (instantiate outside of onLayout()
for better performance):
private RectF shadowRect = new RectF(0,0,0,0);
In onLayout()
:
int inset = 20;
shadowRect.set(inset, inset, measuredWidth - inset, measuredHeight - inset);
You should toggle the color/ the alpha value for the shadow Paint
as well as the values for stroke width and inset until you think it looks good.
Apply in onDraw()
after you've drawn the colored segments:
canvas.drawRoundRect(shadowRect, cornerRadius, cornerRadius, shadowPaint);
It can also look nice (more 3D) if you stack semi-transparent Paint
s with decreasing stroke width and increasing inset, like building your own color gradient.
Thanks to @wblaschko for sharing the code snippet on ViewOutlineProvider
!
I added it to my example and got the following effect:
Changes to my code (note: only possible for Api level 21+)
An inner class of the custom View:
@TargetApi(21)
static class ScalingOutlineProvider extends ViewOutlineProvider {
private int cornerRadius;
ScalingOutlineProvider(int cornerRadius){
this.cornerRadius = cornerRadius;
}
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight (), cornerRadius);
}
}
And at the end of init()
:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
// elevation of 4dp (cornerRadius was 60dp)
setElevation(cornerRadius/15);
setOutlineProvider(new ScalingOutlineProvider(cornerRadius));
}
For the shadow follow-up question, you have two options. Use built-in methods or draw your own (depending on needs). The former is likely the correct way to do it, unless you need a custom shadow.
Built-In Methods
See the section on Outline in this blog post: https://android.jlelse.eu/mastering-shadows-in-android-e883ad2c9d5b
Example code from the post:
Create an OutlineProvider
public class ScalingLayoutOutlineProvider extends ViewOutlineProvider {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, width, height, radius);
}
}
Add the Outline Provider to your View
public class ScalingLayout extends FrameLayout {
//...
viewOutline = new ScalingLayoutOutlineProvider(w, h, currentRadius);
setOutlineProvider(viewOutline);
//..
}
Drawing Your Own
For the border, you can use View.getElevation() to get the desired height: https://developer.android.com/reference/android/view/View#getElevation() and then draw another shape behind the bar for the shadow.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With