AdjustPan calculation - android

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
}

Related

Android: Zooming EditText Android Issue in translation And Getting Touch event of childView when Placed outside parentView

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.

Move button back to origin after dragging/moving it

I have an ImageButton that I need aligned to the bottom of the screen, and when I click and drag it, it moves with my finger by a certain distance, beyond which it will not move further from the origin but continue following the direction my finger is from the origin.
When I let go, the button has to slide back to the origin.
What I have managed to do so far is to have the button follow my finger via this answer: Moving buttons via Touch
However, how should I get the origin of the button at initialization for it to bounce back after I release?
Originally I implemented the alignment of the button by having a android:layout_gravity="center|bottom" to place it at the bottom, but it somehow messes with the position of the button relative to my finger (It follows my finger movements but it's off center).
Hence, I used mButton.setY() and mButton.setX() to place it at the bottom of the screen, but it seemed hackish to use the screen dimensions to place it. Can't think of a better way.
You can set the width and height of your layout in XML using
layout_width and layout_height.
Then in your MainActivity, instantiate the layout (I will use LinearLayout as an example) and get its width and height.
LinearLayout l = (LinearLayout)findviewbyid(R.id.l1);
int height = l.getHeight();
int width = l.getWidth();
Now, whenever you release the button, you can set your its position in the following way:
mButton.setY(height - 50)
This will position the ImageButton 50 pixels above the bottom of your layout. You can also set the x position of the button as you want in the same way. Also, you should probably store height - 50 as a global variable (origin) that you can call from anywhere in your code.

Redrawing clipped content in Android View after moving into bounds

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...

Make INVISIBLE View handle touches as ACTION_OUTSIDE

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.

How can I know which childview is shown on the current Device screen in a scrollview?

How can I know which childview is shown on the current device screen, in a scrollview?
I want to make an infinite Ruler App, so I need to dynamically add and remove view when the view is flipping, but how and where can I find out which childview is shown on the current device screen, so I can add or remove the correct view when I get that childview changed notify?
Or, can anyone who can give me some other idea to realize this function?
I have met this problem too. you can call getlocalonscreen function to get coordinate of view. for example:
1.
view.getlocaloncreen(xy);// xy[0] is x axis, xy[1] is y axis.
2.
int nScreenWid = getwindowsmanager().getdefaultdisplay().getwidth();
int nScreenHei = getwindowsmanager().getdefaultdisplay().getHeight();
3.
if( xy[0] > nscreenWid )
{
// your view is beyond the x axis.
}
if( xy[1] > xxxxhei )
{ // beyond y axis }
Use scrollView.getScrollY() to find how far down the scroll view the scroll point is, and compare that to the heights of the child views (adding them up as you go along) to see which one must be on screen.

Categories

Resources