View outside bounds not drawing properly - android

I'm drawing a tooltip after clicking inside a custom bar chart (created with MPAndroidChart). The view hierarchy is as follows
<LinearLayout>
<TextView text=Move & Max Pain/>
<RelativeLayout with 2 textviews>
<chart
clipToChildren=false
clipToPadding=false
/>
</LinearLayout>
While the View is inside the Chart or its inmediate sibling, everthing looks good. But the moment it collides with its sibling, the tooltip is truncated
Using HierarchyViewer I can see that the content is present, but it's not drawn.
In order to get the clipping, I'm using this code inside draw
#Override
public void draw(Canvas canvas, float posx, float posy) {
// take offsets into consideration
posx += getXOffset();
posy += getYOffset();
canvas.save();
// translate to the correct position and draw
canvas.translate(posx, posy);
Rect clipBounds = canvas.getClipBounds();
clipBounds.inset(0, -getHeight());
canvas.clipRect(clipBounds, Region.Op.INTERSECT);
draw(canvas);
canvas.translate(-posx, -posy);
canvas.restore();
}
If I change Op to Region.Op.Replace, the tooltip is draw correctly but it replaces the Toolbar content, instead of scrolling under it.

You'll need the bounds of the area in which you want to be able to draw the tooltip, and I'm assuming that would be a scrollview. Then you can intersect the tooltip bounds with the scroll to work out what the clipping should be; and if it should be drawn at all.
To explain it in code it would be something like this (untested):
Rect scrollViewRect; // the bounds of your scrollview
Rect tooltipRect; // the bounds of your tooltip
bool intersects = tooltipRect.intersect(scrollViewRect)
if(intersects)
{
canvas.clipRect(tooltipRect, Region.Op.REPLACE);
draw(canvas);
}

Related

Draw Circle in Preview in CameraX describing TapToFocus

I am trying to implement TapToFocus feature using the CameraX Api .I have implemented it successfully but not able to know how to draw a circle on the preview describing the Location pressed by the user .
I want to have a circle in the preview like the image has
The are many ways to draw and animate a focus ring around a tap position on PreviewView, some fancier than others. One simple way of doing so is to:
Create a Drawable of the ring, something like this for instance.
Layout an ImageView containing the Drawable on top of PreviewView, and initially hide it.
<FrameLayout ...>
<androidx.camera.view.PreviewView
... />
<ImageView
android:id="#+id/focusRing"
android:src="#drawable/focus_ring"
android:visibility="invisible"
... />
</FrameLayout>
Set up a touch listener on PreviewView. On a touch event, use the event's coordinates to show the ring around it.
private void animateFocusRing(float x, float y) {
ImageView focusRing = findViewById(R.id.focusRing);
// Move the focus ring so that its center is at the tap location (x, y)
float width = focusRing.getWidth();
float height = focusRing.getHeight();
focusRing.setX(x - width / 2);
focusRing.setY(y - height / 2);
// Show focus ring
focusRing.setVisibility(View.VISIBLE);
focusRing.setAlpha(1F);
// Animate the focus ring to disappear
focusRing.animate()
.setStartDelay(500)
.setDuration(300)
.alpha(0F)
.setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animator) {
focusRing.setVisibility(View.INVISIBLE);
}
// The rest of AnimatorListener's methods.
});
}

Draw in front of certain child views

I have a ViewGroup that consists on a header and a circle of menu items. Basically I have a closing/opening animation where my item views go behind the header view. Since all views have transparencies, when the item views go behind the header view, they are still visible and end up appearing behind the header view through the transparencies.
What I wanted to do is to intersect the item views with the hweader view, erasing the intersection. What I came up with was to override dispatchDraw and do something like PorterDuff.Mode.CLEAR
But I can only do this to all views at once, in the sense. Using the code below, it'll erase everything that's been drawn in the view in that specific area, thus the header as well.
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
//do stuff here
}
Is there any way i can draw the view again, or even select which views I want to erase?
Just for future reference, this is what I did. Override dispatch draw, erase the given area and draw the child again with child.draw(canvas)
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
Paint p = new Paint();
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawCircle((float) center.x, (float) center.y, headerSize / 2, p);
canvas.save();
canvas.translate(padding, padding);
getChildAt(0).draw(canvas);
canvas.restore();
}

Android L ViewOutlineProvider, strange behavior when created rect side is out of screen

I have a strange problem with the ViewOutlineProvider for a clipped View on Android L.
I try to increase the size of the clip circle while moving the finger over the screen (x coordinates etc.). (Like on the Android L lock screen, left and right icons, which increases when you drag your finger in the specific direction)
The size changing works perfectly, until the clip bounds reaches the left or bottom border of the screen, after this the clip bounds are disappeared and i don't have any clip bounds. (Nothing rounded)
The Rect values, which causes the problem:
Rect(-1, 1033 - 101, 1136) (Rect(left, top - right, bottom))
I initiate and add the view, which i want to clip, in this way:
toClipView = new ViewClipTest(getContext());
toClipView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
addView(toClipView);
The clip view has a gray background and a red ImageView as child, it extends a FrameLayout
Update code for clip bounds scaling (is called from a onTouchEvent, while ACTION_MOVE):
public void scaleClipbounds(float convertScale) {//-1 to 1
float scale = ...;
currentScale = scale;
...
post(new Runnable() {
#Override
public void run() {
setOutlineProvider(new ClipOutline(createScaledRect(currentScale)));
invalidate();
}
});
}
...
case MotionEvent.ACTION_MOVE:
newX = event.getRawX();
newY = event.getRawY();
float newScale = ...;
scaleClipbounds(newScale);
...
My ViewOutlineProvider:
public class ClipOutline extends ViewOutlineProvider {
private Rect outerLineRect;
public ClipOutline(Rect outerLineRect) {
this.outerLineRect = outerLineRect;
}
#Override
public void getOutline(View view, Outline outline) {
outline.setOval(outerLineRect);
}
}
Xml structure:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.ClipViewTestContainer
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
If I initiate the clip bounds with the same rect, which causes the issue, everything works well, until I try to increase the bounds while onTouch again.
What is my mistake here? Or how can I force that the clip bounds can be set out of the screen boundaries? Is there a more fluent way to implement such kind of feature, instead of using an ViewOutlineProvider?
The problem is that your rect you're passing into setOval (-1, 1033 - 101, 1136) has size 102 x 103, which means your outline won't be a circle, and thus won't be eligible for clipping. Only circles, rects, and round rects can be clipped to, see https://developer.android.com/reference/android/graphics/Outline.html#canClip()
Additionally, you can significantly simplify your runnable / outline provider creation by, within scaleClipBounds, updating an existing custom outline provider, and calling View#invalidateOutline().
public void scaleClipbounds(float convertScale) {
...
mOutlineProvider.setRect(rect)
invalidateOutline()
}

How to draw content behind parent View in Custom ViewGroup

I am using this ArcMenu https://github.com/daCapricorn/ArcMenu
Please check the code and the image of the ArcMenu on the github page
This custom view is arranged this way ->
ArcMenu[View] has Children -> ArcLayout[ViewGroup] has children -> ImageViews (to hold the icons for menu)
I am trying to draw colored arc behind the Arclayout. I am doing this in the onDraw
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
if(isExpanded()){
Log.d("ArcLayout","onDraw(Canvas)");
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(colorOne);
paint.setAlpha(125);
canvas.drawArc(rect, startAngle, sweepAngle, true, paint);
//Dimensions of rect are set in OnMeasure
}
super.onDraw(canvas);
}
Now the problem is that the OnDraw gets called for all the children and the logic for drawing is same for the parentView and children view. I want the Colored Arc to be drawn behind the parent view and not the children(Imageviews).
Rect dimensions are top=0, left=0, right=getMeasuredWidth(), bottom=getMeasuredHeight().
When the call to canvas.onDraw is made, top Left of the rect becomes relative to child i.e 0,0 of the child and the not the parent. The result is a colored arc behind the child and not the parent.
How should I ensure that the colored arc gets drawn behind the Parent view only?

Why is the rectangle not shown when I used drawRect()?

Why is the rectangle not shown when I used drawRect() on canvas object,
and also declared it inside onCreate method.
Code
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activitymain);
Chronometer stopWatch = (Chronometer)findViewById(R.id.chrono);
mDrawingActivity = (DrawingActivity)findViewById(R.id.the_canvas);
Button b = (Button)findViewById(R.id.startButton);
b.setText("start");
b.setOnClickListener(this);
}
OnDraw() Method
protected void onDraw(Canvas Square)
{
super.onDraw(Square);
Paint squareColor = new Paint();
squareColor.setColor(Color.CYAN); // change the box color to cyan
Square.drawRect(100,100,100,100, squareColor);
return;
}
Clarification: Even the button and chronometer are not shown too and the program is forced closed.
You are drawing an point rectangle.
Change line
Square.drawRect(100,100,100,100, squareColor);
to
Square.drawRect(100, 100, 200, 200, squareColor)
Here is the definition from doc.
drawRect(float left, float top, float right, float bottom, Paint paint)
Draw the specified Rect using the specified paint. The rectangle will be filled or framed based on the Style in the paint.
Parameters
left The left side of the rectangle to be drawn
top The top side of the rectangle to be drawn
right The right side of the rectangle to be drawn
bottom The bottom side of the rectangle to be drawn
paint The paint used to draw the rect

Categories

Resources