This is onMeasure() on CustomView which extend FrameLayout. After investigate onMeasure(), heigh size is always zero. How could I know what is the height size of this CustomView to manipulate child view later.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int viewHeightMode = MeasureSpec.getMode(heightMeasureSpec);
int viewWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int viewHeight = MeasureSpec.getSize(heightMeasureSpec);
int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
First of all please read this question. It is about measuring of View.
Main difference about ViewGroup that you should measure in your code all your childs.
for(int i=0; i<getChildCount(); i++) {
View child = getChildAt(i);
LayoutParams lp = child.getLayoutParams();
int widthMeasureMode = lp.width == LayoutParams.WRAP_CONTENT ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY,
heightMeasureMode = lp.height == LayoutParams.WRAP_CONTENT ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY;
int widthMeasure = MeasureSpec.makeMeasureSpec(getWidth() - left, widthMeasureMode),
heightMeasure = MeasureSpec.makeMeasureSpec(getHeight() - top, heightMeasureMode);
child.measure(widthMeasure, heightMeasure);
int childWidth = child.getMeasuredWidth(),
childHeight = child.getMeasuredHeight();
//make something with that
}
That shows how to get size of all childs. May be you want to calculate sum of heights, may be just find maximum value - it is your own goal.
By the way. If your base class is not ViewGroup, but FrameLayout for example, measuring childs could be done in onLayout method. In this case in your onMeasure method you can do nothing about measuring childs - just take sizes. But it is just a guess - better to check that.
Related
I created my custom drawable. And to draw it, I'm using the bounds. But, when the user use:
android:layout_width="match_parent"
android:layout_height="wrap_content"
I get bounds.height() == 1. Then, I want to use as height = 0.8 * bounds.width.
I tried it, because when the user use WRAP_CONTENT I get bounds.height() = 1. So then I set my drawable height to 0.8*width(). But I don't get the proper size. I just get like the height is 1. I read that you need to call invalidate(), but how do I call it, because if I call invalidate() in draw() then the method is called infinitely.
You should use onMeasure method.
Lifecycle call it when it's need to invalidate size of view you working on.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
ViewGroup.LayoutParams params = getLayoutParams();
//height and width expected from wrap_content and match_parent
int newHeight = MeasureSpec.getSize(heightMeasureSpec);
int newWidth = MeasureSpec.getSize(widthMeasureSpec);
int myHeight= newWidth * 0.8;
height = myHeight
width = newWidth
}
Consider using layout_constraintDimensionRatio of ConstraintLayout:
Official Guide
Or u can also fix the size issue in onMeasure().
I have written a very simple viewgroup extending LinearLayout as below.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ViewParent viewParent = getParent();
if(viewParent instanceof RelativeLayout)
{
RelativeLayout parentLayout = (RelativeLayout)viewParent;
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
Log.d(Constants.TAG, "Parent height is " + parentLayout.getMeasuredHeight());
Log.d(Constants.TAG, "Parent height is1 " + parentLayout.getHeight());
int height = parentLayout.getMeasuredHeight();
// getChildAt(0).measure(MeasureSpec.EXACTLY | AndroidUtilities.dp(70), MeasureSpec.EXACTLY | (int)(parentLayout.getMeasuredHeight()*0.6));
// getChildAt(1).measure(MeasureSpec.EXACTLY | AndroidUtilities.dp(70), MeasureSpec.EXACTLY | (int)(parentLayout.getMeasuredHeight()*0.4));
getChildAt(0).measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(70), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int)(height* 0.6), MeasureSpec.EXACTLY));
getChildAt(1).measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(70), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int)(height* 0.6), MeasureSpec.EXACTLY));
setMeasuredDimension(widthSize, parentLayout.getMeasuredHeight());
}
The parent of this viewgroup is a RelativeLayout containing a simple TextView.
On Android 4.2.2, parentLayout.getMeasuredHeight() returns 16777215. I am not able to make the sense of this number. It is working fine for higher Android versions.
Has anyone encountered this before?
I haven't seen this, but there's a note in the docs about a bug which fits your scenario:
http://developer.android.com/reference/android/view/View.MeasureSpec.html#makeMeasureSpec(int,%20int)
As for 16777215 (which equals 2^24 - 1), I'm not sure, but it seems like a pretty big coincidence that it's equal to MEASURED_SIZE_MASK. See
http://developer.android.com/reference/android/view/View.html#MEASURED_SIZE_MASK
I have a bitmap and below it is a time line.
As an example consider the right side layout of the FIGURE.
All the bottom timelines (1, 2, 3...) are in the same height from top.
The timeline is a textview which has fixed layout height and width as it is defined in xml
like timeline 1 is defined as:
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/HView"
android:layout_marginLeft="18dp"
android:layout_marginTop="345dp"
android:textSize="14sp"
android:text="1"
android:textColor="#000000" />
However the bitmap height and width can vary as it is done programatically.
So in certain cases, the bitmap height increases enough to overlap the timeline. In other words,
the vertical position of bitmap increases with respect to the vertical position of the timeline.
I want to get:
1) the ended vertical position of bitmap with respect to top of the screen.
2) the ended vertical position of timeline with respect to top of the screen.
I tried to do the following:
TextView bottomTimeLine = (TextView) view.findViewById(R.id.textView1);
bottomTimeLine.getHeight(); //returns 0.
bottomTimeLine.getBottom(); //returns 0.
ImageView img = new ImageView(getActivity());
img.setImageDrawable(getResources().getDrawable(R.drawable.disp_bg));
img.getHeight(); //returns 0.
img.getBottom(); //returns 0.
As seen from the code, both the methods, getHeight() and getBottom() are returning height as 0.
How to get the height (view end position) of both with respect to top of the cell display ?
Hope this helps
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(
parentWidth / 2, parentHeight);
}
This is how it can be done:
final TextView bottomTimeLine = (TextView) view.findViewById(R.id.textView1);
final int[] timelineCoord = new int[2];
final int[] imgCoord = new int[2];
ViewTreeObserver vto = bottomTimeLine.getViewTreeObserver();
vto.addOnGlobalLayoutListener((new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
bottomTimeLine.getLocationOnScreen(timelineCoord);
Log.d(" bottomTimeLine H ", ""+timelineCoord[1]);
timelineHeight = timelineCoord[1];
}
}));
ViewTreeObserver vt1 = img.getViewTreeObserver();
vt1.addOnGlobalLayoutListener((new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
img.getLocationOnScreen(imgCoord);
imgHeight = imgCoord[1] + img.getHeight();
Log.d("Img H ", ""+imgHeight);
if(imgHeight < timelineHeight)
{
int heightDiff = imgHeight - timelineHeight ;
heightDiff = heightDiff + 3;
img.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, heightDiff));
}
}
}));
My code for fixing ImageView:
private void fixImageWidth() {
int parentHeight = getHeight();
if (parentHeight == 0 || getParent() == null)
return;
Drawable drawable = image.getDrawable();
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) image.getLayoutParams();
if (drawable != null) {
int height = drawable.getIntrinsicHeight();
int width = drawable.getIntrinsicWidth();
lp.width = (int) ((float)(parentHeight - lp.topMargin * 2) / height * width);
} else {
lp.width = LayoutParams.WRAP_CONTENT;
}
image.requestLayout();
}
But sometimes actually image bounds is not changed. Below you could see HierarchyViewer properties of that object:
EDIT:
After I lot for debugging I determined, sometimes requestLayout don't remeasure image view. How does this happens?
EDIT:
I found solution, but still don't know reason. Solution is below:
image.post(new Runnable() {
#Override
public void run() {
image.requestLayout();
}
});
Any ideas?
Because getWidth() and getLayoutParams().width are different things. 1st relates to the View, the second is a layout request to the parent. If the parent cannot match the request the View maybe laid out with a different width. In this case you have requested MATCH_PARENT in the layout height and since an ImageView has a default scaleType of FIT_CENTER therefore content aspect ratio is maintained so the width will change.
I have an extended RelativeLayout which, when programmatically positioned and sized using RelativeLayout.LayoutParams, needs to restrict itself to a given aspect ratio. Typically, I would wish for it to constrain itself to 1:1, so that if the RelativeLayout.LayoutParams contain a width of 200 and a height of 100, the custom RelativeLayout would constrain itself to 100 x 100.
I am already used to overriding onMeasure() in ordinary custom Views in order to achieve similar aims. For example, I have created my own SVG image converter, and the custom View that renders the SVG image has an overridden onMeasure() that ensures that the call to setMeasuredDimension() contains dimensions that (a) fit within the original measurement specifications, and (b) match the aspect ratio of the original SVG image.
Going back to my custom RelativeLayout which I wish to constrain itself in a similar way, I've tried overriding onMeasure() but I haven't had much success. Knowing that RelativeLayout's onMeasure() performs all of the child View placement, what I'm generally trying to do at the moment, but without the desired results, is to override onMeasure() such that I initially modify the dimension specifications first (i.e. apply my desired constraints) and then call super.onMeasure(). Like this:
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec){
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// Restrict the aspect ratio to 1:1, fitting within original specified dimensions
int chosenDimension = Math.min(chosenWidth, chosenHeight);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
What actually happens when I do this is that, bizarrely, the height is properly restricted as I intended, but the width is not. To illustrate:
Specifying a height of 200 and width of 100 in the RelativeLayout.LayoutParams results in my custom RelativeLayout having a height of 100 and width of 100. -> Correct.
Specifying a height of 100 and width of 200 in the RelativeLayout.LayoutParams results in my custom RelativeLayout having a height of 100 and width of 200. -> Not correct.
I realise that I could instead apply my aspect ratio constraint logic within the calling class that's placing the RelativeLayout in the first place (and in the meantime I may well do that to get around this), but really this is an implementation detail that I want the RelativeLayout itself to perform.
Clarification: The resultant width and height values I'm reading back are from using getWidth() and getHeight(). These values are read back some time in the future, after the layout process has been performed again.
I have got around this now by also setting the width and height of the LayoutParams currently held by the RelativeLayout in the onMeasure().
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec){
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// Restrict the aspect ratio to 1:1, fitting within original specified dimensions
int chosenDimension = Math.min(widthSize, heightSize);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);
getLayoutParams().height = chosenDimension;
getLayoutParams().width = chosenDimension;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
This now works as desired: The size of the RelativeLayout (and subsequent calls to getWidth() and getHeight()) now agree with size restrictions applied in my overridden onMeasure().