The way I solved this problem is by creating a custom view for the child views, and then overriding onMeasure() for the custom view. The new onMeasure() sets the width and height to be as large as possible.
The problem is when you show the soft keyboard and rotate the phone. With the orientation change and the keyboard showing, onMeasure() sets the "largest" available height to be something ridiculously small, so when I hide the keyboard, the child views have the wrong size.
Is there a way to tell the views to recompute the layout when the keyboard goes away? Or am I doing onMeasure() wrong? Here's the code:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
setLayoutParams(new LinearLayout.LayoutParams(
measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec))
);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
Display display = ( (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int screenWidth = display.getWidth();
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the view
result = screenWidth;
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by measureSpec
result = Math.min(result, specSize);
}
}
Log.d(TAG, "Width: "+String.valueOf(result));
return result;
}
measureHeight() is done the same way.
Your super.onMeasure(widthMeasureSpec, heightMeasureSpec) uses the passed in values I would think you would want these to be the actual measured width and height:
super.onMeasure(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
Related
I am creating a custom view which extends LinearLayout. I'm adding some shapes to the linear layout, currently with a fixed value of space between them. I'm doing so by defining a LayoutParams and setting the margins to create the space between the shapes.
What I want to do is span them at an equal space across the entire screen so they would fill it, but only if the width of the view is set to either match_parent or fill_parent. If it's set to wrap_content then the original fixed value of space should be set.
I've tried doing:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
spaceBetweenShapesPixels = (parentWidth - shapeWidth * numberOfShapes) / numberOfShapess;
}
However, the method seems to be called twice - Once for the width and height of the parent and after that for the View's itself and when it's for the view itself, the space gets a 0 value.
So how can I just make this logic:
if(width is wrap_content)
{
space = 10;
}
else
{
space = (parentWidth - shapeWidth * numberOfShapes) / numberOfShapess;
}
You should use WidthMode and HeightMode
In the onMeasure() method. Below is sample a from one of my projects
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int measuredWidth = 0, measuredHeight = 0;
if (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST) {
measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
}
if (heightMode == MeasureSpec.EXACTLY) {
measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
} else if (heightMode == MeasureSpec.AT_MOST) {
double height = MeasureSpec.getSize(heightMeasureSpec) * 0.8;
measuredHeight = (int) height;// + paddingTop + paddingBottom;
}
}
MeasureSpec.EXACTLY - A view should be exactly this many pixels regardless of how big it actually wants to be.
MeasureSpec.AT_MOST - A view can be this size or smaller if it measures out to be smaller.
MeasureSpec.UNSPECIFIED - A view can be whatever size it needs to be in order to show the content it needs to show.
Refer this for more details https://stackoverflow.com/a/16022982/2809326
I have a custom View which has bigger height than its inner drawn content. I tried to set desired content height manually in code 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);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
centerX = chosenDimension / 2 - offsetCenterX;
centerY = chosenDimension / 2 - offsetCenterY;
setMeasuredDimension(chosenDimension, chosenDimension - 200); //IN HERE I`M TRYING TO CROP THE VIEWS HEIGHT
}
But it appears that my View scales down. I do not need this.
My solution to this was to reset ALL the dimensions in dimen.xml, but it kinda wrong. Any idea how to crop it in a right and easy way?
Here it is a picture what I want to cut off
Well, I would not have to cut off that space. Just set the correct values in onMesaure(). And to scale all dimens in dimens.xml
Suppose I put some points as (x1,y1) = (133,123), (x2,y2) = (149,136), (x3,y3) = (182,136) and so on which makes a shape something like this:
Now I want to change the position of these points as per the screen resolution such that the shape gets resized and gets centered and also such that the shape doesn't get damaged. Please help me.
You can grab the scale factor from the DisplayMetrics as shown in the Android - Supporting Multiple Screens documentation:
final float scale = getResources().getDisplayMetrics().density;
Multiply all you x and y coordinates with scale and your points are screen density independent.
To fit the image on the screen (or View probably), you can grab the width and height of your View. Check the width and height of your image and calculate the maximum scale factor.
Combine (multiply) both scale factors and your image should fit your View.
you can use onMeasure method to get measure then you can start painting with that position. I dont know below code is working truly, maybe it need to be optimized.
protected void onDraw(Canvas canvas) {
int height = getMeasuredHeight();
int width = getMeasuredWidth();
// Find the center
px = width / 2;
py = height / 2;
canvas.drawColor(BACKGROUND);
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.drawPath(mPath, mPaint);
// TODO remove if you dont want points to be drawn
for (Point point : mPoints) {
canvas.drawPoint(point.x + px, point.y + py, mPaint);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = measureHeight(heightMeasureSpec);
int measuredWidth = measureWidth(widthMeasureSpec);
setMeasuredDimension(measuredHeight, measuredWidth);
}
private int measureHeight(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
// Default size if no limits are specified.
int result = 500;
if (specMode == MeasureSpec.AT_MOST) {
// Calculate the ideal size of your
// control within this maximum size.
// If your control fills the available
// space return the outer bound.
result = specSize;
} else if (specMode == MeasureSpec.EXACTLY) {
// If your control can fit within these bounds return that value.
result = specSize;
}
return result;
}
private int measureWidth(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
// Default size if no limits are specified.
int result = 500;
if (specMode == MeasureSpec.AT_MOST) {
// Calculate the ideal size of your control
// within this maximum size.
// If your control fills the available space
// return the outer bound.
result = specSize;
} else if (specMode == MeasureSpec.EXACTLY) {
// If your control can fit within these bounds return that value.
result = specSize;
}
return result;
}
I am creating a triangle (Custom View) and then adding it to the parent view (RelativeLayout). i am facing problem here was view taking full screen of parent, how can i set the size of custom view upto that boundaries. triangle extends from view and it has touch listeners. dynamically i'm changing the size of the triangle.
i was added onMeasure() to that view but it takes some fixed amount of width and height..
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, "Width spec: " + MeasureSpec.toString(widthMeasureSpec));
Log.d(TAG, "Height spec: " + MeasureSpec.toString(heightMeasureSpec));
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
setMeasuredDimension(chosenDimension, chosenDimension);
}
i want set the width and height of custom view depending on the size of view when user change how much it has. look at the image.
It does not matter where the points are. You can draw the triangle of any orientation. But the custom view will always be a rectangular. If you do not draw anything outside the triangle the view will appear triangular.
I made a custom view. If I add the view to the layout XML file and I set the height to fill_parent "specSize" return 0. Why?
Code:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = 90;
int specMode = MeasureSpec.getMode(heightMeasureSpec);
int specSize = MeasureSpec.getSize(MeasureSpec.getMode(heightMeasureSpec));
if(specMode != MeasureSpec.UNSPECIFIED){
measuredHeight = specSize;
}
setMeasuredDimension(60, measuredHeight);
}
Does anyone know how I can get the height of fill_parent?
You don't need to call MeasureSpec.getMode inside of call to 'getSize' whole idea of measure spec is to combine way of measuring ( knowing as Spec) and associated Size. See documentation for MeasureSpec.makeMeasureSpec method.So correct code will look something like:
int widthSpec = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
The correct code will be:
int specMode = MeasureSpec.getMode(heightMeasureSpec); ----This was already correct
int specSize = MeasureSpec.getSize(heightMeasureSpec); ----This is corrected.