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.
Related
I've created a custom view which extends TextView. I want to use a BlurMaskFilter on an oval, however, unless I use LAYER_TYPE_SOFTWARE, the oval isn't blurred as expected. Using LAYER_TYPE_SOFTWARE will blur the view correctly, however now it is clipped to its bounds which I want to avoid as I am trying to create a colored shadow effect.
Bizarrely I only have this issue on API's 21-27 (min API is 21). On API's 28+ BlurMaskFilter works correctly with hardware acceleration and also has no issues drawing outside the view bounds.
LAYER_TYPE_HARDWARE
LAYER_TYPE_SOFTWARE
LAYER_TYPE_HARDWARE API 28+ (Desired effect)
I've tried setting clipChildren=false on all the views in the hierarchy, with no luck.
I also tried increasing the canvas.clipRect using negative values as per this post: https://stackoverflow.com/a/15506277/5752426 however this only seemed to work when using positive values to decrease the drawing area, and had no effect when using negative values to draw outside the view bounds.
I can somewhat get around the issue by increasing the view bounds in onMeasure() and then only painting inside those view bounds, however, this isn't ideal as I now have to account for this extra space when positioning my views in the layouts. Furthermore, the problem becomes inconsistent to account for as different views might have different elevations and shadow/blur sizes.
init block for the custom view:
init {
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
setWillNotDraw(false)
...
onDraw for the custom view:
override fun onDraw(canvas: Canvas?) {
...
if (elevation > 0f) {
circleShadowBackgroundPaint.maskFilter = circleBlurMaskFilter
canvas?.drawOval(circleBlurBackground, circleShadowBackgroundPaint)
}
...
Any help would be appreciated!
If you set this View's LayerType to LAYER_TYPE_SOFTWARE,the clipCHildren and clipToPadding will no longer has anly effect on this View.You should setLayerType on its Parent(or parent's parent ,if the parent is as large as the size of this View).
You can try (View (getParent())).setLayerType(View.LAYER_TYPE_SOFTWARE, null)
I am trying to design an UI very similar to this. I have been able to design it almost similar to the image above, but am not getting a way to implement the slanting or sloping part.
1) Can any one give an example layout of how can I implement the slanting layout?
2) And how can I place the FAB right there over the slant portion?
Any help would be really appreciated.
You can create a custom view with Slant top using Canvas and then place it over your TextView, to achieve this look and feel.
Code snippet for slant top custom view:
public class SlantView extends View {
private Context mContext;
Paint paint ;
Path path;
public SlantView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
mContext = ctx;
setWillNotDraw(false);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas) {
int w = getWidth(), h = getHeight();
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setAntiAlias(true);
path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(0,0);
path.lineTo(0,h);
path.lineTo(w,h);
path.close();
canvas.drawPath(path, paint);
}
}
Code snippet for how to use it with TextView
<com.pix.app.views.SlantView
android:layout_width="match_parent"
android:layout_height="30dp"
android:id="#+id/slant_view"
/>
<TextView/>
Other way to achieve Slant View is this:
<FrameLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:background="#color/colorAccent"
xmlns:android="http://schemas.android.com/apk/res/android" >
<ImageView
android:layout_width="match_parent"
android:layout_height="280dp"
android:src="#color/colorPrimary"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:rotation="-70"
android:layout_marginTop="100dp"
android:background="#color/colorAccent"></LinearLayout>
</FrameLayout>
This will give you following output:
1) Can any one give an example layout of how can I implement the slanting layout?
You cannot. Views are always rectangular. You may however make it look slanted, i.e. with background image.
2) And how can I place the FAB right there over the slant portion?
You cannot have slant. It's just bottom edge of the square bounding box. So you should be able to put it there w/o any problem.
Normally images are represented in rectangular form. You can use padding/margin to design UI according to your need. Obviously other than sloping part will be transparent image.
You need to have the root or at least the immediate parent layout as FrameLayout, then,
1) For the image part, you can have a normal ImageView to show the image. Below that you can have a LinearLayout (blank, with white background).
Now just tilt the blank LinearLayout to an angle probably 45 degrees to cut the background image in slant style. Here, just set the XML property of the blank LinearLayout,
android:rotation = "45"
2) For the FAB, just move it to the cutting point, gravity of the layout should be set to right according to your screenshot.
Hope this helps. Cheers!
You'll have to write a custom View to do it. There's no native control that can do this.
Canvas's provide support for clipping paths, so it's easy enough to clip the image provided you can draw the image. That being said, drawing large images in an OnDraw implementation is less than straightforward (memory management and correct scaling and caching behaviour isn't difficult, but it's less than trivial)
An easier way to do it would be to extend something like a FrameLayout or some kind of ViewGroup. If you override dispatchDraw, you can push a clip path onto the canvas, which will clip children. And then you can put other controls inside your custom control.
Something like this:
public class ClipFrameLayout extends FrameLayout {
.... constructors and stuff...
#Override
void dispatchDraw(Canvas canvas)
{
canvas.save();
canvas.clipPath(mCustomClipPath);
super.dispatchDraw(canvas);
canvas.restore();
}
}
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.
After searching I only found how to set shadow&light effect to texts,how do I put shadow effect on a view, to make the edge between views more clear?
I know that you can set the background image with shadow effect to the view. But this does not really solve the problem unless I learn how to generate those images first.
paint.setShadowLayer(float radius, float dx, float dy, int color);
I guess this will be useful.
You can also place dummy View element at edges in layout. Set its background to a drawable xml with a gradient going black to transparent.
Is it possible to add TextView and ImageView on canvas?
Canvas does not inherit from ViewGroup, so it does not have the ability to add child views to it.
With Canvas, you use drawBitmap and drawText methods to draw images and text instead of adding child controls like TextView and ImageView.
It's a round about way, but this tutorial adds a textview to his canvas:
http://www.helloandroid.com/tutorials/how-draw-multiline-text-canvas-easily
If you need to freely position ImageViews and TextViews, you should use a RelativeLayout.
The RelativeLayout has advanced positioning features and allows overlapping.
- put your view (which is using canvas) inside the RelativeLayout,
public class YourComponent extends View{
#Override
protected void onDraw(Canvas canvas)
...
public void onDraw(){
....
....
ImageView yourImageView = .....
RelativeLayout.LayoutParams fParams = new RelativeLayout.LayoutParams(25,25);
fParams.leftMargin = 100; //x coordinate
fParams.topMargin = 25; /y coordinate
yourImageView.setLayoutParams(fParams);
((RelativeLayout) YourComponent.this.getParent()).addView(yourImageView);
}
}
above code : i created layout params which shows 25x25 dimensions for view. and after that , i used margins to set proper position on the parent layout. and set this LayoutParams to ImageView. and for last, i added this imageView to parent Layout.
But beware that during the onDraw() execution, calling operation on external view may stop onDraw(). so if you want to use this way be sure you already completed all onDraw operation.
I am not so sure why this happens. I did not try to observe too much on it yet.