I have a round corners layout, now I want to add a child view (an Imageview) which matches the parent layout's height and width.
My problem is that the child view hides the round corners of the parent.
How can I constrain it inside the borders of the parent layout without using the margin property, so that the parent's round corners stay visible?
PS: I created round corners of parent layout by overriding the onDraw() method.
My code:
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, width, height, mpaint);
super.onDraw(canvas);
}
In my opinion, you can put your child views into a CardView(in support v7), which is actually a FrameLayout, but it handle the corners by just set one line of code:
app:cardCornerRadius="3dp"
It can clip the corner with the radius you set no matter what the child views are.
I suggest you add padding to the "rounded corner" view. This could be padding on all sides, bottom and top or left and right. Depending on what suits you the best.
I can't think of a more simple method than this. Your onDraw method looks fine, first the background than the child views.
When you add a child to your ViewGroup, that child is being drawn on top of your ViewGroup, thus your rounded corner doesn't take effect.
In order to achieve your goal you have to perform clipping on a certain path in your layout. That sounds a bit complicated, but in fact it is not.
Essentially, you can understand clipping as "cutting off" some part from your layout.
RectF rect = new RectF(0, 0, width, height);
path = new Path();
path.addRoundRect(rect, cornerRadius, cornerRadius, Direction.CW);
canvas.clipPath(path); // clipping here
// now anything that is outside this path will be "clipped", i.e. not drawn
You can refer to this for a complete source code.
Related
I'm having a requirement of draw a border around a parent layout in Xamarin.Android it can be any layout like FrameLayout, LinearLayout etc.
I have achieved this by using GradientDrawable - Which is just like setting a background for the layout with a shape.
So my requirement will be achieved
Query
When setting corner radius for the border, it is not clipping the children. How to clip the children or any other way alternative to do the both? Kindly share your suggestion on this.?
Note: I have tried with ClipChildren, ClipToPadding for the layout.
PS: Above images are mentioned for illustration purpose, they are not the exact output.
TIA.
You can clip the view using canvas. Use ClipPath(path) method of Canvas to clip the required area.
Syntax:
Path path = new Path();
path.AddCircle(200,200,100,Direction.CW);
canvas.ClipPath(path);
You can make use of ViewOutlineProvider API. As an example usage see ClippingBasic project.
Having defined outline provider class as such:
private class OvalOutlineProvider extends ViewOutlineProvider {
#Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
}
Then apply view outline to parent:
View parent = findViewById(R.id.parent);
parent.setOutlineProvider(new OvalOutlineProvider());
parent.setClipToOutline(true);
ViewOutlineProvider is accessible starting from API 21. Not sure, whether the solution is applicable for Xamarin (hope it does).
Setting ClipToOutline = true; for the parent, resolves the issue.
I am confused with setbounds method of drawable in Android. I know it is used with drawable and it set where drawable and how large it is. Briefly, it defines, bounding rectangle for drawable. But I am confused: does it set border or padding or its width or height?
This is how we use setbounds
drawableInstance.setBounds(L,T,R,B);
First scenario
So in above case, If I set 5,5,5,5 for L,T,R,B respectively, L,T,R,B of drawable will be always from its parent respectively? I mean will it be like setting hidden 5px border to every side?Besides, if image is not large enough to meets that border width to its parent, will image be bigger?
Second scenario
I set 5,5,100,100 for L,T,R,B respectively. What I am confusing is, it will starts drawing from away from parent 5px to the top and 5px to the left. That will be the start point. Then image with will be 100px because I set 100 for right. So it goes to the right 100px. Same to bottom with right.
But I tested it. I think both is not as what I think. How does it work, especially in relation to each parameter of setBounds?
The bounds values are absolute, not relative. That is, width == right - left, and height == bottom - top.
Your first example will have the top left corner of the Drawable at (5, 5), but the width and height will both be 0.
The second example will also have the top left corner at (5, 5), and the width and height will both be 95.
The bounds are not dependent on the dimensions of the Canvas to which the Drawable is drawn. For instance, if you draw a Drawable with the bounds of your second example onto a View that is only 50 by 50, its left and top sides will be inset by 5, but it will get cut off at the right and bottom borders.
I have a custom ViewGroup that contains an ImageView and a few other TextViews that display white text on the image. I would like to draw ONE dark rectangle under all the textviews to improve the contrast.
To accomplish this, in the custom ViewGroup's onLayout, I compute the smallest rectangle that contain all of the TextViews. Now, I have no idea how to draw the rectangle on top of the first child (the image), but below the other children, the TextViews. So I would like to draw the children in the order:
ImageView (first child)
Computed dark Rect
TextViews (all other children)
I tried overriding dispatchDraw() and then after the call to super.dispatchDraw(canvas), I drew the rectangle. But that obviously put the rectangle over the TextViews.
I would appreciate any help.
Thanks!
I have a custom view which is nested inside of a ScrollView. The idea is that the custom view can be longer than the height of the screen, so when you swipe through the ScrollView you can see all of its contents.
In order to do this I adjust the clip of the custom view's canvas like this:
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.clipRect(mCanvasArea, Region.Op.REPLACE);
}
Where mCanvasArea is a pre-generated RectF which represents the entire area the canvas can draw. I logged it's value # 10308.
However when the app runs, the custom view is unscrollable, it acts as if the visible content represents all of the content in the canvas, but I can see more peeking up from the bottom of the screen.
Here is my XML declaration for the view:
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/activity_toolbar"
android:fillViewport="true">
<com.myapp.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</ScrollView>
(I had to add fillViewport in order to get the content to show up in the first place)
Is there anything else that needs to be done in order yo make the view scroll?
You will need to specify the height of the custom view, not the canvas. The canvas passed to onDraw reflects the width and height of the view.
Setting the view to wrap_content doesn't mean it will grow whenever you draw outside of its bounds. It will only "wrap" the child views, not the canvas.
To set your view's height programmatically:
getLayoutParams().height = 500;
invalidate();
I'm not sure what you are going for with this exactly so I can't get too much more specific about where you should set the height, but I'd recommend not changing your view's dimensions in onDraw because when you invalidate it will cause another draw and you'll need some funky logic to handle this recursion.
Instead you could determine the height you need when the view is constructed, and then override onMeasure with something like this:
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, calculatedHeight);
}
This would force your custom view into the calculated height.
I have a button.
The button is constructed from the StateListDrawable (made of 3 9-patch images).
I need to add an extra drawable that will reside on the button's right side, and i need it to be aligned with the button's right side.
I tried to use the following:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/blue_back_button_drawable" />
<item>
<bitmap android:src="#drawable/filter_by_button_v_mark" android:gravity="right"/>
</item>
</layer-list>
it didn't work and the V image is indeed aligned right but it has a diff from the button's right side. here's a snapshot:
I want the image to be aligned with the button's left, the only way right now i think i can do it is:
inherit button, in onLayout AFTER the width has been set get the right edge.
get the background drawable (layerDrawable)
calc the button's width minus the v image width and set it as left margin in the drawable.
I should not mention this sounds horrid :-) i hoped there's a better way. Oh the reason it's not part of the image is that i need to know it's width so i can calc the text padding so it wont be hidden by the button's text and because it's not so nice looking as a 9-patch.
Seems like there was no proper solution to this. layer is for static and not 9-patch images apparently, or so it seems.
So i extended Button, changed it's on Draw, i have to use Canvas.restore() to break out of the margins that pushed my edges down and put my image in the upper right corner...
here's the code:
#Override
public void setPadding(int left, int top, int right, int bottom) {
right += mImageWidth;
super.setPadding(left, top, right, bottom);
}
private void setVImageDrawable(Drawable d) {
mVImageDrawable = d;
mImageWidth = mVImageDrawable.getIntrinsicWidth();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if( mVImageDrawable != null && mIsMarked){
//for some reason the clip binds us to position with margins, which is not so good for us.
//we need to save the clip, restore the canvas to pre clipping mode, do what we need then
//put the canvas back to it's previous clipping state.
Rect bounds = canvas.getClipBounds();
canvas.restore();
int right = getRight();
int left = right - mImageWidth;
int top = getTop();
int bottom = top + mVImageDrawable.getIntrinsicHeight();
mVImageDrawable.setBounds(left, top, right, bottom);
mVImageDrawable.draw(canvas);
canvas.save();
canvas.clipRect(bounds);
}
}
If i don't break out the original canvas iget 'pushed down' by any margins i add, it may also be fixed i think using xml property that specifies if children should be clipped on the layout containing this button, but i find that lame solution as it makes my button dependent on our conditions.
The image itself i get from XML using new attributes and style and theme i declare.
Hope it'll help some ppl out there.