Positioning items relative to the ActionBar in Android - android

I used to have an Activity that would use no ActionBar. The layout is a RelativeLayout with width and height simply matching the parent. It allowed users to drag around buttons using parts of this code in the onTouch event:
MarginLayoutParams marginParams = new MarginLayoutParams(v.getLayoutParams());
int left = (int) event.getRawX() - (v.getWidth() / 2);
int top = (int) event.getRawY() - (v.getHeight());
marginParams.setMargins(left, top, 0, 0);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(marginParams);
v.setLayoutParams(layoutParams);
All dandy—the button moves just below the finger and is placed correctly at the position on screen. Now, however, I enabled the ActionBar, and suddenly when clicked, the button moves ~30 pixels below the finger. To illustrate this behavior, here's what I used to have (on the left) and what happens right now (on the right):
I assume this is because the ActionBar now displaces everything by its own height. Of course, I do not want this to happen, or at least correct this, so:
How can I shift the placement in setMargins() according to the actual size of the ActionBar without using a hardcoded value?
How can I prevent the ActionBar from shifting everything in the first place? Put differently, how can I ensure my button is placed relative to the ActionBar's bottom line and directly where the finger points?
This is what I want:

(I used an approach is my code and it works. I am going to tell you the same here)
I think setMargin() is not such a clever thing to use here. Instead you can use setX() and setY() methods.
Whenever one switches over from a full screen activity to the one with Action Bar, one runs into such problem.
You will need to adjust for the Action Bar height. Instead of using getLayoutParams(), use getLocationInWindow() or getLocationOnScreen(). Since getRawX() and getRawY() work with screen positions of the view, these APIs should be used.

Related

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.

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?

move the screen content to the left outside the screen android

is there a way to move the whole screen content outside the screen bounds, with titlebar and all, on the left for example, i tried
content = ((LinearLayout) act.findViewById(android.R.id.content)
.getParent());
to get the screen content and add a margin to it
FrameLayout.LayoutParams pr = (android.widget.FrameLayout.LayoutParams) content
.getLayoutParams();
pr.rightMargin = xOffset;
content.setLayoutParams(pr);
but it doesnt work, the margin appears but the content is not moved just resized to fit the screen, anyone know how to do this the simple way?
you can set the margin in negative(-) values.
for example if your want the layout to move upper use this:
android:marginTop = "-50dip or more u can put"
Are you trying to flip between views? As in, moving that layout off the screen to show another one? If so you should look at using a widget called ViewFlipper which will do exactly as you need. It can also be animated etc.
http://developer.android.com/reference/android/widget/ViewFlipper.html

How can I drag and drop Views within Android RelativeLayout

I made my own drag and drop functionality.
I wanted to do this with a RelativeLayout, but this seems not to work very well.
At the beginning i can tell each View I want to drag on which position it should be by setting it below the View before.
But when I want to drag a View, all the other Views beneath it are moved too, because they want to keep the order in which I set them.
But what other layout can I use for this? I have to use this drag and drop functionality on different screen sizes, so I cant' give them fixed x and y values.
I faced the same trouble a while ago, if you do not want to drag other views (either below or above or beside your view) along with the view being dragged, then use ViewGroup.LayoutParams instead of RelativeLayout.LayoutParams, the latter is imported if you hit Ctrl-Shift-O, so change it manually to the former.
Edit: Since you wanted the description of how I achieved that, here is the actual code
MarginLayoutParams marginParams = new MarginLayoutParams(image.getLayoutParams());
int left = (int) event.getRawX() - (v.getWidth() / 2);
int top = (int) event.getRawY() - (v.getHeight());
marginParams.setMargins(left, top, 0, 0);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(marginParams);
image.setLayoutParams(layoutParams);
I had to do a lot of research to achieve the same and it took me 2 days to figure out that a thing called MarginLayoutParams exists...
Make sure that you have
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
as your imports and not RelativeLayout.LayoutParams.
Also make sure that you cast marginparams to your relative layout
Best of luck...

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