Andorid custom view `onMeasure` not setting the desired width and height - android

I've created an InnerCustomView and although I'm overriding onMeasure, the view height is not set as the desired height.
Here is some context.
I have a CustomView. This view has a container (RelativeLayout) and inside here I have 4 InnerCustomViews plus two guidelines that I need to place in the container. For simplicity, I'm only showing here one of those InnerCustomViews because I can't manage to set this view to my width and height requirements.
This is the CustomView layout relevant information:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/grey">
<ImageView
android:id="#+id/image"
....
/>
<RelativeLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/red"
android:layout_below="#+id/image">
<View
android:id="#+id/guide"
android:layout_width="12dp"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="#android:color/holo_green_dark" />
.......
<...InnerCustomView
android:id="..."
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toEndOf="#id/guide"
android:background="#color/green"
... />
</RelativeLayout>
</RelativeLayout>
In CustomView I'm doing this:
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//Based on the container size I calculate the InnerCustomView desiredSize
innerCustomView.measure(MeasureSpec.makeMeasureSpec(desiredSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(desiredSize, MeasureSpec.EXACTLY));
}
The InnerCustomView only has an ImageView and a TextView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:background="#drawable/grey">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
I have this in InnerCustomView:
public class InnerCustomView extends LinearLayout {
//Default constructors and other stuff.
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
//Here I'm loging the values in widthMeasureSpec and heightMeasureSpec and I can see that they are the desiredSize
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
}
If you look at the image bellow, except height, my layout is as expected. The vertical guide (dark green) is in the correct place and the InnerCustomView is at it's right. The problem is that the width and height are not the ones I've passed when I called measure.
Width takes the available space and height also thus taking the whole height of the view (light green)
On the other hand, the InnerCustomView content has the desiredSize (grey).
Any ideia why is this happening?
I've tried to invalidate() the view, requestLayout(), forceLayout() but with no success.

Related

ImageView.getWidth() return same value in landscape and portrait screen

I want to make my ImageView's size is rectangle shaped based on its width and it's done with overiding onGlobalLayout() and put photo.getWidth(). But it is only work in portrait screen, and no in landscape screen.
Portrait is works fine :
In landscape, the image not rectangle shaped
And this is the code :
fragment_productdetail.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="horizontal">
<ImageView
android:id="#+id/ivImage0"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="3dp"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:src="#drawable/noimage"/>
<ImageView
android:id="#+id/ivImage1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="3dp"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:src="#drawable/noimage"/>
<ImageView
android:id="#+id/ivImage2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="3dp"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:src="#drawable/noimage"/>
<ImageView
android:id="#+id/ivImage3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="3dp"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:src="#drawable/noimage"/>
</LinearLayout>
FragmentProductDetail.java
ivPhotos[0] = (ImageView) inflatedView.findViewById(R.id.ivImage0);
ivPhotos[1] = (ImageView) inflatedView.findViewById(R.id.ivImage1);
ivPhotos[2] = (ImageView) inflatedView.findViewById(R.id.ivImage2);
ivPhotos[3] = (ImageView) inflatedView.findViewById(R.id.ivImage3);
for (final ImageView photo : ivPhotos) {
photo.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
photo.getViewTreeObserver().removeOnGlobalLayoutListener(this);
photo.getLayoutParams().height = photo.getWidth();
}
});
}
Then i try put Log.i("zihad", ""+photo.getWidth()); inside onGlobalLayout() to get the width in landscape, then it gives me same value of width in portrait. So what should i do to make it works in portrait and landscape?
*sorry for bad english, i ask here also to improve my english
Quick Tip:
I am not sure, if this is what you want. From what I assume, I think you might require a layout with Square ImageView. If you are trying to change the layout after rendering it, you should call photo.requestLayout(); before setting its height.
An alternative solution would be to use a SquareImageView. To achieve this, just create a View which extends ImageView and set the height by overriding its onMeasure like this.
public class MyImageView extends ImageView {
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
setMeasuredDimension(width, width);
}
Now in your layout, substitute ImageView for MyImageView(with complete package name).

Squaring Up A Relative Layout / Image View

So I've read quite a few questions on here as well as tried lots of stuff to try to get this to work. I'm either misunderstanding all of the examples I've read or am looking at the wrong stuff.
What I'm trying to do is set up a relative layout within a relative layout that contains an image view. I want to be able to have the relative layout match the size of the imageview (which I want scaled based on specific screen size) to keep a constant square size. This view will contain buttons as well.When I run the below code I get a square for the image view but the buttons are displayed on the top of the screen. So it appears to me that the imageview is properly scaling down into a square but the layout itself is still matching the overall screen size. I have an XML file set up as follows:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<RelativeLayout
android:id="#+id/layout_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
>
<ImageView
android:id="#+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="#drawable/bg" />
<Button
android:id="#+id/button_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/button_one" />
....
</RelativeLayout>
</RelativeLayout>
Java code below:
public class MainActivity extends ActionBarActivity {
RelativeLayout layoutTwo, mainLayout;
Button buttonOne, buttonTwo, buttonThree, buttonFour, buttonFive;
Button buttonSix, buttonSeven, buttonEight, buttonNine;
ImageView scalingBackground;
int screenWidth, screenHeight, finalSquare;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainLayout = (RelativeLayout) findViewById(R.id.main_layout);
layoutTwo = (RelativeLayout) findViewById(R.id.layout_two);
scalingBackground = (ImageView) findViewById(R.id.image_view);
initializeButtons();
squareBackground();
}
private void squareBackground() {
screenWidth = mainLayout.getLayoutParams().width;
scalingBackground.getLayoutParams().height = screenWidth;
scalingBackground.getLayoutParams().width = screenWidth;
layoutTwo.getLayoutParams().height = screenWidth;
layoutTwo.getLayoutParams().width = screenWidth;
}
Not quite sure whats going on here, I'd appreciate a kick in the right direction. Thanks
You can use a custom imageview in which height and width are same.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<RelativeLayout
android:id="#+id/layout_two"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
>
<com.packagename.SquareImageView
android:id="#+id/image_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="#drawable/bg" />
<Button
android:id="#+id/button_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/button_one" />
....
</RelativeLayout>
</RelativeLayout>
Create a class for custom imageview.
public class SquareImageView extends ImageView {
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
setMeasuredDimension(width, width);
}
}

Receiving unfair results in setting same size for width and height in android

Normally my gui look like below
I tried to implement same size for width and height in Get button. But the result is unfair (see the image below)
code snippet i tried
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_spinner);
spinner = (Spinner)findViewById(R.id.spinner1);
spinner.setAdapter(new MyAdapter(this, R.layout.row, strings));
Button btn = (Button)findViewById(R.id.getbtn);
int x = btn.getLayoutParams().height;
btn.getLayoutParams().width = x;
}
xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Testing"
/>
<Spinner
android:id="#+id/spinner1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:prompt="#string/prompt"/>
<Button
android:id="#+id/getbtn"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="Get"
/>
</LinearLayout>
</LinearLayout>
How to solve this issue ?
Thanks in advance.
You are giving fill_parent to the android:layout_height attribute to the Button XML. So, when you programmtically set that height value to width of Button then the Button taking so large width to fill the parent width.
Now, change the following attribute of Button
android:layout_height="fill_parent"
to
android:layout_height="wrap_content"
you can create custom button, overriding onMeasure() as below:
public class SquareHeightButton extends Button {
...
...
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
}

ImageView with no source does not honor layout, adding source does not repair layout

My base layout is a ListView of LinearLayouts, and each item in the grid is the FrameLayout below. I am downloading an image to the ImageView. Prior to the image being loaded, there is no content and the layout is shrunk to the height of the small ProgressBar (first problem), when I expect it to be 240dp or 120dp. After the image is placed in the view, the layout does not adjust (second problem) and the height remains the shrunken dimension of a small ProgressBar.
loading image code:
#Override
public View getView(View convertView) {
// ...
// set up holder
// ...
new GetPhotoTask().execute(holder.my_tile_image, holder.my_loading, my_media_url);
holder.my_tile_image.setVisibility(View.INVISIBLE);
holder.my_loading.setVisibility(View.VISIBLE);
holder.my_tile_label.setText(activityObject1.track.name);
// ...
}
private final class GetPhotoTask extends AsyncTask<Object, Void, Drawable> {
ImageView iv;
ProgressBar pb;
#Override
protected Drawable doInBackground(Object... params) {
iv = (ImageView) params[0];
pb = (ProgressBar) params[1];
return new BitmapDrawable(getResources(), loadAsset(params[2]).media_url, true));
}
#Override
protected void onPostExecute(Drawable result) {
super.onPostExecute(result);
pb.setVisibility(View.GONE);
iv.setImageDrawable(result);
iv.setVisibility(View.VISIBLE);
}
}
the main xml:
<ListView
android:id="#+id/my_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="2dp" />
the xml for each row:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_row_type_3"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="240dp"
android:weightSum="3" >
<include layout="#layout/my_tile_layout"
android:id="#+id/my_tile_1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<LinearLayout
android:id="#+id/my_row_type_2"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:weightSum="2" >
<include layout="#layout/my_tile_layout"
android:id="#+id/my_tile_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<include layout="#layout/my_tile_layout"
android:id="#+id/my_tile_3"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
the xml for row content:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_tile_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="2dp" >
<ImageView
android:id="#+id/my_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:contentDescription="#string/foo" />
<ProgressBar
android:id="#+id/my_loading"
style="?android:attr/progressBarStyleSmall"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<TextView
android:id="#+id/my_tile_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/my_title"
android:layout_gravity="left|bottom" />
</FrameLayout>
Why does the initial layout shrink to the height of progress bar even though I've hard coded 240dp for each row? Why does setting a bitmap in the ImageView content not cause it to resize at that point either? The first question most important, as I believe it will nullify the second. Thanks in advance.
SOLUTION
To get the structure I want, I use RelativeLayout for the tiles (for layering ability), and for each row I am overriding the LinearLayout to enforce the sizing I want set for each row. As there is a potential for each tile's content to be larger or smaller than the view, this is screwing up my ability to rely on either centerCrop or fitCenter. So in my custom layout, I set the height to be proportional to the width of each row, and the mode of the MeasureSpec to be EXACTLY.
public final class MyRow3 extends LinearLayout {
// ...
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, makeMeasureSpec(getSize(widthMeasureSpec) * 2 / 3, MeasureSpec.EXACTLY));
}
I'm not sure why it isn't honouring it but why not do something like this instead:
Set an absolute height in your ImageView to guarantee some space.
<ImageView
android:layout_height="###dp"
/>
Then change the layout back to an auto adjusted state just before the image is loaded:
iv.setLayoutParams( new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT ) );
To get the structure I want, I use RelativeLayout for the tiles (for layering ability), and for each row I am overriding the LinearLayout to enforce the sizing I want set for each row. As there is a potential for each tile's content to be larger or smaller than the view, this is screwing up my ability to rely on either centerCrop or fitCenter. So in my custom layout, I set the height to be proportional to the width of each row, and the mode of the MeasureSpec to be EXACTLY.
public final class MyRow3 extends LinearLayout {
// ...
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, makeMeasureSpec(getSize(widthMeasureSpec) * 2 / 3, MeasureSpec.EXACTLY));
}

Measure all view hierarchy before drawing in Android

I have next view hiererchy:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:gravity="center">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/group_bg"
android:gravity="center_vertical"
android:baselineAligned="false">
<LinearLayout
android:id="#+id/itemplace"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_weight="1" >
<include
android:layout_width="wrap_content"
android:layout_height="wrap_content"
layout="#layout/pin" />
</LinearLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/arrow_black"/>
</LinearLayout>
</LinearLayout>
I need to get itemplace width before this hierarchy will be drawing. I can not use OnLayoutChangeListener because I use API level 7.
I know that I must use measure() and layout() methods to calculate size of my views but I dont know hot it to do.
Root view in my hierarchy must fill all width of screen, so itemplace width depended of parent size, parent paddings and FrameLayout width.
What must I do to get it width?
I have find out way to get view width. I can get it after android measuret it but before drawing using OnPreDrawListener in ViewTreeObserver:
view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
int avaliableWidth = view.getWidth() - view.getPaddingLeft() - view.getPaddingRight();
Log.d("width", String.valueOf(avaliableWidth));
return true;
}
});
From Android documentation:
Callback method to be invoked when the view tree is about to be drawn.
At this point, all views in the tree have been measured and given a
frame. Clients can use this to adjust their scroll bounds or even to
request a new layout before drawing occurs.
This is available since API 1:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
}

Categories

Resources