RelativeLayout LayoutParam Above - android

I have two views, lets say A and B. I want A to be placed above B vertically, but center-aligned with B horizontaly, i.e. I want to achieve this:
I tried:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)
viewA.getLayoutParams();
layoutParams.addRule(
RelativeLayout.ABOVE,
viewB.getId()
);
But apparently what it did is placing A vertically above B, but horizontally floating to the left (aligned with X's left)
Please help.

What you have is on the right path, but you need to also add a rule for the horizontal centering: layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
I recognize that this doesn't quite do what you asked in that it is specifying that you center in the parent view horizontally as opposed to specifying that you align the horizontal centers of A and B, but there isn't a direct way to do the latter with a RelativeLayout rule. (There are many indirect ways to do it instead.)
Addition:
Given that your view A is not centered horizontally in the parent, here's a suggestion. I don't know of a great / non-hacky way to do this, but I think that since RelativeLayout rules are applied during the measurement pass, you can use the left (and right) coordinates of view A after measurement is done to set the (public) left and right properties of view B to what they need to be in order to center the view under A.
So for example, if your parent view is a custom extension of View you can override its onMeasure() method, call like:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); // So that mA.left and mA.right first get set...
mB.left = BWIDTH - (mA.left + mA.right) / 2;
mB.right = mB.left + BWIDTH;
/* You probably will need to re-measure (i.e., call super.onMeasure() again here)
* if you need to get views X and Y to the left of view B or if
* if view B exceeds the previously-measured size... */
}
And if not, you can use setOnLayoutChangeListener() on the parent and a callback to achieve the same thing.

Related

change placing of child views dynamically using onDraw?

Is there a way to dynamically change the position(like a manually translation animation) on a child view thats been defined and places inside onLayout?
i have this onLayout defined here:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//place topview at the top of the layout by removing the ySpace value defined fro the height
// of this view and dividing the height so that the topView takes nearly half the space
// height of this view container
topView.layout(0, 0, getWidth(), (getHeight() - ySpace) / 2);
}
i want to manually change the y axis position of topView Dynamically .
i was thinking ofabout onDraw but onDraw doesnt that childViews and draws them. Instead it just gives you a blank canvas to manually draw items in it.
Currently implementing a custom ViewGroup that takes x amount of child views and dynamically moves them around. My question is how do i move them around?

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 set the four parameters of View.layout(l, t, r, b)

I am facing the same problem in this question, but I have difficulty in understanding the exact meaning of the four position parameters in layout(int l, int t, int r, int b). I know they represent left, top, right and bottom respectively relative to parent, but "relative" to where exactly?
For example, if I translate the button down 100 pixel, in order to move the clickable area down 100 pixel should I set
// l t r b
button.layout(0, 100, 0, button.getHeight()+100)
? Does the integer 100 of second parameter mean 100 pixel down relative to the top of parent? Is the fourth parameter means button.getHeight()+100 relative to the top of parent also??? I tested on my device, the clickable area does not move down as I want. I'm very confused here, any help is greatly appreciated.
The parameters to layout are relative to the parent view's upper left corner. X increases as you move to the right and Y increases as you move down. It is not relative to the view's current position.
You probably don't want to call layout on a child view outside of its parent's onLayout method. There are several other bits of internal bookkeeping that happen as a result of calling layout that will change internal state in a way you don't want for simple animations.
Typically layout is called like this:
child.layout(x, y, x + child.getMeasuredWidth(), y + child.getMeasuredHeight());
The parameters are absolute, with (0, 0) at the parent's upper left corner.
But if you're simply moving a view around for animation you probably want to do something more like:
// Adjust horizontally; dx = change in x position
child.offsetLeftAndRight(dx);
// Adjust vertically; dy = change in y position
child.offsetTopAndBottom(dy);
The parameters to the offset* methods are relative to the view's current position.
If you write a ViewGroup class that moves its child views around in this way, remember that you can get a layout request at any time. At that point your ViewGroup's onLayout method will try to position the child based on its usual layout rules and it will need to adjust this positioning to reflect any other accumulated movement you want to preserve.

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 can I dynamically set the position of view in 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);

Categories

Resources