Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android customView how to calculate onMeasure to fill parent

I'm trying to draw a custom view, everything is rendering fine, except the right most portion is cut off. I want the view to fill the parent and only use the needed amount of height (i.e the height of the bitmap I'm drawing. When i remove padding from the xml layout of the activity the custom view renders fine. So I'm not calculating onMeasure with the parents padding taken into account.

Please could someone take a look at this code and describe why and how I am going wrong? Thanks

EDITED TO include @nicko's answer and more detail:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    ViewParent parent = getParent();
    int leftPadding = 0;
    int rightPadding = 0;

    if (parent instanceof ViewGroup) {
        leftPadding = ((ViewGroup) parent).getPaddingLeft();
        rightPadding = ((ViewGroup) parent).getPaddingRight();
    }
    float wanabeHeight = calendar.getHeight();
    float wanabeWidth = parentWidth - leftPadding - rightPadding;

    int height = 0;
    //Determine Height
    switch(heightMode){
        case MeasureSpec.EXACTLY: height = parentHeight;
            break;
        case MeasureSpec.AT_MOST:
            height = Math.min((int)wanabeHeight, parentHeight);
            break;
        case MeasureSpec.UNSPECIFIED:
        default:
            height = (int)wanabeHeight;
            break;
    }
    setMeasuredDimension((int)wanabeWidth, height);
}


@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    calendarX = left;
    personX = (int)((right - left) * 0.5);
    checkX = right - check.getWidth() - getPaddingRight();
    lineY = (int)((bottom - top) * 0.5);
    bitmapY = (int)(((bottom - top) * 0.5) - (calendar.getHeight() * 0.5));
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(calendar, calendarX, bitmapY, paint);
    canvas.drawBitmap(person, personX, bitmapY, paint);
    canvas.drawBitmap(check, checkX, bitmapY, paint);

    canvas.drawLine((calendarX + calendar.getWidth() + PADDING), lineY, personX - PADDING, lineY, linePaint);
    canvas.drawLine((personX + person.getWidth() + PADDING), lineY, checkX - PADDING, lineY, linePaint);
}
like image 962
serenskye Avatar asked Oct 20 '22 13:10

serenskye


2 Answers

In the onDraw, everything is relative to the canvas - but it seems that you are drawing everything according to the view positions that come through in onLayout which are relative to the screen.

So, for your right hand image, you actually need to calculate the X position like this:

checkX = right - left - check.getWidth();

You'll also probably find that the left hand image is indented a bit if the parent has any padding so try positioning it at 0:

calendarX = 0;
like image 64
Martyn Avatar answered Oct 23 '22 10:10

Martyn


You can get parent padding following way:

ViewParent parent = getParent();
int topPadding = 0;
int bottomPadding = 0;

if (parent instanceof ViewGroup) {
    topPadding = ((ViewGroup) parent).getPaddingTop();
    bottomPadding = ((ViewGroup) parent).getPaddingBottom();
}

And use it:

parentHeight -= topPadding - bottomPadding;
int height = 0;

//Determine Height
switch(heightMode){
    case MeasureSpec.EXACTLY: 
        height = parentHeight;
        break;
    case MeasureSpec.AT_MOST:
        height = Math.min((int)wanabeHeight, parentHeight);
        break;
    case MeasureSpec.UNSPECIFIED:
    default:
        height = (int)wanabeHeight; 
        break;
}
like image 34
Niko Avatar answered Oct 23 '22 10:10

Niko