ImageViews added to FrameLayout have zero dimensions - android

I am developing a custom ViewGroup that contains a number of views. In the constructor, I create a number of FrameLayouts that will each contain two ImageViews. Each Framelayout and the child views are created using the following code.
FrameLayout frame = new FrameLayout(context);
ImageView digitView = new ImageView(context);
digitView.setScaleType(ScaleType.CENTER_CROP);
frame.addView(digitView,
new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
ImageView bezelView = new ImageView(context);
bezelView.setScaleType(ScaleType.CENTER_CROP);
bezelView.setImageResource(R.drawable.bezel);
frame.addView(bezelView);
this.addView(frame, params);
In the onMeasure callback I set the dimensions of the custom ViewGroup
this.setMeasuredDimension(254, 43);
and in the onLayout callback, I calculate the dimensions of each of the FrameLayouts and call the layout method for each
#Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom)
{
if (changed)
{
float width = (float)(right - left) / (float)this.digits;
if ((int)(width/ASPECT_RATIO) > bottom- top)
width = (float)(bottom - top) * ASPECT_RATIO;
this.digitWidth = (int)width;
this.digitHeight = (int)(width/ASPECT_RATIO);
this.xOffset = ((right - left) - (this.digits * this.digitWidth))/2;
this.yOffset = ((bottom - top) - this.digitHeight)/2;
for (int i = this.digits-1; i >=0; i--)
{
int x = xOffset + i * this.digitWidth;
this.placeFrames.get(i).layout(x, this.yOffset, x + this.digitWidth, yOffset + this.digitHeight);
}
}
}
The issue I am having is that the FrameLayouts are being correctly sized and positioned within the custom GroupView, but the ImageViews are not being resized by the FrameLayouts - they have zero width and height. I validated this using the HierarchyViewer.
I think I've missed something - I assumed that the layout method on the FrameLayout would take care of resizing the child ImageViews based on the layout parameters passed in when they were added to the FrameLayout as children, but it seems not.
A pointer in the right direction would be very much appreciated.
Thanks
Andrew

I assumed that the layout method on the FrameLayout would take care of resizing the child ImageViews based on the layout parameters passed in when they were added to the FrameLayout as children, but it seems not.
You are not layout() these ImageView objects yourself nor calling super.onLayout()..

Related

android , moving view in linearlayout

I made a linear layout and it contain some Image views . when i move one with (set X(view.get x + 10)) function, it moves... but it moves behind other views. that the view become hidden.
and the other problem is when i get X & Y of the view, its always 0,0. but the view is in the middle of the screen. what should I do??? should i give up with linear layout??
if(wichmov == "right" ){
if(martin.getX() < width){
martin.setX(martin.getX()+ deltax);
}
else if(wichmov == "left"){
if(martin.getX() > 0){
martin.setX(martin.getX()- deltax );
}
}
}
this is how i move it.
When are you trying to call getX()? This might be something to look into: View getX() and getY() return 0.0 after they have been added to the Activity
If you're making the call in onCreate, it'll be zero.
I just figured it out.
I use a relative layout and then set the linear layout as match parent. After designing the background with the linear layout, I define an image view after the linear layout and inside of the relative layout, and then in Java, I set the position of it in the exact place I want it to (front of the special box of linear layout that I wanted it move), and width and height of it too.
The following code will help you to place your view to the exact place of your screen you want it, and set its width and height:
DisplayMetrics metrics = this.getResources().getDisplayMetrics(); //getting screen size
width = metrics.widthPixels;
height = metrics.heightPixels;
view.setX(width *5/8); //setting the place
view.setY(height/4);
LayoutParams para = view.getLayoutParams(); //set size of view
para.height = (int) (height/2);
para.width = (int) (width/4);
view.setLayoutParams(para);

Layout width and height being ignored

I have a dialog with a layout inside and a SurfaceTexture with a video stream. When I receive the width and height from the video, I resize my layout like this:
private void resizeView(final VideoFormatInfo info) {
final Size size = calculateSize(info.getWidth(), info.getHeight());
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
final ViewGroup.LayoutParams layoutParams = mInnerLayout.getLayoutParams();
layoutParams.width = size.x;
layoutParams.height = size.y;
Log.i(TAG, String.format("run: setting innerlayout bounds to %d,%d", size.x, size.y));
mInnerLayout.setLayoutParams(layoutParams);
}
});
}
Now I have a fullscreen button that is supposed to resize the layout to the whole screen. But when I press it, the layout remains in a small area of the screen.
When I check the log the proper value on size.x and size.y is there (the bounds of the screen), yet the layout is not properly resized.
The innerlayout is added to a customView named "VideoPlayer". I set the color of the videoplayer background to red so when I switch to fullscreen the whole screen turns red, except for the video stream in the middle. This means that the underlying view is being properly resized but the innerLayout is not for some reason.
Funny thing is, I have another layout over the video render that creates a "flash effect" to simulate a camera flash when taking a snapshot. When that flash effect is triggered, then the video is resized to the whole screen.
So this is my layout tree:
VideoPlayerView (CustomView, not VideoView)
innerLayout (RelativeLayout)
videoSurfaceTexture (SurfaceTextureView)
flashLayout (RelativeLayout)
I also set this for debugging:
#Override
public void onSurfaceTextureSizeChanged(final SurfaceTexture surfaceTexture, final int width, final int height) {
Log.d(TAG, "onSurfaceTextureSizeChanged size=" + width + "x" + height + ", st=" + surfaceTexture);
Log.i(TAG, String.format("innerlayout bounds are %d,%d", mInnerLayout.getLayoutParams().width, mInnerLayout.getLayoutParams().height));
}
And the values on the inner layout are the proper values (those of the whole screen) when I press fullscreen, but the layout is not resized. I can tell it's the layout not being resized because I changed its background color to green and added some padding and I can see it in the center of screen taking a small space.
It looks as though somehow the view is not being updated with the layout changes.
I am running out of ideas here. I tried invalidate(), postInvalidate() and forceLayout() but those dont work.
You missed one important part of forceLayout():
This method does not call requestLayout() or forceLayout() on the parent.
So make the parent do a layout as well:
mInnerLayout.setLayoutParams(layoutParams);
mInnerLayout.forceLayout();
mInnerLayout.getParent().requestLayout();
final ViewGroup.LayoutParams layoutParams = mInnerLayout.getLayoutParams();
layoutParams.width = size.x;
layoutParams.height = size.y;
Log.i(TAG, String.format("run: setting innerlayout bounds to %d,%d", size.x, size.y));
ViewGroup parent = ((ViewGroup)mInnerLayout.getParent());
parent.removeView(mInnerLayout);
mInnerLayout.setLayoutParams(layoutParams);
parent.addView(mInnerLayout);//you might need to get the index so you slot it in there.
This will do. -(all thoughts)
EDIT
i didnt want to add explanation because it was all thoughts and i needed verifying if it will work
But the explanation for my code is LayoutParams are what the Parent uses to layout its children hence it is useful only in the laying out pulse or time.
Changing the layoutParams object makes the View object dirty, other factors need to be met before a dirty View is layed out, so that is why the values change but the View is not changed.
you could have also just called View.invalidate() and View.requestLayout() on that particular View or Parent and it will also solve your problem, calling View.invalidate() alone will not do instantly for you. eg
layoutParams.width = size.x;
layoutParams.height = size.y;
Log.i(TAG, String.format("run: setting innerlayout bounds to %d,%d", size.x, size.y));
//re-setting the layout params is also not neccessary
mInnerLayout.invalidate();
mInnerLayout.requestLayout();
The reason the first approach solves your problem is because the View is remove and added which calls for a Laying out to be processed
:) also you should have just accepted it and let the bounty period elapsed :)
use Inflator like
View view = inflater.inflate( R.layout.item /* resource id */,
MyView.this /* parent */,
false /*attachToRoot*/);
for more check Layout params of loaded view are ignored

When is the correct moment to call getWidth() / getHeight() on a View?

EDIT --> Please, this is NOT a question WHY getWidth() / getHeight() return zero.
I have a Fragment inside an Activity.
I am dynamically adding SubViews (red) to a LinearLayout (RowView, blue) in horizontal orientation that is a child of the Fragment (green). How many of those childviews (SubViews) are added to the LinearLayout is determined at runtime.
The SubViews must have a fixed width of 200dp. Therefore, when dynamically adding the SubViews to the Linearlayout, I want to check if there is enough space for another SubView, or if a new Row needs to be started. The width of the RowView should be variable, and is NOT necessarily equal to the screen size.
In order to check if there is enough space, I simply see if the combined width of all SubViews is smaller than the width of the Linearlayout - the width of one SubView. Inside my custom LinearLayout (RowView), this looks as follows:
public void addSubView(SubView v) {
int childcount = getChildCount();
int totalChildWidth = 0;
for(int i = 0; i < childcount; i++) {
totalChildWidth += getChildAt(i).getWidth();
}
if(totalChildWidth < getWidth() - 200) { // there is space left in this row
addView(v);
}
}
The problem simply is, that getWidth() of the LinearLayout, and getWidth() of already added SubViews return 0, and therefore, the check if there is enough space goes wrong.
The reason for that is that I am calling getWi´dtdh() when the Views (the UI) have not yet been rendered on the screen, so my question is when is the right time to call my addSubView() method? Currently, I am calling it when creating the Fragment, which is obviously wrong:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment, container, false);
RowView row = (RowView) v.findViewById(R.id.rowView);
ArrayList<SubView> subViews = DBManager.getSubViewList(); // connects to a database and returns all available subviews
for(int i = 0; i < subViews.size(); i++) {
row.addSubView(subViews.get(i));
}
return v;
}
So where to call my addSubView(...) method where it is ensured that getWidth() inside it will not return 0? And in general, when is the correct moment (which callback method, according to Activity lifecycle) for getWidth() or getHeight() of a View to be called, where it is ensured that they will not return 0?
What I have tried so far:
Call addSubView(...) of my RowView in Fragments onActivityCreated(...) --> doesn't work
Connect to the database inside the RowViwes onSizeChanged(...) method, and all addSubView(...) there --> doesn't work
Do it as described in the code above, but with a Handler with 500ms delay --> works, because UI is rendered, but is not a proper solution for me
What you try to do is not working as the layouting isn't finished at the moment you want to know the width of the element. There are two solutions. The first one is to determine the actual width of your layout, which is ease in your case, as it is the actual screen width:
int width = getActivity().getResources().getDisplayMetrics().widthPixels
Use this value to determine how many of your subviews you can add in one row by converting the 200dp into pixel:
int viewWidth = (int) (200 * (getActivity().getResources().getDisplayMetrics().densityDpi / 160f));
Then you can calculate the maximal number of views for a row:
int maxViewsToAdd = (int) width/viewWidth;
The other solution is a globalLayoutListener, you can find a description here. But this seems not to work in all cases.

HorizontalScrollView get Visible Children

I added a linear layout in horizontal scroll view and in layout add some text views. Is it possible to get visible children in this layout.
This code get all child but i want to get visible (currently displaying) children only:
final HorizontalScrollView scroll = (HorizontalScrollView)findViewById(R.id.horizontalScrollView1);
LinearLayout linearLayout = ((LinearLayout)scroll.findViewById(R.id.linearLayout1));
int chilrenNum = linearLayout.getChildCount();
Well , after a bit of searching on SO I found this answer to listen to scroll events. Implement Scroll Event Listener in Android.
The idea is to override onScrollChanged in your ScrollView and keep track of the visible part of your scrollview in your activity.
Doing that you can easily get the visible views by a code that looks like this:
int currentPosition = lastXPosition; // lastXPosition gets updated from scroll event
int layoutWidth = linearLayout.getWidth();
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int childWidth = layoutWidth/linearLayout.getChildCount();
int firstVisibleXPos = currentPosition - width/2; // currentPosition will be in the middle of the screen
int lastVisibleXPos = currentPosition + width/2;
int indexOfFirstVisible = firstVisibleXPos/childWidth;
int indexOfLastVisible = lastVisibleXPos/ childWidth;
All the above code assumes fixed child view sizes . if you are using variable child size you will need to get their width first and keep track of it then calculate the visiblity based on index and position in parent view.

When is the width of an Android View set?

I have a little issue on what sequence things are being called when adding stuff to a RelativeLayout. I have a class extending Activity (name it RelActivity) where I want to create a RelativeLayout and put several custom Views (name it cusView) into that RelativeLayout. The topMargin and leftMargin of a custom View is calculated by using the position of another custom View (i.e. the first custom View has to be positioned directly by setting a number to topMargin and leftMargin). Please note that the Rules of RelativeLayout is not sufficient in this case.
So, over to the problem. In my RelActivity I do this:
Create a RelativeLayout (name it relLayout)
Iterate a cursor with cusViews recieved from a database
For the first cusView -> Set position by topMargin and leftMargin using a LayoutParameter
For the other cusViews -> calculate their topMargin and leftMargin by using one of the other cusViews and a LayoutParameter
Set RelActivity's contentView to relLayout
What happens is that all cusViews but the first one are squeezed in the top left corner because both leftMargin and topMargin are always calculated to be zero. This happens because I use the width of the cusViews to calculate the topMargin and leftMargin, and the width of the cusView has not given a value yet.
Is the width first calculated in the cusView's overrided method onSizeChanged()? Is the onSizeChanged() method get called first when the layout is presented on the screen? If so, how do I work around this issue? Do I have to calculate the positionings after onSizeChanged() is done?
Edit: Here is a minimum working example:
Here is my onCreate in RelActivity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
relLayout = new RelativeLayout(this);
cusViews = new ArrayList<CusView>();
listParams = new ArrayList<RelativeLayout.LayoutParams>();
readDBandSetLayout();
setContentView(relLayout);
}
There is too much information in the readDBandSetLayout() method to present it all here. below are the most important details. If I create the LayoutParams in the following way it works fine, the cusViews are listed downwards and rightwards of eachother:
queryCursor = customApplication.customData.query( number); //Fetches cursor
for ( int i = 0; i < numberOfRows; i++ ){
if ( i == 0 ){
LayoutParams p = new LayoutParams(this.getResources().getDimensionPixelSize(R.dimen.small), this.getResources().getDimensionPixelSize(R.dimen.small));
p.topMargin = 50;
p.leftMargin = 50;
listParams.add(p);
}
else{
LayoutParams p = new LayoutParams(this.getResources().getDimensionPixelSize(R.dimen.large),this.getResources().getDimensionPixelSize(R.dimen.large));
p.addRule(RelativeLayout.BELOW, cusViews.get(i-1).getId());
p.addRule(RelativeLayout.RIGHT_OF, cusViews.get(i-1).getId());
listParams.add(p);
}
relLayout.addView(cusViews.get(i), listParams.get(i));
}
However, what I want to do in the else statement is something like:
else{
LayoutParams p = new LayoutParams(this.getResources().getDimensionPixelSize(R.dimen.large),this.getResources().getDimensionPixelSize(R.dimen.large));
//Here I want to calculate cusView2Topmargin and cusView2Leftmargin based on the widths of the first or previosly positioned cusViews. But here the widths are 0 since they haven't been calculated yet.
p.topMargin = cusView2Topmargin; //Always zero
p.leftMargin = cusView2Leftmargin; //Always zero
listParams.add(p);
}
So the problem lies in that the widths of the cusViews are zero at the point I need them to calculate the layout parameters topMargin and leftMargin.
Unfortunately I cannot use the RelativeLayout's Rules for what I want to achieve. If there were some way to create rules like RelativeLayout.RIGHT_OF and RelativeLayout.BELOW I could do it like that. Is this possible?
Its not very clear what your goal is for this layout. It might well be possible to use a simple LinearLayout to get what you want.
If you want to size these from a database lookup then try simply adding each of the views, using addView() first, storing a reference to each, then go back and sett the margins to place them in the proper positions.

Categories

Resources