I want to create transparent system overlay window, which has several round views:
Green view is VISIBLE, red one is INVISIBLE. The thing is that I need red view to pass touches to the underlying window, but by default it doesn't.
I tried setting visibility of red view to GONE, but then overall size of containing view changes. Since I need the containing view to be snapped to the right edge of the screen, this means that position of green view changes, and I don't want this.
Is there any way to override default touch handling in INVISIBLE state?
Here's an actual screenshot to make my question more clear:
Also, I had to override dispatchTouchEvent() to dispatch touch events correctly:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Circle target=null;
int targetIndex=-1;
for (int i=0; i<getChildCount(); i++) {
Circle child=(Circle) getChildAt(i);
if (child.viewMetrics.containsPoint(ev.getX(), ev.getY()) && (ev.getAction()==MotionEvent.ACTION_MOVE || child.isVisible())) {
target=child;
targetIndex=i;
break;
}
}
onMotionEvent(ev, target, targetIndex);
Log.d(">| CircularLayout", "action: "+ev.getAction()+", target: "+target);
return target!=null && target.dispatchTouchEvent(ev);
}
Create custom view to do this. In such case you do not need any trickery related to visible invisible - it's you who decide what is your clickable area and also you who decies what area will alter the state (like is drawn as pressed on touch event). So your custom view should be of the size of desired area but in your onDraw() you shall only draw the change on that smaller inner area.
Here are basics on how to create custom views: http://developer.android.com/training/custom-views/index.html
What I ended up doing is calculating two sets of view dimensions: maximum possible (size of the red area) and current (green area). When child view is hidden or shown I set width and height of window LayoutParams to current dimensions and then set difference between maximum and current dimensions to y and x parameters of LayoutParams.
For some reason applying modified LayoutParams triggers measure/layout pass twice (which in turn causes the view to "bounce"), but that seems to be the whole new story.
Related
I have a view in top of the screen which I want to always be displayed (In every device, even if keyboard is opened). The problem is that in the middle of the screen I have an EditText. In some devices, if the EditText gains focus, the keyboard opens and causes layout to move upward. I do not want to use AdjustResize which will cause background image to stretch. Is it possible to know if a layout was moved or not because of keyboard being opened? Also, how does this upward movement been calculated? I am thinking about reposition the view whenever is needed
-First attempt to solve the problem: I have tried Coordinator layout. I did not find a behavior to do what i want, so i tried to implement my own behavior. I added a space view in the bottom of the layout and whenever this view changed its position i tried to translate downward the top view. But even if layoutDependsOn() returns true the onDependentViewChanged() is not called in order to calculate the translation.
-Second attempt to solve the problem: I added again a space view in the bottom of the layout. Also, I Added an onGlobalLayoutListener in order to track if the keyboard was opened. If so, i tried to calculate space view's position and compare it with its previous position in order to move downward the top view.
-Third attempt was to use AdjustNothing and hand by myself the translations of the views that need to reposition theirselves on keyboard open. This was the only attempt that gave me some results, but seems buggy and in the future i am sure that will cause much more problems than it solves.
In addition, i found that x, y, translationX, translationY do not change in adjustPan. The only way to calculate the correct position is to use getLocationOnScreen(). But this is possible after the view was laid out. Can we calculate it before the view is laid out?
For future reference, in order to calculate the pixels of upward translation in AdjustPan mode you must do something like the following code. Basically, in AdjustPan we push as much as is needed the layout in order to make the focused EditText fully visible.
val rect = Rect()
constraintLayout.getWindowVisibleDisplayFrame(rect)
val editTextGlobalRect = Rect()
view.getGlobalVisibleRect(editTextGlobalRect)
//if it is true the editText will be hidden so the layout will be moved upwards due to adjust pan
val translation = if (editTextGlobalRect.bottom > visibleArea.bottom) {
//Calculate how much the layout is going to be pushed
editTextGlobalRect.bottom - visibleArea.bottom.toFloat()
} else {
0f
}
Also, if you want to calculate the new position of the view, in order to stick in the same screen position, you must do something like that:
val stationaryViewGlobalRect = Rect()
stationaryView.getGlobalVisibleRect(stationaryViewGlobalRect)
stationaryView.y = when {
translation <= stationaryViewGlobalRect.top -> {
//Stationary view is not hidden at all but it was moved upwards
stationaryViewGlobalRect.top - translation + resources.getDimension(R.dimen.stationary_view_margin_top)
}
translation <= stationaryViewGlobalRect.bottom -> {
//Stationary view is not fully hidden
translation + (stationaryViewGlobalRect.bottom - translation)
}
else -> translation + stationaryViewGlobalRect.top //Stationary view is fully hidden
}
I wanted to animate view as shown in the image. How to do it?
Click here to see the image
I have the following code but how to set the parameter current?
How Animation works?
When the user moves his finger over the list of icons, the height of the current icon, where the user has touched, is set to 400.
TransitionManager.beginDelayedTransition(layout);
View child = layout.getChildAt(current);
child.setMinimumHeight(400);
where current is of type int.
Shouldn't try to change a View height, margin or padding if you want to do an animation.
Do a scale animation instead on focus:
View child = layout.getChildAt(current);
child.animate().scaleX(2f).scaleY(2f).setDuration(400).start();
Scale to normal size on unfocus:
View child = layout.getChildAt(current);
child.animate().scaleX(1f).scaleY(1f).setDuration(400).start();
my question is related to android feature to zoom the parent consisting of multiple child view's.
ZoomView->ParentView->[multiple childViews]
I am working on a Demo project to Zoom the child View and to pan infinitely.Zooming works perfectly as needed.
PROBLEM 1:
But if there is a EditText in the view and i try to zoom on that then below issues are faced by me.
on zooming in the text is blurred
pointing on a text is working fine but translating through the text is
translating very fast as its translation is multiplied by the
scalingFactor
Selecting the text is also having above issue.
Try running the the Demo to understand the issue if not clear from above
I have tried two approaches to Zoom the content of the View but both approaches gave the same issue.
Scaling canvas and transforming the MotionEvent by scaling the MotionEvent by Matrix.class of the Parent Class.
Setting ScaleX and ScaleY of the Parent containing the childViews
hierarchy of my demo project zooming the view(UML Diagram)
Basic problem is placing the cursor at the right position when the view is scaled to some value.
I have already referred to this thread
PROBLEM 2:
if i have a childView which is movable in the parent View then after zooming out if the childView is translated outside the bounds of parentView the events are ceased to capture and the child view becomes untouchable
i have tried using TouchDelegate but i don't know how to expand the parentView touch area .Code reference Thread
scale is 1 the area touch area is equal to screen for parentView and ZoomView
But when scale factor is not equal to one the touch area of parent is lesser(zoomIn) than the ZoomView as displayed
Yellow-parentView Touch Region
Cyan-ZoomView Touch Region
Green-ChildView Touch Region
ScreenShot here
Note:
This is my first Question on StackOverflow So please recommend if some edits are needed.
This the the closest i got to solve above problem's.
PROBLEM 1:
On zooming in the text is blurred
setLayerType(LAYER_TYPE_HARDWARE,null);
set layer type Hardware in the view which has text View. for me i
set it in green square view.
pointing on a text is working fine but translating through the text is translating very fast as its translation is multiplied by the scalingFactor
Selecting the text is also having above issue.
For above two:
This is a bug in android when you scale a view using the scaleX,scaleY api's the EditText get's event based on the current scale value of parent's.But issue is with the Cursor of editText.
The best solution we got is we wrote our own EditTextView (which extend's TextView).As we checked android framework source code we came to know that android use's PopUpWindow to display cursor on screen.
The source of issue is when we use scaleX and scaleY editText get's scaled even't as it's parent is scaled but as cursor is a PopUpWindow and not a child of EditText so event's are not scaled and above issue occur's
So wrote our own EditText if you see the source code EditText code is not very complex and can be written on our own.And to solve the scaling issue of Cursor, we added our own Cursor PopUpWindow and made it Scale aware.
NOTE:
(UPDATE)
Check at demo code here customEditText and CustomCursor.
Run the project set focus pink focus(BY Android) will come ,wait for few seconds then the black cursor will appear that is the custom cursor.
It is a very basic example and we need to add all other edit text Feature's on our own like multi-Select, popUpMenu etc.
PROBLEM 2: if i have a childView which is movable in the parent View then after zooming out if the childView is translated outside the bounds of parentView the events are ceased to capture and the child view becomes untouchable i have tried using TouchDelegate but i don't know how to expand the parentView touch area
This solution is specific to the given problem not a generic one
as there can be multiple solution's for this problem.
In this case 3 view's are there:
Yellow-parentView of green square View
Cyan-parent of yellow View
Green-ChildView non touchable outside parent
My Solution is that i used created a callback from green square view to Yellow parent view. Whenever the translation of green square view ended the callback is triggered and in yellow parent i used below code.
override fun eventEnded() {
setDelegate()
}
fun setDelegate() {
val rect = Rect()
val parent = (this.parent as View)
parent.getHitRect(rect)
val touchDelegate = TouchDelegate(rect, this)
if (View::class.java.isInstance(editorContainer.parent)) {
(editorContainer.parent as View).touchDelegate = touchDelegate
}
}
The above code translate to : when child view translation is ended check the bound's of parent and update the TouchDelegate accordingly.
As the problem i faced was when i scaled(Zoomed) the view then the View's delegate was not updated so for that also solution is same make a callback from zoomView (parent) to yellowView (childView) onScaleEnded call setDelegate().
When scale changes the hitArea also changes but as per delegate nothing is changed so we need to update the rect of TouchDelegate.
Please feel free to discuss in comment section. If someone has a better solution Really eager to know.As we tried many different solution's but nothing else worked.This is the best i found and i am using above code in production and haven't faced any issue till now.
The best approach for translating and scaling EditText is to use scaleX, scaleY, translateX, translateY on this particular EditText instead of parent layout canvas translation.
e.g. in kotlin
editText.scaleX = scaleFactor
editText.scaleY = scaleFactor
editText.translateX = offsetLeft
editText.translateŠ½ = offsetTop
By the way, it is possible to check if touch area and drawing area are the same in android studio using Tools -> Layout Inspector.
Within Android, I'm trying to move a TextView from outside the parents bounds into view, but the contents never shows up, or remains clipped if it was partially within the bounds already.
Initial situation at start
Situation after animation
(Below this is another view, that was completely out of bounds and isn't drawn either)
I have 4 TextViews below each other in a custom Object extending RelativeLayout. Based on a percentage the top 2 should move outside it's bounds and the bottom 2 should move in (from the bottom).
I use the following code to update the properties of each TextView. In this class each variable **positionY* is filled with their initial position from the layout-xml. effect is percentage between 0 & 1. The animation works, but the views aren't drawn again.
public class ActionBarTitleView extends RelativeLayout {
public void updateTransition(float effect) {
float height = getHeight();
titleView1.setY(title1positionY - height*effect);
detailView1.setY(detail1positionY - height*effect);
titleView2.setY(title2positionY - height*effect);
detailView2.setY(detail2positionY - height*effect);
invalidate();
}
}
What I tried
After some researching I found a few hints what the issue might be, but so far none of the tried options had any effect. Below is a list of things I've found on SO and tried.
Calling invalidate() on the RelativeLayout - No effect.
Invalditing the TextViews - No effect.
clipChildren = false for the RelativeLayout - No effect.
setWillNotDraw = false for the RelativeLayout - No effect. (onDraw is being called)
I haven't tried to solve this with a ScrollView, but I don't want to really, cause that adds another layer in the hierachy for something pretty small.
I thought I understood the drawing logic, but perhaps I'm missing something, so I hope someone might be able to point me in the right direction.
What I ended up doing (September 3rd)
Since no real solution was offered, I tried again and came to the following "fix". I set both second labels to Visibility.GONE, but within the original bounds of the container view. Then when I start the animation, I set their correct values, then move them outside the bounds and finally setting Visiblity.VISIBLE. When the animation progresses the labels roll into view as supposed to. So a fix to the issue, but no real explanation why the TextViews aren't drawn again...
According to the docs for the View class:
The geometry of a view is that of a rectangle. A view has a location, expressed as a pair of left and top coordinates, and two dimensions, expressed as a width and a height. The unit for location and dimensions is the pixel.
It is possible to retrieve the location of a view by invoking the methods getLeft() and getTop(). The former returns the left, or X, coordinate of the rectangle representing the view. The latter returns the top, or Y, coordinate of the rectangle representing the view.
In addition, several convenience methods are offered to avoid unnecessary computations, namely getRight() and getBottom(). These methods return the coordinates of the right and bottom edges of the rectangle representing the view. For instance, calling getRight() is similar to the following computation: getLeft() + getWidth().
My interpretation of the above is that the View's position is controlled by its "Left" and "Top" values, while its width and height are controlled by its "Width" and "Height" values. This seems especially clear considering that last sentence, where "Right" is derived by adding Left and Width.
Despite this, when I use setLeft() and/or setTop() to change the position of the View, the SIZE of the View changes on screen! Meanwhile, the lower right corner of the View stays anchored to its original spot. This behavior implies that "Right" and "Bottom" are actual values, not derived as described in the docs.
So what is really going on here? The docs say one thing, but the behavior says the opposite. What is the proper way to reposition a View?
EDIT: I added a RelativeLayout:
myParams = new RelativeLayout.LayoutParams(300,300);
myParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
myParams.addRule(RelativeLayout.CENTER_VERTICAL);
myView.setLayoutParams(myParams);
...to create a View 300x300 centered on the screen. Works perfectly. But examining that RelativeLayout, the location seems to be controlled by leftMargin and topMargin - yet both are zero! That raises the questions of 1) how can you examine the LayoutParams to know where the View is right now, and 2) how can you alter the LayoutParams to move it to a different location?
EDIT: As an experiment, I added an onTouch method to the View and did this within it (excerpt):
if (MotionEvent.ACTION_MOVE == iAction) {
myParams = (RelativeLayout.LayoutParams) v.getLayoutParams();
myParams.leftMargin = 0;
myParams.topMargin = 0;
v.setLayoutParams(myParams);
}
...on the theory that my vertically and horizontally centered View would then move to the upper left corner of the screen. Result: It didn't move at all. Not exactly surprising, since .leftMargin and .topMargin were already zero, but I wanted to try it just in case there was some magic hiding here.
Other suggestions?