Changing view dimensions programmatically on android - android

I have two views which overlap each other and one of them acts as a master to other. I am translating all the pinching and panning to scale and adjust the master view.
Now I wanna send this info over to the slave(Subclass of android.view.View). Being said that the other view doesn't respond to this dimensions. I have tried following methods
ViewGroup.LayoutParams params = this.getLayoutParams();
params.height = height;
params.width = width;
this.setLayoutParams(params);this.requestLayout();
setMeasuredDimension(width, height);
this.setMinimumHeight(height);
this.setMinimumWidth(width)
None of these seem to work.
Using the onTouchEvent I resize my TextureView and send the same to the other View object. On getting this Info about change in size which is in most cases greater than the parent dimensions(and screen size) the other view should also increase/change its size.
The subclassed view doesn't grow to the dimensions supplied to it. Moreover the increase is limited to the parent size(in my case its width: 1080 height: 1776) while it can go above that in the textureView which uses
textureView.setMinimumWidth(newWidth);
textureView.setMinimumHeight(newHeight);
Any ideas as to what maybe going wrong or a trusted way to change layout/dimensions across two views?

setLayoutParams => that would be the canonical way to position a View inside its parent.
setMeasuredDimension => this is used from View.onMeasure to tell the layout system what size your particular View wants. Use only if you're implementing your own View subclass.
setMinimumWidth/setMinimumHeight => tells the default View implementation what minimum size the View should have. If you implement View.onMeasure yourself, that shouldn't be needed.
All in all, if you stick with the standard Android framework components, setLayoutParams should be the place to look into.

The methods you've listed are all valid ways of setting sizes if used correctly, and in the right situations.
This is difficult to solve without further information.
In what way isn't it working? Is it causing a crash, or are the view sizes just not what you expect?
Where does that snippet sit in your code? I would guess its part of a custom class of your own which extends ViewGroup, but as you're new to stackoverflow we have no idea of your level of programming experience. It might be in completely the wrong place, and this refers to some other object entirely.
One thing I can suggest you check :
You mention your slave is a subclass of android.view.View, but this on its own is not enough to be able to use ViewGroup.LayoutParams. You need to ensure your slave is a direct or indirect subclass of android.view.ViewGroup.
Provide more of your code and info on what you are observing, and we will try and help you further.
Update:
Ok, so it's a TextureView that you are trying to resize.
I'd recommend you check the reported size of your TextureView after you set it's height and width. You could do this with break-points and run a debug, or you could use myTextureView.getWidth() and .getHeight(), and use Log statments to output them to the LogCat.
You can also check the size of the SurfaceTexture associated with your TextureView. Set a SurfaceTextureListener on your TextureView. When this listener is triggered, onSurfaceTextureAvailable() will provide the width and height of the SurfaceTexture.
I suggest this because you may find that you are able to change the size of the TextureView, but it is the content of the the TextureView that is not changing size. In this case you can override TextureView.onSizeChanged(int w, int h, int oldw, int oldh) to define the necessary transform to scale your content between the old and new sizes.

Related

Best practice for getting View dimensions, possibly before they are drawn to screen

I've seen a ton of different questions and answers to the problems people are having with retrieving height and width of views, particularly this thread.
The following are mentioned methods to retrieve View dimensions:
onWindowFocusChanged() in your activity
Subclassing the type of view you're using and overridding either onSizeChanged(), onLayout(), or onMeasure()
Using a ViewTreeObserver and addOnGlobalLayoutListener()
Each of them seem to work in some cases, while in other cases people say something doesn't work correctly. Since there isn't a setOnDimensionsKnown(OnDimensionsKnown odk) or similar method for the View class, which of these methods (or possibly one not mentioned) will give me the dimensions my view will eventually have, regardless if it's drawn yet, has wrap_content or fill_parent parameters, or it has explicit height and width set in px, dp, or similar?
EDIT: Perhaps a specific example would be helpful, I'm trying to make a PopupWindow wrap it's contents, and to be offset in the -x direction by the value of it's width. The problem is that the contents width, and thus the popupwindows width, are not measured until after I show the popup. So basically I can't think of a good way to measure the width it will be before it is drawn to screen.
My usual approach is to override whatever view class I need the dimensions of before it being drawn, and create my own setOnDimensionsKnown(OnDimensionsKnown odk) method and fire it with the width and height values that onMeasure is called with. This works it every case I've needed, but it doesn't seem very elegant to override every view class to do this.
I can post code to help explain the example more.
Good way of doing it is adding an OnGlobalLayoutListener() to the view, as stated in this answer (point 3 in your question, in my opinion that's the most reliable for any View). The way I do it for a PopupWindow is to inflate it's layout and then call measure on it:
popupView = getActivity().getLayoutInflater().inflate(R.layout.popup_layout, null);
popup = new PopupWindow(popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true);
popupView.measure(view.getMeasuredWidth(), view.getMeasuredHeight()); //the view is parent's layout (not sure if those are correct values, but it measures okay in my case)
and then just get the numbers by calling popupView.getMeasuredHeight() and .getMeasuredWitdh().
Your problem is probably long gone by now, but hopefully someone else will stumble upon this and find it helpful. :)

Android Custom View sizing

I am attempting to experiment in creating my own Views by subclassing "View." I want to simulate a new Button type class that will be drawn with the onDraw where I simply draw a rectangle from the canvas being passed in. I then add that custom View into the layout XML file, but I cannot control its size. For example, I set the width to "fill_parent" and the height to "wrap_content" but I don't know what the content of my View is. (BTW, the LinearLayout that my view is in is oriented to "vertical")
Can anyone give me some pointers in how to control the size of my View? For example, how does my custom view know what parameters were set in the XML file?
Android cannot detect the contents of your Canvas, so it assumes the contents are 0x0.
To do what you're looking for, you have to override onLayout or onMeasure (onLayout is specified by Android developers as a way to lay out children instead). You will also need a way to calculate the size of your contents.
First, start with a member variable called Point mSize = new Point(0, 0);. In a method other than onDraw, but called just as often, you will want to replace this with the size of your contents. If you draw a circle that has a radius of 25, do: mSize.x = 50; mSize.y = 50;.
Next, I'll introduce you to this answer (for onMeasure). It shows how to set the size of an item based on its parent's dimensions (divided by 2, in this case). Then, you can pull the size from mSize.x and mSize.y and apply those dimensions.
Finally, calling invalidate() after you measure your new mSize should force the View to lay itself out again, and adjust to your new parameters. This is the one part I'm unsure of in this regard.
NB: Also, you could call setLayoutParams using a fixed width and height once you calculate the size of your Canvas contents. Not sure of the performance implications for this, though.

What is the difference between getWidth/Height() and getMeasuredWidth/Height() in Android SDK?

The Android Documentation says that there is two sizes for a view, the measured dimensions and the drawing dimensions. The measured dimension is the one computed in the measure pass (the onMeasure method), while the drawing dimensions are the actual size on screen. Particularly, the documentation says that:
These values may, but do not have to, be different from the measured width and height.
So, my question is: what could make the drawing dimension be different of the measured dimension? If the onMeasure(int,int) method respects the layout requirements (given as the parameters widthMeasureSpec and heightMeasureSpec, how could the SDK decides that the view should have a different drawing size?
Additionally, how/where in the Android Source Code the measured width/height is used to compute the drawing width/height? I tryed to look into the View source code, but I can't figure out how the measuredWidth/Height is used to compute the final width/height. Maybe it has something to do with the padding, but I'm not sure.
As the name suggests the measuredWidth/height is used during measuring and layoutting phase.
Let me give an example,
A widget is asked to measure itself, The widget says that it wants to be 200px by 200px. This is measuredWidth/height.
During the layout phase, i.e. in onLayout method. The method can use the measuredWidth/height of its children or assign a new width/height by calling layout method of the view.
lets say the onLayout method calls childview.layout(0,0,150,150) now the width/height of the view is different than the measured width/height.
I would suggest not to use the measuredWidth/height outside onLayout method.
to summarize .
onMeasure -> sets up measuredWidth/measuredHeight
onLayout -> sets up the width/height of the widget.
additionallly
public void View.layout(int l, int t, int r, int b)
seems to be place where the assignment of position and size happens.

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);

Programmatically resize of view depends on android version?

I am facing a weird behavior regarding the subject.
I have a very simple layout containing only an empty RelativeLayout.
Once I have input form the user this relative layout is filled with square tiles to achieve a mosaic-like effect. Each tile is made by a FrameLayout containing two images (only one is drawn at any given time). It is not possible for me to put the tiles in the XML layout because I do not know in advance how many of them I will need.
In the onSizeChanged of my relative layout, I force a resize on all the tiles to fit the new size.
The code is something like this:
public void resizeTiles(int w, int h) {
int l1 = w / X; int l2 = h / Y;
int tileS = (l1 <= l2 ? l1 : l2);
android.view.ViewGroup.LayoutParams lp;
for (Tile t : myTiles) {
lp = t.getLayoutParams();
lp.width = tileS;
lp.height = tileS;
}
}
In my manifest file I have the following:
<uses-sdk
android:minSdkVersion="4"
android:targetSdkVersion="4"
/>
Thus, target system is 1.6.
So far so good, this is working like a charm ... but only on 2.2.
The same binary placed on emulator 2.1-update1 or previous is giving me back an empty layout (I also tried a couple of physical devices, same result).
Debugging, I tracked down the problem is in the resize; commenting out width and height assignments I see the tiles but with distorted proportions.
Any suggestion on how to make this working ?
Thanks in advance.
onSizeChanged() is called late in the layout process, once the final size has been determined. There may already have been some layout passes that have happened down through the hierarchy at that point.
Basic answer is: layout params are for telling the view's parent its layout params prior to is performing a layout. If they change, the layout must be invalidated to perform a new complete layout with the new params. You should never change these in the middle of a layout.
The best thing to do is just write your own layout manager. (If you are doing layout of tiles, RelativeLayout does a ton more stuff that you don't need, making it a lot less efficient than necessary.) It is actually not very hard to write a simple layout manager.
You can use the simple layout managers in the framework as a guide:
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/AbsoluteLayout.java
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/FrameLayout.java
(Note that even these are a lot more complicated than you probably need, since they are intended to be general purpose for the layouts they are implementing. We really should have an API demo of a custom layout that is truly simple.)

Categories

Resources