How can I dynamically set the position of view in Android? - android

How can I change the position of view through code? Like changing its X, Y position. Is it possible?

For anything below Honeycomb (API Level 11) you'll have to use setLayoutParams(...).
If you can limit your support to Honeycomb and up you can use the setX(...), setY(...), setLeft(...), setTop(...), etc.

Yes, you can dynamically set the position of the view in Android. Likewise, you have an ImageView in LinearLayout of your XML file. So you can set its position through LayoutParams.But make sure to take LayoutParams according to the layout taken in your XML file. There are different LayoutParams according to the layout taken.
Here is the code to set:
LayoutParams layoutParams=new LayoutParams(int width, int height);
layoutParams.setMargins(int left, int top, int right, int bottom);
imageView.setLayoutParams(layoutParams);

There are different valid answers already, but none seems to properly suggest which method(s) to use in which case, except for the corresponding API level restrictions:
If you can wait for a layout cycle and the parent view group supports MarginLayoutParams (or a subclass), set marginLeft / marginTop accordingly.
If you need to change the position immediately and persistently (e.g. for a PopupMenu anchor), additionally call layout(l, t, r, b) with the same coordinates. This preempts what the layout system will confirm later.
For immediate (temporary) changes (such as animations), use setX() / setY() instead. In cases where the parent size doesn't depend on WRAP_CHILDREN, it might be fine to use setX() / setY() exclusively.
Never use setLeft() / setRight() / setBottom() / setTop(), see below.
Background:
The mLeft / mTop / mBottom / mRight fields get filled from the corresponding LayoutParams in layout(). Layout is called implicitly and asynchronously by the Android view layout system. Thus, setting the MarginLayoutParams seems to be the safest and cleanest way to set the position permanently. However, the asynchronous layout lag might be a problem in some cases, e.g. when using a View to render a cursor, and it's supposed to be re-positioned and serve as a PopupMenu anchor at the same time. In this case, calling layout() worked fine for me.
The problems with setLeft() and setTop() are:
Calling them alone is not sufficient -- you also need to call setRight() and setBottom() to avoid stretching or shrinking the view.
The implementation of these methods looks relatively complex (= doing some work to account for the view size changes caused by each of them)
They seem to cause strange issues with input fields: EditText soft numeric keyboard sometimes does not allow digits
setX() and setY() work outside of the layout system, and the corresponding values are treated as an additional offset to the left / top / bottom / right values determined by the layout system, shifting the view accordingly. They seem to have been added for animations (where an immediate effect without going through a layout cycle is required).

There is a library called NineOldAndroids, which allows you to use the Honeycomb animation library all the way down to version one.
This means you can define left, right, translationX/Y with a slightly different interface.
Here is how it works:
ViewHelper.setTranslationX(view, 50f);
You just use the static methods from the ViewHelper class, pass the view and which ever value you want to set it to.

I would recommend using setTranslationX and setTranslationY. I'm only just getting started on this myself, but these seem to be the safest and preferred way of moving a view. I guess it depends a lot on what exactly you're trying to do, but this is working well for me for 2D animation.

You can try to use the following methods, if you're using HoneyComb Sdk(API Level 11).
view.setX(float x);
Parameter x is the visual x position of this view.
view.setY(float y);
Parameter y is the visual y position of this view.
I hope it will be helpful to you. :)

For support to all API levels you can use it like this:
ViewPropertyAnimator.animate(view).translationYBy(-yourY).translationXBy(-yourX).setDuration(0);

Set the left position of this view relative to its parent:
view.setLeft(int leftPosition);
Set the right position of this view relative to its parent:
view.setRight(int rightPosition);
Set the top position of this view relative to its parent:
view.setTop(int topPosition);
Set the bottom position of this view relative to its parent:
view.setBottom(int bottomPositon);
The above methods are used to set the position the view related to its parent.

Use LayoutParams.
If you are using a LinearLayout you have to import android.widget.LinearLayout.LayoutParams, else import the proper version of LayoutParams for the layout you're using, or it will cause a ClassCastException, then:
LayoutParams layoutParams = new LayoutParams(int width, int height);
layoutParams.setMargins(int left, int top, int right, int bottom);
imageView.setLayoutParams(layoutParams);
NB: Note that you can use also imageView.setLeft(int dim), BUT THIS WON'T set the position of the component, it will set only the position of the left border of the component, the rest will remain at the same position.

Use RelativeLayout, place your view in it, get RelativeLayout.LayoutParams object from your view and set margins as you need. Then call requestLayout() on your view. This is the only way I know.

In Kotlin you can do it as below;
view
.animate()
.x(50f)
.y(100f)
.duration = 500L

I found that #Stefan Haustein comes very close to my experience, but not sure 100%. My suggestion is:
setLeft() / setRight() / setBottom() / setTop() won't work sometimes.
If you want to set a position temporarily (e.g for doing animation, not affected a hierachy) when the view was added and shown, just use setX()/ setY() instead. (You might want search more in difference setLeft() and setX())
And note that X, Y seem to be absolute, and it was supported by AbsoluteLayout which now is deprecated. Thus, you feel X, Y is likely not supported any more. And yes, it is, but only partly. It means if your view is added, setX(), setY() will work perfectly; otherwise, when you try to add a view into view group layout (e.g FrameLayout, LinearLayout, RelativeLayout), you must set its LayoutParams with marginLeft, marginTop instead (setX(), setY() in this case won't work sometimes).
Set position of the view by marginLeft and marginTop is an unsynchronized process. So it needs a bit time to update hierarchy. If you use the view straight away after set margin for it, you might get a wrong value.

One thing to keep in mind with positioning is that each view has an index relative to its parent view. So if you have a linear layout with three subviews, the subviews will each have an index: 0, 1, 2 in the above case.
This allows you to add a view to the last position (or the end) in a parent view by doing something like this:
int childCount = parent.getChildCount();
parentView.addView(newView, childCount);
Alternatively you could replace a view using something like the following:
int childIndex = parentView.indexOfChild(childView);
childView.setVisibility(View.GONE);
parentView.addView(newView, childIndex);

Related

android - get estimated height of a programmatically created view

How to get programmatically created RelativeLayout's height after adding all the views in it (i.e., when it really can be measured)? I programmatically created Buttons that should be children for that RL, set their margins and alignment, added them to the RL, so RL's height can be measured. Ordinary rl.getHeight() returns 0. I used this code:
rl.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
int rlHeight=rl.getMeasuredHeight();
And on Nexus 5 (API 23) everything's ok, but on the other device with API 17 (as I remember, 4.2 or something) it throws NPE. What to do?
P.S. OnGlobalLayoutListener and OnPreDrawListener are not ok, because I need the height to be measured before calling setParams(...) and adding the RL to outer layout.
You can use a GlobalLayoutListener or an OnPreDrawListener to programmatically find a views height at runtime. Refer to this answer for an example: https://stackoverflow.com/a/6569243/4898635
Make sure to remove the listener after you have your height however, otherwise it will continuously be called.

Android how is View position controlled?

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?

How To Scale A Layout While Keep Its Children Original Scale

Can I Scale A Layout(e.g LinearLayout) from 0.5 to 1.0 while Keeping the size And position of the TextView inside the layout to 1.0 During the whole transformation (the overflow parts should hidden)?
I see That The Scale Animation in IOS Keep the scale of the Children And How Can I Achieve this in Android ?
Plus,On Starting Animation, The Parent Layout of the animated block has already make space for it,But in IOS ,the parent makes room gradually during the animation, does it have an option for me to do the same thing in Android ?
This can be done pretty easily with the Property Animation API.
havent tried it on LinearLayout but if the view children are ordered correctly with their "layout_width" & "layout_height" you can use the ValueAnimator class to scale the parent view via LayoutParams.width & height.
Only setback is API 11 and above.

Unable to make child view wider than parent in Android Layout

I'm moving a view after an animation completes(to slide out a menu from the left). However, I don't seem to be able to achieve the effect I'm looking for. What I'd like is for the view to extend to the right past the parent's bounds, like this:
but what's happening is actually this:
The view resets itself to stay within the bounds of the parent. Even if I set an absolute pixel value (by looking at the display's width, or even a randomly large value).
The reason I need to do this is detailed in this SO question about a view's actual position after an animation has completed:
TranslateAnimated ImageView not clickable after animation [Android]
any thoughts? Thanks!
So, here is a related question. It explains how to move a view out of the screen.
I had same problem and I found the solution. I hope it can be helpful.
You need to set fixed width if you want to achieve this effect. To find real width of the view you need to measure it.
So full solution is:
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
int height = view.getMeasuredHeight();
int width = view.getMeasuredWidth();
view.setLayoutParams(new LinearLayout.LayoutParams(width, height));
(Use LayoutParams of the type corresponding the type of the parent layout)

How to scale View / ViewGroup - animate layout in a proper way?

In a few words, I want to scale view - in the same way that Android Market does it, when you click the "More" button, on the for examplae 'Description'.
I figure it out, that the Android Market has the layout of following structure :
<FrameLayout
android:id="#+id/artists_frame"
android:layout_width="fill_parent"
android:layout_height="64dip"
android:layout_below="#id/something1"
android:layout_above="#id/something2"
>
<!-- Some view there, which height >> 64dip -->
</FrameLayout>
So, I've tried various Animations / LayoutAnimations on the FrameLayout (via view.setAnimation(), view.setLayoutAnimation() and ScaleAnimation), but the effect is always the same : the view animates, but it's real layout_height after scaling is still the same (so the position of the other views, that depend on the given FrameLayout, remain the same).
After that, I've thought - I change in the loop the layout_height of the given FrameLayout:
layoutParams = view.getLayoutParams()
layoutParams.height = scaleTo;
layout.setLayoutParams(layoutParams);
I've it got animating, market-like view, but the cost (performance!!!) is way to high...
So, the question is : Is there any other, proper way to scale (change e.g. height from 50dip to 200dip) the given View / ViewGroup so that the position of the views below the animating view also changes?
As Chet Haase says in this blog post: http://android-developers.blogspot.com/2011/02/animation-in-honeycomb.html
"...Finally, the previous animations changed the visual appearance of the target objects... but they didn't actually change the objects themselves..."
Since what your need is only visual, instead of changing the view's LayoutParameters I would animate the view in the bottom as well with a translate using android:fillAfter for both.

Categories

Resources